mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
DEV: Move more tests into modules (#11119)
Models, services, mixins, utilities, and most of the controllers
This commit is contained in:
@@ -128,18 +128,39 @@ export function controllerModule(name, args = {}) {
|
||||
});
|
||||
}
|
||||
|
||||
export function discourseModule(name, hooks) {
|
||||
export function discourseModule(name, options) {
|
||||
// deprecated(
|
||||
// `${name}: \`discourseModule\` is deprecated. Use QUnit's \`module\` instead.`,
|
||||
// { since: "2.6.0" }
|
||||
// );
|
||||
|
||||
if (typeof options === "function") {
|
||||
module(name, function (hooks) {
|
||||
hooks.beforeEach(function () {
|
||||
this.container = getOwner(this);
|
||||
this.registry = this.container.registry;
|
||||
|
||||
this.owner = this.container;
|
||||
this.siteSettings = currentSettings();
|
||||
});
|
||||
|
||||
options.call(this, hooks);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
module(name, {
|
||||
beforeEach() {
|
||||
this.container = getOwner(this);
|
||||
this.siteSettings = currentSettings();
|
||||
if (hooks && hooks.beforeEach) {
|
||||
hooks.beforeEach.call(this);
|
||||
if (options && options.beforeEach) {
|
||||
options.beforeEach.call(this);
|
||||
}
|
||||
},
|
||||
afterEach() {
|
||||
if (hooks && hooks.afterEach) {
|
||||
hooks.afterEach.call(this);
|
||||
if (options && options.afterEach) {
|
||||
options.afterEach.call(this);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
import { queryAll } from "discourse/tests/helpers/qunit-helpers";
|
||||
import { moduleForComponent } from "ember-qunit";
|
||||
import componentTest from "discourse/tests/helpers/component-test";
|
||||
|
||||
moduleForComponent("share-button", { integration: true });
|
||||
|
||||
componentTest("share button", {
|
||||
template: '{{share-button url="https://eviltrout.com"}}',
|
||||
|
||||
test(assert) {
|
||||
assert.ok(queryAll(`button.share`).length, "it has all the classes");
|
||||
|
||||
assert.ok(
|
||||
queryAll('button[data-share-url="https://eviltrout.com"]').length,
|
||||
"it has the data attribute for sharing"
|
||||
);
|
||||
},
|
||||
});
|
||||
@@ -1,13 +0,0 @@
|
||||
import Button from "discourse/components/d-button";
|
||||
|
||||
export default Button.extend({
|
||||
classNames: ["btn-default", "share"],
|
||||
icon: "link",
|
||||
title: "topic.share.help",
|
||||
label: "topic.share.title",
|
||||
attributeBindings: ["url:data-share-url"],
|
||||
|
||||
click() {
|
||||
return true;
|
||||
},
|
||||
});
|
||||
@@ -1,48 +1,49 @@
|
||||
import EmberObject from "@ember/object";
|
||||
import { mapRoutes } from "discourse/mapping-router";
|
||||
import { moduleFor } from "ember-qunit";
|
||||
import { discourseModule } from "discourse/tests/helpers/qunit-helpers";
|
||||
import { test } from "qunit";
|
||||
|
||||
moduleFor("controller:avatar-selector", "controller:avatar-selector", {
|
||||
beforeEach() {
|
||||
discourseModule("Unit | Controller | avatar-selector", function (hooks) {
|
||||
hooks.beforeEach(function () {
|
||||
this.registry.register("router:main", mapRoutes());
|
||||
},
|
||||
needs: ["controller:modal"],
|
||||
});
|
||||
|
||||
test("avatarTemplate", function (assert) {
|
||||
const avatarSelectorController = this.subject();
|
||||
|
||||
const user = EmberObject.create({
|
||||
avatar_template: "avatar",
|
||||
system_avatar_template: "system",
|
||||
gravatar_avatar_template: "gravatar",
|
||||
|
||||
system_avatar_upload_id: 1,
|
||||
gravatar_avatar_upload_id: 2,
|
||||
custom_avatar_upload_id: 3,
|
||||
});
|
||||
|
||||
avatarSelectorController.setProperties({ user });
|
||||
test("avatarTemplate", function (assert) {
|
||||
const avatarSelectorController = this.owner.lookup(
|
||||
"controller:avatar-selector"
|
||||
);
|
||||
|
||||
user.set("avatar_template", "system");
|
||||
assert.equal(
|
||||
avatarSelectorController.get("selectedUploadId"),
|
||||
1,
|
||||
"we are using system by default"
|
||||
);
|
||||
const user = EmberObject.create({
|
||||
avatar_template: "avatar",
|
||||
system_avatar_template: "system",
|
||||
gravatar_avatar_template: "gravatar",
|
||||
|
||||
user.set("avatar_template", "gravatar");
|
||||
assert.equal(
|
||||
avatarSelectorController.get("selectedUploadId"),
|
||||
2,
|
||||
"we are using gravatar when set"
|
||||
);
|
||||
system_avatar_upload_id: 1,
|
||||
gravatar_avatar_upload_id: 2,
|
||||
custom_avatar_upload_id: 3,
|
||||
});
|
||||
|
||||
user.set("avatar_template", "avatar");
|
||||
assert.equal(
|
||||
avatarSelectorController.get("selectedUploadId"),
|
||||
3,
|
||||
"we are using custom when set"
|
||||
);
|
||||
avatarSelectorController.setProperties({ user });
|
||||
|
||||
user.set("avatar_template", "system");
|
||||
assert.equal(
|
||||
avatarSelectorController.get("selectedUploadId"),
|
||||
1,
|
||||
"we are using system by default"
|
||||
);
|
||||
|
||||
user.set("avatar_template", "gravatar");
|
||||
assert.equal(
|
||||
avatarSelectorController.get("selectedUploadId"),
|
||||
2,
|
||||
"we are using gravatar when set"
|
||||
);
|
||||
|
||||
user.set("avatar_template", "avatar");
|
||||
assert.equal(
|
||||
avatarSelectorController.get("selectedUploadId"),
|
||||
3,
|
||||
"we are using custom when set"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import sinon from "sinon";
|
||||
import { moduleFor } from "ember-qunit";
|
||||
import { discourseModule } from "discourse/tests/helpers/qunit-helpers";
|
||||
import { test } from "qunit";
|
||||
import { logIn } from "discourse/tests/helpers/qunit-helpers";
|
||||
import User from "discourse/models/user";
|
||||
@@ -9,247 +9,252 @@ import { fakeTime } from "discourse/tests/helpers/qunit-helpers";
|
||||
|
||||
let BookmarkController;
|
||||
|
||||
moduleFor("controller:bookmark", {
|
||||
beforeEach() {
|
||||
logIn();
|
||||
KeyboardShortcutInitializer.initialize(this.container);
|
||||
BookmarkController = this.subject({
|
||||
currentUser: User.current(),
|
||||
site: { isMobileDevice: false },
|
||||
});
|
||||
BookmarkController.onShow();
|
||||
},
|
||||
|
||||
afterEach() {
|
||||
sinon.restore();
|
||||
},
|
||||
});
|
||||
|
||||
function mockMomentTz(dateString) {
|
||||
fakeTime(dateString, BookmarkController.userTimezone);
|
||||
}
|
||||
|
||||
test("showLaterToday when later today is tomorrow do not show", function (assert) {
|
||||
mockMomentTz("2019-12-11T22:00:00");
|
||||
discourseModule("Unit | Controller | bookmark", function (hooks) {
|
||||
hooks.beforeEach(function () {
|
||||
logIn();
|
||||
KeyboardShortcutInitializer.initialize(this.container);
|
||||
|
||||
assert.equal(BookmarkController.get("showLaterToday"), false);
|
||||
});
|
||||
BookmarkController = this.owner.lookup("controller:bookmark");
|
||||
BookmarkController.setProperties({
|
||||
currentUser: User.current(),
|
||||
site: { isMobileDevice: false },
|
||||
});
|
||||
BookmarkController.onShow();
|
||||
});
|
||||
|
||||
test("showLaterToday when later today is after 5pm but before 6pm", function (assert) {
|
||||
mockMomentTz("2019-12-11T15:00:00");
|
||||
assert.equal(BookmarkController.get("showLaterToday"), true);
|
||||
});
|
||||
hooks.afterEach(function () {
|
||||
sinon.restore();
|
||||
});
|
||||
|
||||
test("showLaterToday when now is after the cutoff time (5pm)", function (assert) {
|
||||
mockMomentTz("2019-12-11T17:00:00");
|
||||
assert.equal(BookmarkController.get("showLaterToday"), false);
|
||||
});
|
||||
test("showLaterToday when later today is tomorrow do not show", function (assert) {
|
||||
mockMomentTz("2019-12-11T22:00:00");
|
||||
|
||||
test("showLaterToday when later today is before the end of the day, show", function (assert) {
|
||||
mockMomentTz("2019-12-11T10:00:00");
|
||||
assert.equal(BookmarkController.get("showLaterToday"), false);
|
||||
});
|
||||
|
||||
assert.equal(BookmarkController.get("showLaterToday"), true);
|
||||
});
|
||||
test("showLaterToday when later today is after 5pm but before 6pm", function (assert) {
|
||||
mockMomentTz("2019-12-11T15:00:00");
|
||||
assert.equal(BookmarkController.get("showLaterToday"), true);
|
||||
});
|
||||
|
||||
test("nextWeek gets next week correctly", function (assert) {
|
||||
mockMomentTz("2019-12-11T08:00:00");
|
||||
test("showLaterToday when now is after the cutoff time (5pm)", function (assert) {
|
||||
mockMomentTz("2019-12-11T17:00:00");
|
||||
assert.equal(BookmarkController.get("showLaterToday"), false);
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
BookmarkController.nextWeek().format("YYYY-MM-DD"),
|
||||
"2019-12-18"
|
||||
);
|
||||
});
|
||||
test("showLaterToday when later today is before the end of the day, show", function (assert) {
|
||||
mockMomentTz("2019-12-11T10:00:00");
|
||||
|
||||
test("nextMonth gets next month correctly", function (assert) {
|
||||
mockMomentTz("2019-12-11T08:00:00");
|
||||
assert.equal(BookmarkController.get("showLaterToday"), true);
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
BookmarkController.nextMonth().format("YYYY-MM-DD"),
|
||||
"2020-01-11"
|
||||
);
|
||||
});
|
||||
test("nextWeek gets next week correctly", function (assert) {
|
||||
mockMomentTz("2019-12-11T08:00:00");
|
||||
|
||||
test("laterThisWeek gets 2 days from now", function (assert) {
|
||||
mockMomentTz("2019-12-10T08:00:00");
|
||||
assert.equal(
|
||||
BookmarkController.nextWeek().format("YYYY-MM-DD"),
|
||||
"2019-12-18"
|
||||
);
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
BookmarkController.laterThisWeek().format("YYYY-MM-DD"),
|
||||
"2019-12-12"
|
||||
);
|
||||
});
|
||||
test("nextMonth gets next month correctly", function (assert) {
|
||||
mockMomentTz("2019-12-11T08:00:00");
|
||||
|
||||
test("laterThisWeek returns null if we are at Thursday already", function (assert) {
|
||||
mockMomentTz("2019-12-12T08:00:00");
|
||||
assert.equal(
|
||||
BookmarkController.nextMonth().format("YYYY-MM-DD"),
|
||||
"2020-01-11"
|
||||
);
|
||||
});
|
||||
|
||||
assert.equal(BookmarkController.laterThisWeek(), null);
|
||||
});
|
||||
test("laterThisWeek gets 2 days from now", function (assert) {
|
||||
mockMomentTz("2019-12-10T08:00:00");
|
||||
|
||||
test("showLaterThisWeek returns true if < Thursday", function (assert) {
|
||||
mockMomentTz("2019-12-10T08:00:00");
|
||||
assert.equal(
|
||||
BookmarkController.laterThisWeek().format("YYYY-MM-DD"),
|
||||
"2019-12-12"
|
||||
);
|
||||
});
|
||||
|
||||
assert.equal(BookmarkController.showLaterThisWeek, true);
|
||||
});
|
||||
test("laterThisWeek returns null if we are at Thursday already", function (assert) {
|
||||
mockMomentTz("2019-12-12T08:00:00");
|
||||
|
||||
test("showLaterThisWeek returns false if > Thursday", function (assert) {
|
||||
mockMomentTz("2019-12-12T08:00:00");
|
||||
assert.equal(BookmarkController.laterThisWeek(), null);
|
||||
});
|
||||
|
||||
assert.equal(BookmarkController.showLaterThisWeek, false);
|
||||
});
|
||||
test("tomorrow gets tomorrow correctly", function (assert) {
|
||||
mockMomentTz("2019-12-11T08:00:00");
|
||||
test("showLaterThisWeek returns true if < Thursday", function (assert) {
|
||||
mockMomentTz("2019-12-10T08:00:00");
|
||||
|
||||
assert.equal(
|
||||
BookmarkController.tomorrow().format("YYYY-MM-DD"),
|
||||
"2019-12-12"
|
||||
);
|
||||
});
|
||||
assert.equal(BookmarkController.showLaterThisWeek, true);
|
||||
});
|
||||
|
||||
test("startOfDay changes the time of the provided date to 8:00am correctly", function (assert) {
|
||||
let dt = moment.tz(
|
||||
"2019-12-11T11:37:16",
|
||||
BookmarkController.currentUser.resolvedTimezone(
|
||||
BookmarkController.currentUser
|
||||
)
|
||||
);
|
||||
test("showLaterThisWeek returns false if > Thursday", function (assert) {
|
||||
mockMomentTz("2019-12-12T08:00:00");
|
||||
|
||||
assert.equal(
|
||||
BookmarkController.startOfDay(dt).format("YYYY-MM-DD HH:mm:ss"),
|
||||
"2019-12-11 08:00:00"
|
||||
);
|
||||
});
|
||||
assert.equal(BookmarkController.showLaterThisWeek, false);
|
||||
});
|
||||
test("tomorrow gets tomorrow correctly", function (assert) {
|
||||
mockMomentTz("2019-12-11T08:00:00");
|
||||
|
||||
test("laterToday gets 3 hours from now and if before half-past, it rounds down", function (assert) {
|
||||
mockMomentTz("2019-12-11T08:13:00");
|
||||
assert.equal(
|
||||
BookmarkController.tomorrow().format("YYYY-MM-DD"),
|
||||
"2019-12-12"
|
||||
);
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
BookmarkController.laterToday().format("YYYY-MM-DD HH:mm:ss"),
|
||||
"2019-12-11 11:00:00"
|
||||
);
|
||||
});
|
||||
|
||||
test("laterToday gets 3 hours from now and if after half-past, it rounds up to the next hour", function (assert) {
|
||||
mockMomentTz("2019-12-11T08:43:00");
|
||||
|
||||
assert.equal(
|
||||
BookmarkController.laterToday().format("YYYY-MM-DD HH:mm:ss"),
|
||||
"2019-12-11 12:00:00"
|
||||
);
|
||||
});
|
||||
|
||||
test("laterToday is capped to 6pm. later today at 3pm = 6pm, 3:30pm = 6pm, 4pm = 6pm, 4:59pm = 6pm", function (assert) {
|
||||
mockMomentTz("2019-12-11T15:00:00");
|
||||
|
||||
assert.equal(
|
||||
BookmarkController.laterToday().format("YYYY-MM-DD HH:mm:ss"),
|
||||
"2019-12-11 18:00:00",
|
||||
"3pm should max to 6pm"
|
||||
);
|
||||
|
||||
mockMomentTz("2019-12-11T15:31:00");
|
||||
|
||||
assert.equal(
|
||||
BookmarkController.laterToday().format("YYYY-MM-DD HH:mm:ss"),
|
||||
"2019-12-11 18:00:00",
|
||||
"3:30pm should max to 6pm"
|
||||
);
|
||||
|
||||
mockMomentTz("2019-12-11T16:00:00");
|
||||
|
||||
assert.equal(
|
||||
BookmarkController.laterToday().format("YYYY-MM-DD HH:mm:ss"),
|
||||
"2019-12-11 18:00:00",
|
||||
"4pm should max to 6pm"
|
||||
);
|
||||
|
||||
mockMomentTz("2019-12-11T16:59:00");
|
||||
|
||||
assert.equal(
|
||||
BookmarkController.laterToday().format("YYYY-MM-DD HH:mm:ss"),
|
||||
"2019-12-11 18:00:00",
|
||||
"4:59pm should max to 6pm"
|
||||
);
|
||||
});
|
||||
|
||||
test("showLaterToday returns false if >= 5PM", function (assert) {
|
||||
mockMomentTz("2019-12-11T17:00:01");
|
||||
assert.equal(BookmarkController.showLaterToday, false);
|
||||
});
|
||||
|
||||
test("showLaterToday returns false if >= 5PM", function (assert) {
|
||||
mockMomentTz("2019-12-11T17:00:01");
|
||||
assert.equal(BookmarkController.showLaterToday, false);
|
||||
});
|
||||
|
||||
test("reminderAt - custom - defaults to 8:00am if the time is not selected", function (assert) {
|
||||
BookmarkController.customReminderDate = "2028-12-12";
|
||||
BookmarkController.selectedReminderType =
|
||||
BookmarkController.reminderTypes.CUSTOM;
|
||||
const reminderAt = BookmarkController._reminderAt();
|
||||
assert.equal(BookmarkController.customReminderTime, "08:00");
|
||||
assert.equal(
|
||||
reminderAt.toString(),
|
||||
moment
|
||||
.tz(
|
||||
"2028-12-12 08:00",
|
||||
BookmarkController.currentUser.resolvedTimezone(
|
||||
BookmarkController.currentUser
|
||||
)
|
||||
test("startOfDay changes the time of the provided date to 8:00am correctly", function (assert) {
|
||||
let dt = moment.tz(
|
||||
"2019-12-11T11:37:16",
|
||||
BookmarkController.currentUser.resolvedTimezone(
|
||||
BookmarkController.currentUser
|
||||
)
|
||||
.toString(),
|
||||
"the custom date and time are parsed correctly with default time"
|
||||
);
|
||||
});
|
||||
|
||||
test("loadLastUsedCustomReminderDatetime fills the custom reminder date + time if present in localStorage", function (assert) {
|
||||
mockMomentTz("2019-12-11T08:00:00");
|
||||
localStorage.lastCustomBookmarkReminderDate = "2019-12-12";
|
||||
localStorage.lastCustomBookmarkReminderTime = "08:00";
|
||||
|
||||
BookmarkController._loadLastUsedCustomReminderDatetime();
|
||||
|
||||
assert.equal(BookmarkController.lastCustomReminderDate, "2019-12-12");
|
||||
assert.equal(BookmarkController.lastCustomReminderTime, "08:00");
|
||||
});
|
||||
|
||||
test("loadLastUsedCustomReminderDatetime does not fills the custom reminder date + time if the datetime in localStorage is < now", function (assert) {
|
||||
mockMomentTz("2019-12-11T08:00:00");
|
||||
localStorage.lastCustomBookmarkReminderDate = "2019-12-11";
|
||||
localStorage.lastCustomBookmarkReminderTime = "07:00";
|
||||
|
||||
BookmarkController._loadLastUsedCustomReminderDatetime();
|
||||
|
||||
assert.equal(BookmarkController.lastCustomReminderDate, null);
|
||||
assert.equal(BookmarkController.lastCustomReminderTime, null);
|
||||
});
|
||||
|
||||
test("user timezone updates when the modal is shown", function (assert) {
|
||||
User.current().changeTimezone(null);
|
||||
let stub = sinon.stub(moment.tz, "guess").returns("Europe/Moscow");
|
||||
BookmarkController.onShow();
|
||||
assert.equal(BookmarkController.userHasTimezoneSet, true);
|
||||
assert.equal(
|
||||
BookmarkController.userTimezone,
|
||||
"Europe/Moscow",
|
||||
"the user does not have their timezone set and a timezone is guessed"
|
||||
);
|
||||
User.current().changeTimezone("Australia/Brisbane");
|
||||
BookmarkController.onShow();
|
||||
assert.equal(BookmarkController.userHasTimezoneSet, true);
|
||||
assert.equal(
|
||||
BookmarkController.userTimezone,
|
||||
"Australia/Brisbane",
|
||||
"the user does their timezone set"
|
||||
);
|
||||
stub.restore();
|
||||
});
|
||||
|
||||
test("opening the modal with an existing bookmark with reminder at prefills the custom reminder type", function (assert) {
|
||||
let name = "test";
|
||||
let reminderAt = "2020-05-15T09:45:00";
|
||||
BookmarkController.model = { id: 1, name: name, reminderAt: reminderAt };
|
||||
BookmarkController.onShow();
|
||||
assert.equal(BookmarkController.selectedReminderType, REMINDER_TYPES.CUSTOM);
|
||||
assert.equal(BookmarkController.customReminderDate, "2020-05-15");
|
||||
assert.equal(BookmarkController.customReminderTime, "09:45");
|
||||
assert.equal(BookmarkController.model.name, name);
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
BookmarkController.startOfDay(dt).format("YYYY-MM-DD HH:mm:ss"),
|
||||
"2019-12-11 08:00:00"
|
||||
);
|
||||
});
|
||||
|
||||
test("laterToday gets 3 hours from now and if before half-past, it rounds down", function (assert) {
|
||||
mockMomentTz("2019-12-11T08:13:00");
|
||||
|
||||
assert.equal(
|
||||
BookmarkController.laterToday().format("YYYY-MM-DD HH:mm:ss"),
|
||||
"2019-12-11 11:00:00"
|
||||
);
|
||||
});
|
||||
|
||||
test("laterToday gets 3 hours from now and if after half-past, it rounds up to the next hour", function (assert) {
|
||||
mockMomentTz("2019-12-11T08:43:00");
|
||||
|
||||
assert.equal(
|
||||
BookmarkController.laterToday().format("YYYY-MM-DD HH:mm:ss"),
|
||||
"2019-12-11 12:00:00"
|
||||
);
|
||||
});
|
||||
|
||||
test("laterToday is capped to 6pm. later today at 3pm = 6pm, 3:30pm = 6pm, 4pm = 6pm, 4:59pm = 6pm", function (assert) {
|
||||
mockMomentTz("2019-12-11T15:00:00");
|
||||
|
||||
assert.equal(
|
||||
BookmarkController.laterToday().format("YYYY-MM-DD HH:mm:ss"),
|
||||
"2019-12-11 18:00:00",
|
||||
"3pm should max to 6pm"
|
||||
);
|
||||
|
||||
mockMomentTz("2019-12-11T15:31:00");
|
||||
|
||||
assert.equal(
|
||||
BookmarkController.laterToday().format("YYYY-MM-DD HH:mm:ss"),
|
||||
"2019-12-11 18:00:00",
|
||||
"3:30pm should max to 6pm"
|
||||
);
|
||||
|
||||
mockMomentTz("2019-12-11T16:00:00");
|
||||
|
||||
assert.equal(
|
||||
BookmarkController.laterToday().format("YYYY-MM-DD HH:mm:ss"),
|
||||
"2019-12-11 18:00:00",
|
||||
"4pm should max to 6pm"
|
||||
);
|
||||
|
||||
mockMomentTz("2019-12-11T16:59:00");
|
||||
|
||||
assert.equal(
|
||||
BookmarkController.laterToday().format("YYYY-MM-DD HH:mm:ss"),
|
||||
"2019-12-11 18:00:00",
|
||||
"4:59pm should max to 6pm"
|
||||
);
|
||||
});
|
||||
|
||||
test("showLaterToday returns false if >= 5PM", function (assert) {
|
||||
mockMomentTz("2019-12-11T17:00:01");
|
||||
assert.equal(BookmarkController.showLaterToday, false);
|
||||
});
|
||||
|
||||
test("showLaterToday returns false if >= 5PM", function (assert) {
|
||||
mockMomentTz("2019-12-11T17:00:01");
|
||||
assert.equal(BookmarkController.showLaterToday, false);
|
||||
});
|
||||
|
||||
test("reminderAt - custom - defaults to 8:00am if the time is not selected", function (assert) {
|
||||
BookmarkController.customReminderDate = "2028-12-12";
|
||||
BookmarkController.selectedReminderType =
|
||||
BookmarkController.reminderTypes.CUSTOM;
|
||||
const reminderAt = BookmarkController._reminderAt();
|
||||
assert.equal(BookmarkController.customReminderTime, "08:00");
|
||||
assert.equal(
|
||||
reminderAt.toString(),
|
||||
moment
|
||||
.tz(
|
||||
"2028-12-12 08:00",
|
||||
BookmarkController.currentUser.resolvedTimezone(
|
||||
BookmarkController.currentUser
|
||||
)
|
||||
)
|
||||
.toString(),
|
||||
"the custom date and time are parsed correctly with default time"
|
||||
);
|
||||
});
|
||||
|
||||
test("loadLastUsedCustomReminderDatetime fills the custom reminder date + time if present in localStorage", function (assert) {
|
||||
mockMomentTz("2019-12-11T08:00:00");
|
||||
localStorage.lastCustomBookmarkReminderDate = "2019-12-12";
|
||||
localStorage.lastCustomBookmarkReminderTime = "08:00";
|
||||
|
||||
BookmarkController._loadLastUsedCustomReminderDatetime();
|
||||
|
||||
assert.equal(BookmarkController.lastCustomReminderDate, "2019-12-12");
|
||||
assert.equal(BookmarkController.lastCustomReminderTime, "08:00");
|
||||
});
|
||||
|
||||
test("loadLastUsedCustomReminderDatetime does not fills the custom reminder date + time if the datetime in localStorage is < now", function (assert) {
|
||||
mockMomentTz("2019-12-11T08:00:00");
|
||||
localStorage.lastCustomBookmarkReminderDate = "2019-12-11";
|
||||
localStorage.lastCustomBookmarkReminderTime = "07:00";
|
||||
|
||||
BookmarkController._loadLastUsedCustomReminderDatetime();
|
||||
|
||||
assert.equal(BookmarkController.lastCustomReminderDate, null);
|
||||
assert.equal(BookmarkController.lastCustomReminderTime, null);
|
||||
});
|
||||
|
||||
test("user timezone updates when the modal is shown", function (assert) {
|
||||
User.current().changeTimezone(null);
|
||||
let stub = sinon.stub(moment.tz, "guess").returns("Europe/Moscow");
|
||||
BookmarkController.onShow();
|
||||
assert.equal(BookmarkController.userHasTimezoneSet, true);
|
||||
assert.equal(
|
||||
BookmarkController.userTimezone,
|
||||
"Europe/Moscow",
|
||||
"the user does not have their timezone set and a timezone is guessed"
|
||||
);
|
||||
User.current().changeTimezone("Australia/Brisbane");
|
||||
BookmarkController.onShow();
|
||||
assert.equal(BookmarkController.userHasTimezoneSet, true);
|
||||
assert.equal(
|
||||
BookmarkController.userTimezone,
|
||||
"Australia/Brisbane",
|
||||
"the user does their timezone set"
|
||||
);
|
||||
stub.restore();
|
||||
});
|
||||
|
||||
test("opening the modal with an existing bookmark with reminder at prefills the custom reminder type", function (assert) {
|
||||
let name = "test";
|
||||
let reminderAt = "2020-05-15T09:45:00";
|
||||
BookmarkController.model = { id: 1, name: name, reminderAt: reminderAt };
|
||||
BookmarkController.onShow();
|
||||
assert.equal(
|
||||
BookmarkController.selectedReminderType,
|
||||
REMINDER_TYPES.CUSTOM
|
||||
);
|
||||
assert.equal(BookmarkController.customReminderDate, "2020-05-15");
|
||||
assert.equal(BookmarkController.customReminderTime, "09:45");
|
||||
assert.equal(BookmarkController.model.name, name);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,104 +1,107 @@
|
||||
import { test } from "qunit";
|
||||
import I18n from "I18n";
|
||||
import { controllerModule } from "discourse/tests/helpers/qunit-helpers";
|
||||
import { discourseModule } from "discourse/tests/helpers/qunit-helpers";
|
||||
|
||||
controllerModule("controller:create-account", {
|
||||
needs: ["controller:modal", "controller:login"],
|
||||
});
|
||||
discourseModule("Unit | Controller | create-account", function () {
|
||||
test("basicUsernameValidation", async function (assert) {
|
||||
const testInvalidUsername = async function (username, expectedReason) {
|
||||
const controller = await this.owner.lookup("controller:create-account");
|
||||
controller.set("accountUsername", username);
|
||||
|
||||
test("basicUsernameValidation", async function (assert) {
|
||||
const subject = this.subject;
|
||||
assert.equal(
|
||||
controller.get("basicUsernameValidation.failed"),
|
||||
true,
|
||||
"username should be invalid: " + username
|
||||
);
|
||||
assert.equal(
|
||||
controller.get("basicUsernameValidation.reason"),
|
||||
expectedReason,
|
||||
"username validation reason: " + username + ", " + expectedReason
|
||||
);
|
||||
}.bind(this);
|
||||
|
||||
const testInvalidUsername = async (username, expectedReason) => {
|
||||
const controller = await subject();
|
||||
controller.set("accountUsername", username);
|
||||
testInvalidUsername("", undefined);
|
||||
testInvalidUsername("x", I18n.t("user.username.too_short"));
|
||||
testInvalidUsername(
|
||||
"123456789012345678901",
|
||||
I18n.t("user.username.too_long")
|
||||
);
|
||||
|
||||
const controller = await this.owner.lookup("controller:create-account");
|
||||
controller.setProperties({
|
||||
accountUsername: "porkchops",
|
||||
prefilledUsername: "porkchops",
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
controller.get("basicUsernameValidation.failed"),
|
||||
controller.get("basicUsernameValidation.ok"),
|
||||
true,
|
||||
"username should be invalid: " + username
|
||||
"Prefilled username is valid"
|
||||
);
|
||||
assert.equal(
|
||||
controller.get("basicUsernameValidation.reason"),
|
||||
expectedReason,
|
||||
"username validation reason: " + username + ", " + expectedReason
|
||||
I18n.t("user.username.prefilled"),
|
||||
"Prefilled username is valid"
|
||||
);
|
||||
};
|
||||
|
||||
testInvalidUsername("", undefined);
|
||||
testInvalidUsername("x", I18n.t("user.username.too_short"));
|
||||
testInvalidUsername(
|
||||
"123456789012345678901",
|
||||
I18n.t("user.username.too_long")
|
||||
);
|
||||
|
||||
const controller = await subject();
|
||||
controller.setProperties({
|
||||
accountUsername: "porkchops",
|
||||
prefilledUsername: "porkchops",
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
controller.get("basicUsernameValidation.ok"),
|
||||
true,
|
||||
"Prefilled username is valid"
|
||||
);
|
||||
assert.equal(
|
||||
controller.get("basicUsernameValidation.reason"),
|
||||
I18n.t("user.username.prefilled"),
|
||||
"Prefilled username is valid"
|
||||
);
|
||||
});
|
||||
test("passwordValidation", async function (assert) {
|
||||
const controller = await this.owner.lookup("controller:create-account");
|
||||
|
||||
test("passwordValidation", async function (assert) {
|
||||
const controller = await this.subject();
|
||||
|
||||
controller.set("authProvider", "");
|
||||
controller.set("accountEmail", "pork@chops.com");
|
||||
controller.set("accountUsername", "porkchops");
|
||||
controller.set("prefilledUsername", "porkchops");
|
||||
controller.set("accountPassword", "b4fcdae11f9167");
|
||||
|
||||
assert.equal(controller.get("passwordValidation.ok"), true, "Password is ok");
|
||||
assert.equal(
|
||||
controller.get("passwordValidation.reason"),
|
||||
I18n.t("user.password.ok"),
|
||||
"Password is valid"
|
||||
);
|
||||
|
||||
const testInvalidPassword = (password, expectedReason) => {
|
||||
controller.set("accountPassword", password);
|
||||
controller.set("authProvider", "");
|
||||
controller.set("accountEmail", "pork@chops.com");
|
||||
controller.set("accountUsername", "porkchops");
|
||||
controller.set("prefilledUsername", "porkchops");
|
||||
controller.set("accountPassword", "b4fcdae11f9167");
|
||||
|
||||
assert.equal(
|
||||
controller.get("passwordValidation.failed"),
|
||||
controller.get("passwordValidation.ok"),
|
||||
true,
|
||||
"password should be invalid: " + password
|
||||
"Password is ok"
|
||||
);
|
||||
assert.equal(
|
||||
controller.get("passwordValidation.reason"),
|
||||
expectedReason,
|
||||
"password validation reason: " + password + ", " + expectedReason
|
||||
I18n.t("user.password.ok"),
|
||||
"Password is valid"
|
||||
);
|
||||
};
|
||||
|
||||
testInvalidPassword("", undefined);
|
||||
testInvalidPassword("x", I18n.t("user.password.too_short"));
|
||||
testInvalidPassword("porkchops", I18n.t("user.password.same_as_username"));
|
||||
testInvalidPassword("pork@chops.com", I18n.t("user.password.same_as_email"));
|
||||
});
|
||||
|
||||
test("authProviderDisplayName", async function (assert) {
|
||||
const controller = this.subject();
|
||||
|
||||
assert.equal(
|
||||
controller.authProviderDisplayName("facebook"),
|
||||
I18n.t("login.facebook.name"),
|
||||
"provider name is translated correctly"
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
controller.authProviderDisplayName("idontexist"),
|
||||
"idontexist",
|
||||
"provider name falls back if not found"
|
||||
);
|
||||
const testInvalidPassword = (password, expectedReason) => {
|
||||
controller.set("accountPassword", password);
|
||||
|
||||
assert.equal(
|
||||
controller.get("passwordValidation.failed"),
|
||||
true,
|
||||
"password should be invalid: " + password
|
||||
);
|
||||
assert.equal(
|
||||
controller.get("passwordValidation.reason"),
|
||||
expectedReason,
|
||||
"password validation reason: " + password + ", " + expectedReason
|
||||
);
|
||||
};
|
||||
|
||||
testInvalidPassword("", undefined);
|
||||
testInvalidPassword("x", I18n.t("user.password.too_short"));
|
||||
testInvalidPassword("porkchops", I18n.t("user.password.same_as_username"));
|
||||
testInvalidPassword(
|
||||
"pork@chops.com",
|
||||
I18n.t("user.password.same_as_email")
|
||||
);
|
||||
});
|
||||
|
||||
test("authProviderDisplayName", async function (assert) {
|
||||
const controller = this.owner.lookup("controller:create-account");
|
||||
|
||||
assert.equal(
|
||||
controller.authProviderDisplayName("facebook"),
|
||||
I18n.t("login.facebook.name"),
|
||||
"provider name is translated correctly"
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
controller.authProviderDisplayName("idontexist"),
|
||||
"idontexist",
|
||||
"provider name falls back if not found"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,117 +1,118 @@
|
||||
import { moduleFor } from "ember-qunit";
|
||||
import { discourseModule } from "discourse/tests/helpers/qunit-helpers";
|
||||
import { test } from "qunit";
|
||||
moduleFor("controller:history");
|
||||
|
||||
test("displayEdit", async function (assert) {
|
||||
const HistoryController = this.subject();
|
||||
discourseModule("Unit | Controller | history", function () {
|
||||
test("displayEdit", async function (assert) {
|
||||
const HistoryController = this.owner.lookup("controller:history");
|
||||
|
||||
HistoryController.setProperties({
|
||||
model: { last_revision: 3, current_revision: 3, can_edit: false },
|
||||
topicController: {},
|
||||
});
|
||||
HistoryController.setProperties({
|
||||
model: { last_revision: 3, current_revision: 3, can_edit: false },
|
||||
topicController: {},
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
HistoryController.get("displayEdit"),
|
||||
false,
|
||||
"it should not display edit button when user cannot edit the post"
|
||||
);
|
||||
assert.equal(
|
||||
HistoryController.get("displayEdit"),
|
||||
false,
|
||||
"it should not display edit button when user cannot edit the post"
|
||||
);
|
||||
|
||||
HistoryController.set("model.can_edit", true);
|
||||
HistoryController.set("model.can_edit", true);
|
||||
|
||||
assert.equal(
|
||||
HistoryController.get("displayEdit"),
|
||||
true,
|
||||
"it should display edit button when user can edit the post"
|
||||
);
|
||||
assert.equal(
|
||||
HistoryController.get("displayEdit"),
|
||||
true,
|
||||
"it should display edit button when user can edit the post"
|
||||
);
|
||||
|
||||
HistoryController.set("topicController", null);
|
||||
assert.equal(
|
||||
HistoryController.get("displayEdit"),
|
||||
false,
|
||||
"it should not display edit button when there is not topic controller"
|
||||
);
|
||||
HistoryController.set("topicController", {});
|
||||
HistoryController.set("topicController", null);
|
||||
assert.equal(
|
||||
HistoryController.get("displayEdit"),
|
||||
false,
|
||||
"it should not display edit button when there is not topic controller"
|
||||
);
|
||||
HistoryController.set("topicController", {});
|
||||
|
||||
HistoryController.set("model.current_revision", 2);
|
||||
assert.equal(
|
||||
HistoryController.get("displayEdit"),
|
||||
false,
|
||||
"it should only display the edit button on the latest revision"
|
||||
);
|
||||
HistoryController.set("model.current_revision", 2);
|
||||
assert.equal(
|
||||
HistoryController.get("displayEdit"),
|
||||
false,
|
||||
"it should only display the edit button on the latest revision"
|
||||
);
|
||||
|
||||
const html = `<div class="revision-content">
|
||||
<p><img src="/uploads/default/original/1X/6b963ffc13cb0c053bbb90c92e99d4fe71b286ef.jpg" alt="" class="diff-del"><img/src=x onerror=alert(document.domain)>" width="276" height="183"></p>
|
||||
</div>
|
||||
<aside class="onebox allowlistedgeneric">
|
||||
<header class="source">
|
||||
<img src="/uploads/default/original/1X/1b0984d7ee08bce90572f46a1950e1ced436d028.png" class="site-icon" width="32" height="32">
|
||||
<a href="https://meta.discourse.org/t/discourse-version-2-5/125302">Discourse Meta – 9 Aug 19</a>
|
||||
</header>
|
||||
<article class="onebox-body">
|
||||
<img src="/uploads/default/optimized/1X/ecc92a52ee7353e03d5c0d1ea6521ce4541d9c25_2_500x500.png" class="thumbnail onebox-avatar d-lazyload" width="500" height="500">
|
||||
<h3><a href="https://meta.discourse.org/t/discourse-version-2-5/125302" target="_blank">Discourse Version 2.5</a></h3>
|
||||
<div style="clear: both"></div>
|
||||
</article>
|
||||
</aside>
|
||||
<table background="javascript:alert(\"HACKEDXSS\")">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Column</th>
|
||||
<th style="text-align:left">Test</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td background="javascript:alert('HACKEDXSS')">Osama</td>
|
||||
<td style="text-align:right">Testing</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>`;
|
||||
const html = `<div class="revision-content">
|
||||
<p><img src="/uploads/default/original/1X/6b963ffc13cb0c053bbb90c92e99d4fe71b286ef.jpg" alt="" class="diff-del"><img/src=x onerror=alert(document.domain)>" width="276" height="183"></p>
|
||||
</div>
|
||||
<aside class="onebox allowlistedgeneric">
|
||||
<header class="source">
|
||||
<img src="/uploads/default/original/1X/1b0984d7ee08bce90572f46a1950e1ced436d028.png" class="site-icon" width="32" height="32">
|
||||
<a href="https://meta.discourse.org/t/discourse-version-2-5/125302">Discourse Meta – 9 Aug 19</a>
|
||||
</header>
|
||||
<article class="onebox-body">
|
||||
<img src="/uploads/default/optimized/1X/ecc92a52ee7353e03d5c0d1ea6521ce4541d9c25_2_500x500.png" class="thumbnail onebox-avatar d-lazyload" width="500" height="500">
|
||||
<h3><a href="https://meta.discourse.org/t/discourse-version-2-5/125302" target="_blank">Discourse Version 2.5</a></h3>
|
||||
<div style="clear: both"></div>
|
||||
</article>
|
||||
</aside>
|
||||
<table background="javascript:alert(\"HACKEDXSS\")">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Column</th>
|
||||
<th style="text-align:left">Test</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td background="javascript:alert('HACKEDXSS')">Osama</td>
|
||||
<td style="text-align:right">Testing</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>`;
|
||||
|
||||
const expectedOutput = `<div class="revision-content">
|
||||
<p><img src="/uploads/default/original/1X/6b963ffc13cb0c053bbb90c92e99d4fe71b286ef.jpg" alt class="diff-del">" width="276" height="183"></p>
|
||||
</div>
|
||||
<aside class="onebox allowlistedgeneric">
|
||||
<header class="source">
|
||||
<img src="/uploads/default/original/1X/1b0984d7ee08bce90572f46a1950e1ced436d028.png" class="site-icon" width="32" height="32">
|
||||
<a href="https://meta.discourse.org/t/discourse-version-2-5/125302">Discourse Meta – 9 Aug 19</a>
|
||||
</header>
|
||||
<article class="onebox-body">
|
||||
<img src="/uploads/default/optimized/1X/ecc92a52ee7353e03d5c0d1ea6521ce4541d9c25_2_500x500.png" class="thumbnail onebox-avatar d-lazyload" width="500" height="500">
|
||||
<h3><a href="https://meta.discourse.org/t/discourse-version-2-5/125302" target="_blank">Discourse Version 2.5</a></h3>
|
||||
<div style="clear: both"></div>
|
||||
</article>
|
||||
</aside>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Column</th>
|
||||
<th style="text-align:left">Test</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Osama</td>
|
||||
<td style="text-align:right">Testing</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>`;
|
||||
const expectedOutput = `<div class="revision-content">
|
||||
<p><img src="/uploads/default/original/1X/6b963ffc13cb0c053bbb90c92e99d4fe71b286ef.jpg" alt class="diff-del">" width="276" height="183"></p>
|
||||
</div>
|
||||
<aside class="onebox allowlistedgeneric">
|
||||
<header class="source">
|
||||
<img src="/uploads/default/original/1X/1b0984d7ee08bce90572f46a1950e1ced436d028.png" class="site-icon" width="32" height="32">
|
||||
<a href="https://meta.discourse.org/t/discourse-version-2-5/125302">Discourse Meta – 9 Aug 19</a>
|
||||
</header>
|
||||
<article class="onebox-body">
|
||||
<img src="/uploads/default/optimized/1X/ecc92a52ee7353e03d5c0d1ea6521ce4541d9c25_2_500x500.png" class="thumbnail onebox-avatar d-lazyload" width="500" height="500">
|
||||
<h3><a href="https://meta.discourse.org/t/discourse-version-2-5/125302" target="_blank">Discourse Version 2.5</a></h3>
|
||||
<div style="clear: both"></div>
|
||||
</article>
|
||||
</aside>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Column</th>
|
||||
<th style="text-align:left">Test</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Osama</td>
|
||||
<td style="text-align:right">Testing</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>`;
|
||||
|
||||
HistoryController.setProperties({
|
||||
viewMode: "side_by_side",
|
||||
model: {
|
||||
body_changes: {
|
||||
side_by_side: html,
|
||||
HistoryController.setProperties({
|
||||
viewMode: "side_by_side",
|
||||
model: {
|
||||
body_changes: {
|
||||
side_by_side: html,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await HistoryController.bodyDiffChanged();
|
||||
|
||||
const output = HistoryController.get("bodyDiff");
|
||||
assert.equal(
|
||||
output,
|
||||
expectedOutput,
|
||||
"it keeps HTML safe and doesn't strip onebox tags"
|
||||
);
|
||||
});
|
||||
|
||||
await HistoryController.bodyDiffChanged();
|
||||
|
||||
const output = HistoryController.get("bodyDiff");
|
||||
assert.equal(
|
||||
output,
|
||||
expectedOutput,
|
||||
"it keeps HTML safe and doesn't strip onebox tags"
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,34 +1,36 @@
|
||||
import { moduleFor } from "ember-qunit";
|
||||
import { discourseModule } from "discourse/tests/helpers/qunit-helpers";
|
||||
import { test } from "qunit";
|
||||
import EmberObject from "@ember/object";
|
||||
moduleFor("controller:preferences/account");
|
||||
|
||||
test("updating of associated accounts", function (assert) {
|
||||
const controller = this.subject({
|
||||
siteSettings: {
|
||||
enable_google_oauth2_logins: true,
|
||||
},
|
||||
model: EmberObject.create({
|
||||
id: 70,
|
||||
second_factor_enabled: true,
|
||||
is_anonymous: true,
|
||||
}),
|
||||
currentUser: EmberObject.create({
|
||||
id: 1234,
|
||||
}),
|
||||
site: EmberObject.create({
|
||||
isMobileDevice: false,
|
||||
}),
|
||||
discourseModule("Unit | Controller | preferences/account", function () {
|
||||
test("updating of associated accounts", function (assert) {
|
||||
const controller = this.owner.lookup("controller:preferences/account");
|
||||
controller.setProperties({
|
||||
siteSettings: {
|
||||
enable_google_oauth2_logins: true,
|
||||
},
|
||||
model: EmberObject.create({
|
||||
id: 70,
|
||||
second_factor_enabled: true,
|
||||
is_anonymous: true,
|
||||
}),
|
||||
currentUser: EmberObject.create({
|
||||
id: 1234,
|
||||
}),
|
||||
site: EmberObject.create({
|
||||
isMobileDevice: false,
|
||||
}),
|
||||
});
|
||||
|
||||
assert.equal(controller.get("canUpdateAssociatedAccounts"), false);
|
||||
|
||||
controller.set("model.second_factor_enabled", false);
|
||||
assert.equal(controller.get("canUpdateAssociatedAccounts"), false);
|
||||
|
||||
controller.set("model.is_anonymous", false);
|
||||
assert.equal(controller.get("canUpdateAssociatedAccounts"), false);
|
||||
|
||||
controller.set("model.id", 1234);
|
||||
assert.equal(controller.get("canUpdateAssociatedAccounts"), true);
|
||||
});
|
||||
|
||||
assert.equal(controller.get("canUpdateAssociatedAccounts"), false);
|
||||
|
||||
controller.set("model.second_factor_enabled", false);
|
||||
assert.equal(controller.get("canUpdateAssociatedAccounts"), false);
|
||||
|
||||
controller.set("model.is_anonymous", false);
|
||||
assert.equal(controller.get("canUpdateAssociatedAccounts"), false);
|
||||
|
||||
controller.set("model.id", 1234);
|
||||
assert.equal(controller.get("canUpdateAssociatedAccounts"), true);
|
||||
});
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import { moduleFor } from "ember-qunit";
|
||||
import { discourseModule } from "discourse/tests/helpers/qunit-helpers";
|
||||
import { test } from "qunit";
|
||||
moduleFor("controller:preferences/second-factor");
|
||||
|
||||
test("displayOAuthWarning when OAuth login methods are enabled", function (assert) {
|
||||
const controller = this.subject({
|
||||
siteSettings: {
|
||||
enable_google_oauth2_logins: true,
|
||||
},
|
||||
discourseModule("Unit | Controller | preferences/second-factor", function () {
|
||||
test("displayOAuthWarning when OAuth login methods are enabled", function (assert) {
|
||||
const controller = this.owner.lookup(
|
||||
"controller:preferences/second-factor"
|
||||
);
|
||||
controller.setProperties({
|
||||
siteSettings: {
|
||||
enable_google_oauth2_logins: true,
|
||||
},
|
||||
});
|
||||
|
||||
assert.equal(controller.get("displayOAuthWarning"), true);
|
||||
});
|
||||
|
||||
assert.equal(controller.get("displayOAuthWarning"), true);
|
||||
});
|
||||
|
||||
@@ -1,205 +1,211 @@
|
||||
import { moduleFor } from "ember-qunit";
|
||||
import { discourseModule } from "discourse/tests/helpers/qunit-helpers";
|
||||
import { test } from "qunit";
|
||||
import EmberObject from "@ember/object";
|
||||
import { mapRoutes } from "discourse/mapping-router";
|
||||
import createStore from "discourse/tests/helpers/create-store";
|
||||
|
||||
moduleFor("controller:reorder-categories", "controller:reorder-categories", {
|
||||
beforeEach() {
|
||||
this.registry.register("router:main", mapRoutes());
|
||||
},
|
||||
needs: ["controller:modal"],
|
||||
});
|
||||
discourseModule("Unit | Controller | reorder-categories", function () {
|
||||
test("reorder set unique position number", function (assert) {
|
||||
const store = createStore();
|
||||
|
||||
test("reorder set unique position number", function (assert) {
|
||||
const store = createStore();
|
||||
const categories = [];
|
||||
for (let i = 0; i < 3; ++i) {
|
||||
categories.push(store.createRecord("category", { id: i, position: 0 }));
|
||||
}
|
||||
|
||||
const categories = [];
|
||||
for (let i = 0; i < 3; ++i) {
|
||||
categories.push(store.createRecord("category", { id: i, position: 0 }));
|
||||
}
|
||||
const site = EmberObject.create({ categories: categories });
|
||||
const reorderCategoriesController = this.owner.lookup(
|
||||
"controller:reorder-categories"
|
||||
);
|
||||
reorderCategoriesController.setProperties({ site });
|
||||
reorderCategoriesController.reorder();
|
||||
|
||||
const site = EmberObject.create({ categories: categories });
|
||||
const reorderCategoriesController = this.subject({ site });
|
||||
reorderCategoriesController
|
||||
.get("categoriesOrdered")
|
||||
.forEach((category, index) => {
|
||||
assert.equal(category.get("position"), index);
|
||||
});
|
||||
});
|
||||
|
||||
reorderCategoriesController.reorder();
|
||||
test("reorder places subcategories after their parent categories, while maintaining the relative order", function (assert) {
|
||||
const store = createStore();
|
||||
|
||||
reorderCategoriesController
|
||||
.get("categoriesOrdered")
|
||||
.forEach((category, index) => {
|
||||
assert.equal(category.get("position"), index);
|
||||
const parent = store.createRecord("category", {
|
||||
id: 1,
|
||||
position: 1,
|
||||
slug: "parent",
|
||||
});
|
||||
});
|
||||
|
||||
test("reorder places subcategories after their parent categories, while maintaining the relative order", function (assert) {
|
||||
const store = createStore();
|
||||
|
||||
const parent = store.createRecord("category", {
|
||||
id: 1,
|
||||
position: 1,
|
||||
slug: "parent",
|
||||
});
|
||||
const child1 = store.createRecord("category", {
|
||||
id: 2,
|
||||
position: 3,
|
||||
slug: "child1",
|
||||
parent_category_id: 1,
|
||||
});
|
||||
const child2 = store.createRecord("category", {
|
||||
id: 3,
|
||||
position: 0,
|
||||
slug: "child2",
|
||||
parent_category_id: 1,
|
||||
});
|
||||
const other = store.createRecord("category", {
|
||||
id: 4,
|
||||
position: 2,
|
||||
slug: "other",
|
||||
});
|
||||
|
||||
const categories = [child2, parent, other, child1];
|
||||
const expectedOrderSlugs = ["parent", "child2", "child1", "other"];
|
||||
|
||||
const site = EmberObject.create({ categories: categories });
|
||||
const reorderCategoriesController = this.subject({ site });
|
||||
|
||||
reorderCategoriesController.reorder();
|
||||
|
||||
assert.deepEqual(
|
||||
reorderCategoriesController.get("categoriesOrdered").mapBy("slug"),
|
||||
expectedOrderSlugs
|
||||
);
|
||||
});
|
||||
|
||||
test("changing the position number of a category should place it at given position", function (assert) {
|
||||
const store = createStore();
|
||||
|
||||
const elem1 = store.createRecord("category", {
|
||||
id: 1,
|
||||
position: 0,
|
||||
slug: "foo",
|
||||
});
|
||||
|
||||
const elem2 = store.createRecord("category", {
|
||||
id: 2,
|
||||
position: 1,
|
||||
slug: "bar",
|
||||
});
|
||||
|
||||
const elem3 = store.createRecord("category", {
|
||||
id: 3,
|
||||
position: 2,
|
||||
slug: "test",
|
||||
});
|
||||
|
||||
const categories = [elem1, elem2, elem3];
|
||||
const site = EmberObject.create({ categories: categories });
|
||||
const reorderCategoriesController = this.subject({ site });
|
||||
|
||||
reorderCategoriesController.actions.change.call(
|
||||
reorderCategoriesController,
|
||||
elem1,
|
||||
{ target: { value: "2" } }
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
reorderCategoriesController.get("categoriesOrdered").mapBy("slug"),
|
||||
["test", "bar", "foo"]
|
||||
);
|
||||
});
|
||||
|
||||
test("changing the position number of a category should place it at given position and respect children", function (assert) {
|
||||
const store = createStore();
|
||||
|
||||
const elem1 = store.createRecord("category", {
|
||||
id: 1,
|
||||
position: 0,
|
||||
slug: "foo",
|
||||
});
|
||||
|
||||
const child1 = store.createRecord("category", {
|
||||
id: 4,
|
||||
position: 1,
|
||||
slug: "foochild",
|
||||
parent_category_id: 1,
|
||||
});
|
||||
|
||||
const elem2 = store.createRecord("category", {
|
||||
id: 2,
|
||||
position: 2,
|
||||
slug: "bar",
|
||||
});
|
||||
|
||||
const elem3 = store.createRecord("category", {
|
||||
id: 3,
|
||||
position: 3,
|
||||
slug: "test",
|
||||
});
|
||||
|
||||
const categories = [elem1, child1, elem2, elem3];
|
||||
const site = EmberObject.create({ categories: categories });
|
||||
const reorderCategoriesController = this.subject({ site });
|
||||
|
||||
reorderCategoriesController.actions.change.call(
|
||||
reorderCategoriesController,
|
||||
elem1,
|
||||
{ target: { value: 3 } }
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
reorderCategoriesController.get("categoriesOrdered").mapBy("slug"),
|
||||
["test", "bar", "foo", "foochild"]
|
||||
);
|
||||
});
|
||||
|
||||
test("changing the position through click on arrow of a category should place it at given position and respect children", function (assert) {
|
||||
const store = createStore();
|
||||
|
||||
const elem1 = store.createRecord("category", {
|
||||
id: 1,
|
||||
position: 0,
|
||||
slug: "foo",
|
||||
});
|
||||
|
||||
const child1 = store.createRecord("category", {
|
||||
id: 4,
|
||||
position: 1,
|
||||
slug: "foochild",
|
||||
parent_category_id: 1,
|
||||
});
|
||||
|
||||
const child2 = store.createRecord("category", {
|
||||
id: 5,
|
||||
position: 2,
|
||||
slug: "foochildchild",
|
||||
parent_category_id: 4,
|
||||
});
|
||||
|
||||
const elem2 = store.createRecord("category", {
|
||||
id: 2,
|
||||
position: 3,
|
||||
slug: "bar",
|
||||
});
|
||||
|
||||
const elem3 = store.createRecord("category", {
|
||||
id: 3,
|
||||
position: 4,
|
||||
slug: "test",
|
||||
});
|
||||
|
||||
const categories = [elem1, child1, child2, elem2, elem3];
|
||||
const site = EmberObject.create({ categories: categories });
|
||||
const reorderCategoriesController = this.subject({ site });
|
||||
|
||||
reorderCategoriesController.reorder();
|
||||
|
||||
reorderCategoriesController.actions.moveDown.call(
|
||||
reorderCategoriesController,
|
||||
elem1
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
reorderCategoriesController.get("categoriesOrdered").mapBy("slug"),
|
||||
["bar", "foo", "foochild", "foochildchild", "test"]
|
||||
);
|
||||
const child1 = store.createRecord("category", {
|
||||
id: 2,
|
||||
position: 3,
|
||||
slug: "child1",
|
||||
parent_category_id: 1,
|
||||
});
|
||||
const child2 = store.createRecord("category", {
|
||||
id: 3,
|
||||
position: 0,
|
||||
slug: "child2",
|
||||
parent_category_id: 1,
|
||||
});
|
||||
const other = store.createRecord("category", {
|
||||
id: 4,
|
||||
position: 2,
|
||||
slug: "other",
|
||||
});
|
||||
|
||||
const categories = [child2, parent, other, child1];
|
||||
const expectedOrderSlugs = ["parent", "child2", "child1", "other"];
|
||||
|
||||
const site = EmberObject.create({ categories: categories });
|
||||
const reorderCategoriesController = this.owner.lookup(
|
||||
"controller:reorder-categories"
|
||||
);
|
||||
reorderCategoriesController.setProperties({ site });
|
||||
reorderCategoriesController.reorder();
|
||||
|
||||
assert.deepEqual(
|
||||
reorderCategoriesController.get("categoriesOrdered").mapBy("slug"),
|
||||
expectedOrderSlugs
|
||||
);
|
||||
});
|
||||
|
||||
test("changing the position number of a category should place it at given position", function (assert) {
|
||||
const store = createStore();
|
||||
|
||||
const elem1 = store.createRecord("category", {
|
||||
id: 1,
|
||||
position: 0,
|
||||
slug: "foo",
|
||||
});
|
||||
|
||||
const elem2 = store.createRecord("category", {
|
||||
id: 2,
|
||||
position: 1,
|
||||
slug: "bar",
|
||||
});
|
||||
|
||||
const elem3 = store.createRecord("category", {
|
||||
id: 3,
|
||||
position: 2,
|
||||
slug: "test",
|
||||
});
|
||||
|
||||
const categories = [elem1, elem2, elem3];
|
||||
const site = EmberObject.create({ categories: categories });
|
||||
const reorderCategoriesController = this.owner.lookup(
|
||||
"controller:reorder-categories"
|
||||
);
|
||||
reorderCategoriesController.setProperties({ site });
|
||||
|
||||
reorderCategoriesController.actions.change.call(
|
||||
reorderCategoriesController,
|
||||
elem1,
|
||||
{ target: { value: "2" } }
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
reorderCategoriesController.get("categoriesOrdered").mapBy("slug"),
|
||||
["test", "bar", "foo"]
|
||||
);
|
||||
});
|
||||
|
||||
test("changing the position number of a category should place it at given position and respect children", function (assert) {
|
||||
const store = createStore();
|
||||
|
||||
const elem1 = store.createRecord("category", {
|
||||
id: 1,
|
||||
position: 0,
|
||||
slug: "foo",
|
||||
});
|
||||
|
||||
const child1 = store.createRecord("category", {
|
||||
id: 4,
|
||||
position: 1,
|
||||
slug: "foochild",
|
||||
parent_category_id: 1,
|
||||
});
|
||||
|
||||
const elem2 = store.createRecord("category", {
|
||||
id: 2,
|
||||
position: 2,
|
||||
slug: "bar",
|
||||
});
|
||||
|
||||
const elem3 = store.createRecord("category", {
|
||||
id: 3,
|
||||
position: 3,
|
||||
slug: "test",
|
||||
});
|
||||
|
||||
const categories = [elem1, child1, elem2, elem3];
|
||||
const site = EmberObject.create({ categories: categories });
|
||||
const reorderCategoriesController = this.owner.lookup(
|
||||
"controller:reorder-categories"
|
||||
);
|
||||
reorderCategoriesController.setProperties({ site });
|
||||
|
||||
reorderCategoriesController.actions.change.call(
|
||||
reorderCategoriesController,
|
||||
elem1,
|
||||
{ target: { value: 3 } }
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
reorderCategoriesController.get("categoriesOrdered").mapBy("slug"),
|
||||
["test", "bar", "foo", "foochild"]
|
||||
);
|
||||
});
|
||||
|
||||
test("changing the position through click on arrow of a category should place it at given position and respect children", function (assert) {
|
||||
const store = createStore();
|
||||
|
||||
const elem1 = store.createRecord("category", {
|
||||
id: 1,
|
||||
position: 0,
|
||||
slug: "foo",
|
||||
});
|
||||
|
||||
const child1 = store.createRecord("category", {
|
||||
id: 4,
|
||||
position: 1,
|
||||
slug: "foochild",
|
||||
parent_category_id: 1,
|
||||
});
|
||||
|
||||
const child2 = store.createRecord("category", {
|
||||
id: 5,
|
||||
position: 2,
|
||||
slug: "foochildchild",
|
||||
parent_category_id: 4,
|
||||
});
|
||||
|
||||
const elem2 = store.createRecord("category", {
|
||||
id: 2,
|
||||
position: 3,
|
||||
slug: "bar",
|
||||
});
|
||||
|
||||
const elem3 = store.createRecord("category", {
|
||||
id: 3,
|
||||
position: 4,
|
||||
slug: "test",
|
||||
});
|
||||
|
||||
const categories = [elem1, child1, child2, elem2, elem3];
|
||||
const site = EmberObject.create({ categories: categories });
|
||||
const reorderCategoriesController = this.owner.lookup(
|
||||
"controller:reorder-categories"
|
||||
);
|
||||
reorderCategoriesController.setProperties({ site });
|
||||
reorderCategoriesController.reorder();
|
||||
|
||||
reorderCategoriesController.actions.moveDown.call(
|
||||
reorderCategoriesController,
|
||||
elem1
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
reorderCategoriesController.get("categoriesOrdered").mapBy("slug"),
|
||||
["bar", "foo", "foochild", "foochildchild", "test"]
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -18,238 +18,243 @@ function setTemplates(lookupTemplateStrings) {
|
||||
|
||||
const DiscourseResolver = buildResolver("discourse");
|
||||
|
||||
module("lib:resolver", {
|
||||
beforeEach() {
|
||||
module("Unit | Ember | resolver", function (hooks) {
|
||||
hooks.beforeEach(function () {
|
||||
originalTemplates = Ember.TEMPLATES;
|
||||
Ember.TEMPLATES = {};
|
||||
|
||||
resolver = DiscourseResolver.create();
|
||||
},
|
||||
});
|
||||
|
||||
afterEach() {
|
||||
hooks.afterEach(function () {
|
||||
Ember.TEMPLATES = originalTemplates;
|
||||
},
|
||||
});
|
||||
|
||||
test("finds templates in top level dir", function (assert) {
|
||||
setTemplates(["foobar", "fooBar", "foo_bar", "foo.bar"]);
|
||||
|
||||
lookupTemplate(assert, "template:foobar", "foobar", "by lowcased name");
|
||||
lookupTemplate(assert, "template:fooBar", "fooBar", "by camel cased name");
|
||||
lookupTemplate(assert, "template:foo_bar", "foo_bar", "by underscored name");
|
||||
lookupTemplate(assert, "template:foo.bar", "foo.bar", "by dotted name");
|
||||
});
|
||||
|
||||
test("finds templates in first-level subdir", function (assert) {
|
||||
setTemplates(["foo/bar_baz"]);
|
||||
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:foo/bar_baz",
|
||||
"foo/bar_baz",
|
||||
"with subdir defined by slash"
|
||||
);
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:foo.bar_baz",
|
||||
"foo/bar_baz",
|
||||
"with subdir defined by dot"
|
||||
);
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:fooBarBaz",
|
||||
"foo/bar_baz",
|
||||
"with subdir defined by first camel case and the rest of camel cases converted to underscores"
|
||||
);
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:foo_bar_baz",
|
||||
"foo/bar_baz",
|
||||
"with subdir defined by first underscore"
|
||||
);
|
||||
});
|
||||
|
||||
test("resolves precedence between overlapping top level dir and first level subdir templates", function (assert) {
|
||||
setTemplates(["fooBar", "foo_bar", "foo.bar", "foo/bar"]);
|
||||
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:foo.bar",
|
||||
"foo/bar",
|
||||
"preferring first level subdir for dotted name"
|
||||
);
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:fooBar",
|
||||
"fooBar",
|
||||
"preferring top level dir for camel cased name"
|
||||
);
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:foo_bar",
|
||||
"foo_bar",
|
||||
"preferring top level dir for underscored name"
|
||||
);
|
||||
});
|
||||
|
||||
test("finds templates in subdir deeper than one level", function (assert) {
|
||||
setTemplates(["foo/bar/baz/qux"]);
|
||||
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:foo/bar/baz/qux",
|
||||
"foo/bar/baz/qux",
|
||||
"for subdirs defined by slashes"
|
||||
);
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:foo.bar.baz.qux",
|
||||
"foo/bar/baz/qux",
|
||||
"for subdirs defined by dots"
|
||||
);
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:foo/bar/bazQux",
|
||||
"foo/bar/baz/qux",
|
||||
"for subdirs defined by slashes plus one camel case"
|
||||
);
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:foo/bar/baz_qux",
|
||||
"foo/bar/baz/qux",
|
||||
"for subdirs defined by slashes plus one underscore"
|
||||
);
|
||||
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:fooBarBazQux",
|
||||
undefined,
|
||||
"but not for subdirs defined by more than one camel case"
|
||||
);
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:foo_bar_baz_qux",
|
||||
undefined,
|
||||
"but not for subdirs defined by more than one underscore"
|
||||
);
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:foo.bar.bazQux",
|
||||
undefined,
|
||||
"but not for subdirs defined by dots plus one camel case"
|
||||
);
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:foo.bar.baz_qux",
|
||||
undefined,
|
||||
"but not for subdirs defined by dots plus one underscore"
|
||||
);
|
||||
});
|
||||
|
||||
test("resolves mobile templates to 'mobile/' namespace", function (assert) {
|
||||
setTemplates(["mobile/foo", "bar", "mobile/bar", "baz"]);
|
||||
|
||||
setResolverOption("mobileView", true);
|
||||
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:foo",
|
||||
"mobile/foo",
|
||||
"finding mobile version even if normal one is not present"
|
||||
);
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:bar",
|
||||
"mobile/bar",
|
||||
"preferring mobile version when both mobile and normal versions are present"
|
||||
);
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:baz",
|
||||
"baz",
|
||||
"falling back to a normal version when mobile version is not present"
|
||||
);
|
||||
});
|
||||
|
||||
test("resolves plugin templates to 'javascripts/' namespace", function (assert) {
|
||||
setTemplates(["javascripts/foo", "bar", "javascripts/bar", "baz"]);
|
||||
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:foo",
|
||||
"javascripts/foo",
|
||||
"finding plugin version even if normal one is not present"
|
||||
);
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:bar",
|
||||
"javascripts/bar",
|
||||
"preferring plugin version when both versions are present"
|
||||
);
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:baz",
|
||||
"baz",
|
||||
"falling back to a normal version when plugin version is not present"
|
||||
);
|
||||
});
|
||||
|
||||
test("resolves templates with 'admin' prefix to 'admin/templates/' namespace", function (assert) {
|
||||
setTemplates([
|
||||
"admin/templates/foo",
|
||||
"adminBar",
|
||||
"admin_bar",
|
||||
"admin.bar",
|
||||
"admin/templates/bar",
|
||||
]);
|
||||
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:adminFoo",
|
||||
"admin/templates/foo",
|
||||
"when prefix is separated by camel case"
|
||||
);
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:admin_foo",
|
||||
"admin/templates/foo",
|
||||
"when prefix is separated by underscore"
|
||||
);
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:admin.foo",
|
||||
"admin/templates/foo",
|
||||
"when prefix is separated by dot"
|
||||
);
|
||||
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:adminfoo",
|
||||
undefined,
|
||||
"but not when prefix is not separated in any way"
|
||||
);
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:adminBar",
|
||||
"adminBar",
|
||||
"but not when template with the exact camel cased name exists"
|
||||
);
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:admin_bar",
|
||||
"admin_bar",
|
||||
"but not when template with the exact underscored name exists"
|
||||
);
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:admin.bar",
|
||||
"admin.bar",
|
||||
"but not when template with the exact dotted name exists"
|
||||
);
|
||||
});
|
||||
|
||||
test("returns 'not_found' template when template name cannot be resolved", function (assert) {
|
||||
setTemplates(["not_found"]);
|
||||
|
||||
lookupTemplate(assert, "template:foo/bar/baz", "not_found", "");
|
||||
});
|
||||
|
||||
test("finds templates in top level dir", function (assert) {
|
||||
setTemplates(["foobar", "fooBar", "foo_bar", "foo.bar"]);
|
||||
|
||||
lookupTemplate(assert, "template:foobar", "foobar", "by lowcased name");
|
||||
lookupTemplate(assert, "template:fooBar", "fooBar", "by camel cased name");
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:foo_bar",
|
||||
"foo_bar",
|
||||
"by underscored name"
|
||||
);
|
||||
lookupTemplate(assert, "template:foo.bar", "foo.bar", "by dotted name");
|
||||
});
|
||||
|
||||
test("finds templates in first-level subdir", function (assert) {
|
||||
setTemplates(["foo/bar_baz"]);
|
||||
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:foo/bar_baz",
|
||||
"foo/bar_baz",
|
||||
"with subdir defined by slash"
|
||||
);
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:foo.bar_baz",
|
||||
"foo/bar_baz",
|
||||
"with subdir defined by dot"
|
||||
);
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:fooBarBaz",
|
||||
"foo/bar_baz",
|
||||
"with subdir defined by first camel case and the rest of camel cases converted to underscores"
|
||||
);
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:foo_bar_baz",
|
||||
"foo/bar_baz",
|
||||
"with subdir defined by first underscore"
|
||||
);
|
||||
});
|
||||
|
||||
test("resolves precedence between overlapping top level dir and first level subdir templates", function (assert) {
|
||||
setTemplates(["fooBar", "foo_bar", "foo.bar", "foo/bar"]);
|
||||
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:foo.bar",
|
||||
"foo/bar",
|
||||
"preferring first level subdir for dotted name"
|
||||
);
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:fooBar",
|
||||
"fooBar",
|
||||
"preferring top level dir for camel cased name"
|
||||
);
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:foo_bar",
|
||||
"foo_bar",
|
||||
"preferring top level dir for underscored name"
|
||||
);
|
||||
});
|
||||
|
||||
test("finds templates in subdir deeper than one level", function (assert) {
|
||||
setTemplates(["foo/bar/baz/qux"]);
|
||||
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:foo/bar/baz/qux",
|
||||
"foo/bar/baz/qux",
|
||||
"for subdirs defined by slashes"
|
||||
);
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:foo.bar.baz.qux",
|
||||
"foo/bar/baz/qux",
|
||||
"for subdirs defined by dots"
|
||||
);
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:foo/bar/bazQux",
|
||||
"foo/bar/baz/qux",
|
||||
"for subdirs defined by slashes plus one camel case"
|
||||
);
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:foo/bar/baz_qux",
|
||||
"foo/bar/baz/qux",
|
||||
"for subdirs defined by slashes plus one underscore"
|
||||
);
|
||||
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:fooBarBazQux",
|
||||
undefined,
|
||||
"but not for subdirs defined by more than one camel case"
|
||||
);
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:foo_bar_baz_qux",
|
||||
undefined,
|
||||
"but not for subdirs defined by more than one underscore"
|
||||
);
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:foo.bar.bazQux",
|
||||
undefined,
|
||||
"but not for subdirs defined by dots plus one camel case"
|
||||
);
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:foo.bar.baz_qux",
|
||||
undefined,
|
||||
"but not for subdirs defined by dots plus one underscore"
|
||||
);
|
||||
});
|
||||
|
||||
test("resolves mobile templates to 'mobile/' namespace", function (assert) {
|
||||
setTemplates(["mobile/foo", "bar", "mobile/bar", "baz"]);
|
||||
|
||||
setResolverOption("mobileView", true);
|
||||
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:foo",
|
||||
"mobile/foo",
|
||||
"finding mobile version even if normal one is not present"
|
||||
);
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:bar",
|
||||
"mobile/bar",
|
||||
"preferring mobile version when both mobile and normal versions are present"
|
||||
);
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:baz",
|
||||
"baz",
|
||||
"falling back to a normal version when mobile version is not present"
|
||||
);
|
||||
});
|
||||
|
||||
test("resolves plugin templates to 'javascripts/' namespace", function (assert) {
|
||||
setTemplates(["javascripts/foo", "bar", "javascripts/bar", "baz"]);
|
||||
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:foo",
|
||||
"javascripts/foo",
|
||||
"finding plugin version even if normal one is not present"
|
||||
);
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:bar",
|
||||
"javascripts/bar",
|
||||
"preferring plugin version when both versions are present"
|
||||
);
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:baz",
|
||||
"baz",
|
||||
"falling back to a normal version when plugin version is not present"
|
||||
);
|
||||
});
|
||||
|
||||
test("resolves templates with 'admin' prefix to 'admin/templates/' namespace", function (assert) {
|
||||
setTemplates([
|
||||
"admin/templates/foo",
|
||||
"adminBar",
|
||||
"admin_bar",
|
||||
"admin.bar",
|
||||
"admin/templates/bar",
|
||||
]);
|
||||
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:adminFoo",
|
||||
"admin/templates/foo",
|
||||
"when prefix is separated by camel case"
|
||||
);
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:admin_foo",
|
||||
"admin/templates/foo",
|
||||
"when prefix is separated by underscore"
|
||||
);
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:admin.foo",
|
||||
"admin/templates/foo",
|
||||
"when prefix is separated by dot"
|
||||
);
|
||||
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:adminfoo",
|
||||
undefined,
|
||||
"but not when prefix is not separated in any way"
|
||||
);
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:adminBar",
|
||||
"adminBar",
|
||||
"but not when template with the exact camel cased name exists"
|
||||
);
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:admin_bar",
|
||||
"admin_bar",
|
||||
"but not when template with the exact underscored name exists"
|
||||
);
|
||||
lookupTemplate(
|
||||
assert,
|
||||
"template:admin.bar",
|
||||
"admin.bar",
|
||||
"but not when template with the exact dotted name exists"
|
||||
);
|
||||
});
|
||||
|
||||
test("returns 'not_found' template when template name cannot be resolved", function (assert) {
|
||||
setTemplates(["not_found"]);
|
||||
|
||||
lookupTemplate(assert, "template:foo/bar/baz", "not_found", "");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,59 +1,59 @@
|
||||
import { test, module } from "qunit";
|
||||
import AllowLister from "pretty-text/allow-lister";
|
||||
|
||||
module("lib:allowLister");
|
||||
module("Unit | Utility | allowLister", function () {
|
||||
test("allowLister", function (assert) {
|
||||
const allowLister = new AllowLister();
|
||||
|
||||
test("allowLister", function (assert) {
|
||||
const allowLister = new AllowLister();
|
||||
assert.ok(
|
||||
Object.keys(allowLister.getAllowList().tagList).length > 1,
|
||||
"should have some defaults"
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
Object.keys(allowLister.getAllowList().tagList).length > 1,
|
||||
"should have some defaults"
|
||||
);
|
||||
allowLister.disable("default");
|
||||
|
||||
allowLister.disable("default");
|
||||
assert.ok(
|
||||
Object.keys(allowLister.getAllowList().tagList).length === 0,
|
||||
"should have no defaults if disabled"
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
Object.keys(allowLister.getAllowList().tagList).length === 0,
|
||||
"should have no defaults if disabled"
|
||||
);
|
||||
allowLister.allowListFeature("test", [
|
||||
"custom.foo",
|
||||
"custom.baz",
|
||||
"custom[data-*]",
|
||||
"custom[rel=nofollow]",
|
||||
]);
|
||||
|
||||
allowLister.allowListFeature("test", [
|
||||
"custom.foo",
|
||||
"custom.baz",
|
||||
"custom[data-*]",
|
||||
"custom[rel=nofollow]",
|
||||
]);
|
||||
allowLister.allowListFeature("test", ["custom[rel=test]"]);
|
||||
|
||||
allowLister.allowListFeature("test", ["custom[rel=test]"]);
|
||||
allowLister.enable("test");
|
||||
|
||||
allowLister.enable("test");
|
||||
|
||||
assert.deepEqual(
|
||||
allowLister.getAllowList(),
|
||||
{
|
||||
tagList: {
|
||||
custom: [],
|
||||
},
|
||||
attrList: {
|
||||
custom: {
|
||||
class: ["foo", "baz"],
|
||||
"data-*": ["*"],
|
||||
rel: ["nofollow", "test"],
|
||||
assert.deepEqual(
|
||||
allowLister.getAllowList(),
|
||||
{
|
||||
tagList: {
|
||||
custom: [],
|
||||
},
|
||||
attrList: {
|
||||
custom: {
|
||||
class: ["foo", "baz"],
|
||||
"data-*": ["*"],
|
||||
rel: ["nofollow", "test"],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"Expecting a correct white list"
|
||||
);
|
||||
"Expecting a correct white list"
|
||||
);
|
||||
|
||||
allowLister.disable("test");
|
||||
allowLister.disable("test");
|
||||
|
||||
assert.deepEqual(
|
||||
allowLister.getAllowList(),
|
||||
{
|
||||
tagList: {},
|
||||
attrList: {},
|
||||
},
|
||||
"Expecting an empty white list"
|
||||
);
|
||||
assert.deepEqual(
|
||||
allowLister.getAllowList(),
|
||||
{
|
||||
tagList: {},
|
||||
attrList: {},
|
||||
},
|
||||
"Expecting an empty white list"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import { test, module } from "qunit";
|
||||
import { parseBBCodeTag } from "pretty-text/engines/discourse-markdown/bbcode-block";
|
||||
|
||||
module("lib:pretty-text:bbcode");
|
||||
|
||||
test("block with multiple quoted attributes", function (assert) {
|
||||
const parsed = parseBBCodeTag('[test one="foo" two="bar bar"]', 0, 30);
|
||||
|
||||
assert.equal(parsed.tag, "test");
|
||||
assert.equal(parsed.attrs.one, "foo");
|
||||
assert.equal(parsed.attrs.two, "bar bar");
|
||||
});
|
||||
@@ -3,45 +3,45 @@ import { test, module } from "qunit";
|
||||
import { formattedReminderTime } from "discourse/lib/bookmark";
|
||||
import { fakeTime } from "discourse/tests/helpers/qunit-helpers";
|
||||
|
||||
module("lib:bookmark", {
|
||||
beforeEach() {
|
||||
module("Unit | Utility | bookmark", function (hooks) {
|
||||
hooks.beforeEach(function () {
|
||||
fakeTime("2020-04-11 08:00:00", "Australia/Brisbane");
|
||||
},
|
||||
});
|
||||
|
||||
afterEach() {
|
||||
hooks.afterEach(function () {
|
||||
sinon.restore();
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test("formattedReminderTime works when the reminder time is tomorrow", function (assert) {
|
||||
let reminderAt = "2020-04-12 09:45:00";
|
||||
let reminderAtDate = moment
|
||||
.tz(reminderAt, "Australia/Brisbane")
|
||||
.format("H:mm a");
|
||||
assert.equal(
|
||||
formattedReminderTime(reminderAt, "Australia/Brisbane"),
|
||||
"tomorrow at " + reminderAtDate
|
||||
);
|
||||
});
|
||||
test("formattedReminderTime works when the reminder time is tomorrow", function (assert) {
|
||||
let reminderAt = "2020-04-12 09:45:00";
|
||||
let reminderAtDate = moment
|
||||
.tz(reminderAt, "Australia/Brisbane")
|
||||
.format("H:mm a");
|
||||
assert.equal(
|
||||
formattedReminderTime(reminderAt, "Australia/Brisbane"),
|
||||
"tomorrow at " + reminderAtDate
|
||||
);
|
||||
});
|
||||
|
||||
test("formattedReminderTime works when the reminder time is today", function (assert) {
|
||||
let reminderAt = "2020-04-11 09:45:00";
|
||||
let reminderAtDate = moment
|
||||
.tz(reminderAt, "Australia/Brisbane")
|
||||
.format("H:mm a");
|
||||
assert.equal(
|
||||
formattedReminderTime(reminderAt, "Australia/Brisbane"),
|
||||
"today at " + reminderAtDate
|
||||
);
|
||||
});
|
||||
test("formattedReminderTime works when the reminder time is today", function (assert) {
|
||||
let reminderAt = "2020-04-11 09:45:00";
|
||||
let reminderAtDate = moment
|
||||
.tz(reminderAt, "Australia/Brisbane")
|
||||
.format("H:mm a");
|
||||
assert.equal(
|
||||
formattedReminderTime(reminderAt, "Australia/Brisbane"),
|
||||
"today at " + reminderAtDate
|
||||
);
|
||||
});
|
||||
|
||||
test("formattedReminderTime works when the reminder time is in the future", function (assert) {
|
||||
let reminderAt = "2020-04-15 09:45:00";
|
||||
let reminderAtDate = moment
|
||||
.tz(reminderAt, "Australia/Brisbane")
|
||||
.format("H:mm a");
|
||||
assert.equal(
|
||||
formattedReminderTime(reminderAt, "Australia/Brisbane"),
|
||||
"at Apr 15, 2020 " + reminderAtDate
|
||||
);
|
||||
test("formattedReminderTime works when the reminder time is in the future", function (assert) {
|
||||
let reminderAt = "2020-04-15 09:45:00";
|
||||
let reminderAtDate = moment
|
||||
.tz(reminderAt, "Australia/Brisbane")
|
||||
.format("H:mm a");
|
||||
assert.equal(
|
||||
formattedReminderTime(reminderAt, "Australia/Brisbane"),
|
||||
"at Apr 15, 2020 " + reminderAtDate
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||
import { test, module } from "qunit";
|
||||
/* global BreakString:true */
|
||||
|
||||
module("lib:breakString", {});
|
||||
module("Unit | Utility | breakString", function () {
|
||||
test("breakString", function (assert) {
|
||||
const b = (s, hint) => new BreakString(s).break(hint);
|
||||
|
||||
test("breakString", function (assert) {
|
||||
var b = function (s, hint) {
|
||||
return new BreakString(s).break(hint);
|
||||
};
|
||||
|
||||
assert.equal(b("hello"), "hello");
|
||||
assert.equal(b("helloworld"), "helloworld");
|
||||
assert.equal(b("HeMans11"), "He<wbr>​Mans<wbr>​11");
|
||||
assert.equal(b("he_man"), "he_<wbr>​man");
|
||||
assert.equal(b("he11111"), "he<wbr>​11111");
|
||||
assert.equal(b("HRCBob"), "HRC<wbr>​Bob");
|
||||
assert.equal(
|
||||
b("bobmarleytoo", "Bob Marley Too"),
|
||||
"bob<wbr>​marley<wbr>​too"
|
||||
);
|
||||
assert.equal(b("hello"), "hello");
|
||||
assert.equal(b("helloworld"), "helloworld");
|
||||
assert.equal(b("HeMans11"), "He<wbr>​Mans<wbr>​11");
|
||||
assert.equal(b("he_man"), "he_<wbr>​man");
|
||||
assert.equal(b("he11111"), "he<wbr>​11111");
|
||||
assert.equal(b("HRCBob"), "HRC<wbr>​Bob");
|
||||
assert.equal(
|
||||
b("bobmarleytoo", "Bob Marley Too"),
|
||||
"bob<wbr>​marley<wbr>​too"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,150 +3,170 @@ import createStore from "discourse/tests/helpers/create-store";
|
||||
import { discourseModule } from "discourse/tests/helpers/qunit-helpers";
|
||||
import Site from "discourse/models/site";
|
||||
import sinon from "sinon";
|
||||
|
||||
discourseModule("lib:category-link");
|
||||
|
||||
import { categoryBadgeHTML } from "discourse/helpers/category-link";
|
||||
|
||||
test("categoryBadge without a category", function (assert) {
|
||||
assert.blank(categoryBadgeHTML(), "it returns no HTML");
|
||||
});
|
||||
|
||||
test("Regular categoryBadge", function (assert) {
|
||||
const store = createStore();
|
||||
const category = store.createRecord("category", {
|
||||
name: "hello",
|
||||
id: 123,
|
||||
description_text: "cool description",
|
||||
color: "ff0",
|
||||
text_color: "f00",
|
||||
});
|
||||
const tag = $.parseHTML(categoryBadgeHTML(category))[0];
|
||||
|
||||
assert.equal(tag.tagName, "A", "it creates a `a` wrapper tag");
|
||||
assert.equal(
|
||||
tag.className.trim(),
|
||||
"badge-wrapper",
|
||||
"it has the correct class"
|
||||
);
|
||||
|
||||
const label = tag.children[1];
|
||||
assert.equal(label.title, "cool description", "it has the correct title");
|
||||
assert.equal(
|
||||
label.children[0].innerText,
|
||||
"hello",
|
||||
"it has the category name"
|
||||
);
|
||||
});
|
||||
|
||||
test("undefined color", function (assert) {
|
||||
const store = createStore();
|
||||
const noColor = store.createRecord("category", { name: "hello", id: 123 });
|
||||
const tag = $.parseHTML(categoryBadgeHTML(noColor))[0];
|
||||
|
||||
assert.blank(
|
||||
tag.attributes["style"],
|
||||
"it has no color style because there are no colors"
|
||||
);
|
||||
});
|
||||
|
||||
test("topic count", function (assert) {
|
||||
const store = createStore();
|
||||
const category = store.createRecord("category", { name: "hello", id: 123 });
|
||||
|
||||
assert.equal(
|
||||
categoryBadgeHTML(category).indexOf("topic-count"),
|
||||
-1,
|
||||
"it does not include topic count by default"
|
||||
);
|
||||
assert.ok(
|
||||
categoryBadgeHTML(category, { topicCount: 20 }).indexOf("topic-count") > 20,
|
||||
"is included when specified"
|
||||
);
|
||||
});
|
||||
|
||||
test("allowUncategorized", function (assert) {
|
||||
const store = createStore();
|
||||
const uncategorized = store.createRecord("category", {
|
||||
name: "uncategorized",
|
||||
id: 345,
|
||||
discourseModule("Unit | Utility | category-badge", function () {
|
||||
test("categoryBadge without a category", function (assert) {
|
||||
assert.blank(categoryBadgeHTML(), "it returns no HTML");
|
||||
});
|
||||
|
||||
sinon
|
||||
.stub(Site, "currentProp")
|
||||
.withArgs("uncategorized_category_id")
|
||||
.returns(345);
|
||||
test("Regular categoryBadge", function (assert) {
|
||||
const store = createStore();
|
||||
const category = store.createRecord("category", {
|
||||
name: "hello",
|
||||
id: 123,
|
||||
description_text: "cool description",
|
||||
color: "ff0",
|
||||
text_color: "f00",
|
||||
});
|
||||
const tag = $.parseHTML(categoryBadgeHTML(category))[0];
|
||||
|
||||
assert.blank(
|
||||
categoryBadgeHTML(uncategorized),
|
||||
"it doesn't return HTML for uncategorized by default"
|
||||
);
|
||||
assert.present(
|
||||
categoryBadgeHTML(uncategorized, { allowUncategorized: true }),
|
||||
"it returns HTML"
|
||||
);
|
||||
});
|
||||
|
||||
test("category names are wrapped in dir-spans", function (assert) {
|
||||
this.siteSettings.support_mixed_text_direction = true;
|
||||
const store = createStore();
|
||||
const rtlCategory = store.createRecord("category", {
|
||||
name: "תכנות עם Ruby",
|
||||
id: 123,
|
||||
description_text: "cool description",
|
||||
color: "ff0",
|
||||
text_color: "f00",
|
||||
});
|
||||
|
||||
const ltrCategory = store.createRecord("category", {
|
||||
name: "Programming in Ruby",
|
||||
id: 234,
|
||||
});
|
||||
|
||||
let tag = $.parseHTML(categoryBadgeHTML(rtlCategory))[0];
|
||||
let dirSpan = tag.children[1].children[0];
|
||||
assert.equal(dirSpan.dir, "rtl");
|
||||
|
||||
tag = $.parseHTML(categoryBadgeHTML(ltrCategory))[0];
|
||||
dirSpan = tag.children[1].children[0];
|
||||
assert.equal(dirSpan.dir, "ltr");
|
||||
});
|
||||
|
||||
test("recursive", function (assert) {
|
||||
const store = createStore();
|
||||
|
||||
const foo = store.createRecord("category", {
|
||||
name: "foo",
|
||||
id: 1,
|
||||
});
|
||||
|
||||
const bar = store.createRecord("category", {
|
||||
name: "bar",
|
||||
id: 2,
|
||||
parent_category_id: foo.id,
|
||||
});
|
||||
|
||||
const baz = store.createRecord("category", {
|
||||
name: "baz",
|
||||
id: 3,
|
||||
parent_category_id: bar.id,
|
||||
});
|
||||
|
||||
this.siteSettings.max_category_nesting = 0;
|
||||
assert.ok(categoryBadgeHTML(baz, { recursive: true }).indexOf("baz") !== -1);
|
||||
assert.ok(categoryBadgeHTML(baz, { recursive: true }).indexOf("bar") === -1);
|
||||
|
||||
this.siteSettings.max_category_nesting = 1;
|
||||
assert.ok(categoryBadgeHTML(baz, { recursive: true }).indexOf("baz") !== -1);
|
||||
assert.ok(categoryBadgeHTML(baz, { recursive: true }).indexOf("bar") === -1);
|
||||
|
||||
this.siteSettings.max_category_nesting = 2;
|
||||
assert.ok(categoryBadgeHTML(baz, { recursive: true }).indexOf("baz") !== -1);
|
||||
assert.ok(categoryBadgeHTML(baz, { recursive: true }).indexOf("bar") !== -1);
|
||||
assert.ok(categoryBadgeHTML(baz, { recursive: true }).indexOf("foo") === -1);
|
||||
|
||||
this.siteSettings.max_category_nesting = 3;
|
||||
assert.ok(categoryBadgeHTML(baz, { recursive: true }).indexOf("baz") !== -1);
|
||||
assert.ok(categoryBadgeHTML(baz, { recursive: true }).indexOf("bar") !== -1);
|
||||
assert.ok(categoryBadgeHTML(baz, { recursive: true }).indexOf("foo") !== -1);
|
||||
assert.equal(tag.tagName, "A", "it creates a `a` wrapper tag");
|
||||
assert.equal(
|
||||
tag.className.trim(),
|
||||
"badge-wrapper",
|
||||
"it has the correct class"
|
||||
);
|
||||
|
||||
const label = tag.children[1];
|
||||
assert.equal(label.title, "cool description", "it has the correct title");
|
||||
assert.equal(
|
||||
label.children[0].innerText,
|
||||
"hello",
|
||||
"it has the category name"
|
||||
);
|
||||
});
|
||||
|
||||
test("undefined color", function (assert) {
|
||||
const store = createStore();
|
||||
const noColor = store.createRecord("category", { name: "hello", id: 123 });
|
||||
const tag = $.parseHTML(categoryBadgeHTML(noColor))[0];
|
||||
|
||||
assert.blank(
|
||||
tag.attributes["style"],
|
||||
"it has no color style because there are no colors"
|
||||
);
|
||||
});
|
||||
|
||||
test("topic count", function (assert) {
|
||||
const store = createStore();
|
||||
const category = store.createRecord("category", { name: "hello", id: 123 });
|
||||
|
||||
assert.equal(
|
||||
categoryBadgeHTML(category).indexOf("topic-count"),
|
||||
-1,
|
||||
"it does not include topic count by default"
|
||||
);
|
||||
assert.ok(
|
||||
categoryBadgeHTML(category, { topicCount: 20 }).indexOf("topic-count") >
|
||||
20,
|
||||
"is included when specified"
|
||||
);
|
||||
});
|
||||
|
||||
test("allowUncategorized", function (assert) {
|
||||
const store = createStore();
|
||||
const uncategorized = store.createRecord("category", {
|
||||
name: "uncategorized",
|
||||
id: 345,
|
||||
});
|
||||
|
||||
sinon
|
||||
.stub(Site, "currentProp")
|
||||
.withArgs("uncategorized_category_id")
|
||||
.returns(345);
|
||||
|
||||
assert.blank(
|
||||
categoryBadgeHTML(uncategorized),
|
||||
"it doesn't return HTML for uncategorized by default"
|
||||
);
|
||||
assert.present(
|
||||
categoryBadgeHTML(uncategorized, { allowUncategorized: true }),
|
||||
"it returns HTML"
|
||||
);
|
||||
});
|
||||
|
||||
test("category names are wrapped in dir-spans", function (assert) {
|
||||
this.siteSettings.support_mixed_text_direction = true;
|
||||
const store = createStore();
|
||||
const rtlCategory = store.createRecord("category", {
|
||||
name: "תכנות עם Ruby",
|
||||
id: 123,
|
||||
description_text: "cool description",
|
||||
color: "ff0",
|
||||
text_color: "f00",
|
||||
});
|
||||
|
||||
const ltrCategory = store.createRecord("category", {
|
||||
name: "Programming in Ruby",
|
||||
id: 234,
|
||||
});
|
||||
|
||||
let tag = $.parseHTML(categoryBadgeHTML(rtlCategory))[0];
|
||||
let dirSpan = tag.children[1].children[0];
|
||||
assert.equal(dirSpan.dir, "rtl");
|
||||
|
||||
tag = $.parseHTML(categoryBadgeHTML(ltrCategory))[0];
|
||||
dirSpan = tag.children[1].children[0];
|
||||
assert.equal(dirSpan.dir, "ltr");
|
||||
});
|
||||
|
||||
test("recursive", function (assert) {
|
||||
const store = createStore();
|
||||
|
||||
const foo = store.createRecord("category", {
|
||||
name: "foo",
|
||||
id: 1,
|
||||
});
|
||||
|
||||
const bar = store.createRecord("category", {
|
||||
name: "bar",
|
||||
id: 2,
|
||||
parent_category_id: foo.id,
|
||||
});
|
||||
|
||||
const baz = store.createRecord("category", {
|
||||
name: "baz",
|
||||
id: 3,
|
||||
parent_category_id: bar.id,
|
||||
});
|
||||
|
||||
this.siteSettings.max_category_nesting = 0;
|
||||
assert.ok(
|
||||
categoryBadgeHTML(baz, { recursive: true }).indexOf("baz") !== -1
|
||||
);
|
||||
assert.ok(
|
||||
categoryBadgeHTML(baz, { recursive: true }).indexOf("bar") === -1
|
||||
);
|
||||
|
||||
this.siteSettings.max_category_nesting = 1;
|
||||
assert.ok(
|
||||
categoryBadgeHTML(baz, { recursive: true }).indexOf("baz") !== -1
|
||||
);
|
||||
assert.ok(
|
||||
categoryBadgeHTML(baz, { recursive: true }).indexOf("bar") === -1
|
||||
);
|
||||
|
||||
this.siteSettings.max_category_nesting = 2;
|
||||
assert.ok(
|
||||
categoryBadgeHTML(baz, { recursive: true }).indexOf("baz") !== -1
|
||||
);
|
||||
assert.ok(
|
||||
categoryBadgeHTML(baz, { recursive: true }).indexOf("bar") !== -1
|
||||
);
|
||||
assert.ok(
|
||||
categoryBadgeHTML(baz, { recursive: true }).indexOf("foo") === -1
|
||||
);
|
||||
|
||||
this.siteSettings.max_category_nesting = 3;
|
||||
assert.ok(
|
||||
categoryBadgeHTML(baz, { recursive: true }).indexOf("baz") !== -1
|
||||
);
|
||||
assert.ok(
|
||||
categoryBadgeHTML(baz, { recursive: true }).indexOf("bar") !== -1
|
||||
);
|
||||
assert.ok(
|
||||
categoryBadgeHTML(baz, { recursive: true }).indexOf("foo") !== -1
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,8 +6,14 @@ import { fixture, logIn } from "discourse/tests/helpers/qunit-helpers";
|
||||
import User from "discourse/models/user";
|
||||
import pretender from "discourse/tests/helpers/create-pretender";
|
||||
|
||||
module("lib:click-track-edit-history", {
|
||||
beforeEach() {
|
||||
const track = ClickTrack.trackClick;
|
||||
|
||||
function generateClickEventOn(selector) {
|
||||
return $.Event("click", { currentTarget: fixture(selector).first() });
|
||||
}
|
||||
|
||||
module("Unit | Utility | click-track-edit-history", function (hooks) {
|
||||
hooks.beforeEach(function () {
|
||||
logIn();
|
||||
|
||||
let win = { focus: function () {} };
|
||||
@@ -51,59 +57,53 @@ module("lib:click-track-edit-history", {
|
||||
</div>
|
||||
</div>`
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
var track = ClickTrack.trackClick;
|
||||
|
||||
function generateClickEventOn(selector) {
|
||||
return $.Event("click", { currentTarget: fixture(selector).first() });
|
||||
}
|
||||
|
||||
skip("tracks internal URLs", async function (assert) {
|
||||
assert.expect(2);
|
||||
sinon.stub(DiscourseURL, "origin").returns("http://discuss.domain.com");
|
||||
|
||||
const done = assert.async();
|
||||
pretender.post("/clicks/track", (request) => {
|
||||
assert.equal(
|
||||
request.requestBody,
|
||||
"url=http%3A%2F%2Fdiscuss.domain.com&post_id=42&topic_id=1337"
|
||||
);
|
||||
done();
|
||||
});
|
||||
|
||||
assert.notOk(track(generateClickEventOn("#same-site")));
|
||||
});
|
||||
skip("tracks internal URLs", async function (assert) {
|
||||
assert.expect(2);
|
||||
sinon.stub(DiscourseURL, "origin").returns("http://discuss.domain.com");
|
||||
|
||||
skip("tracks external URLs", async function (assert) {
|
||||
assert.expect(2);
|
||||
const done = assert.async();
|
||||
pretender.post("/clicks/track", (request) => {
|
||||
assert.equal(
|
||||
request.requestBody,
|
||||
"url=http%3A%2F%2Fdiscuss.domain.com&post_id=42&topic_id=1337"
|
||||
);
|
||||
done();
|
||||
});
|
||||
|
||||
const done = assert.async();
|
||||
pretender.post("/clicks/track", (request) => {
|
||||
assert.equal(
|
||||
request.requestBody,
|
||||
"url=http%3A%2F%2Fwww.google.com&post_id=42&topic_id=1337"
|
||||
);
|
||||
done();
|
||||
assert.notOk(track(generateClickEventOn("#same-site")));
|
||||
});
|
||||
|
||||
assert.notOk(track(generateClickEventOn("a")));
|
||||
});
|
||||
skip("tracks external URLs", async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
skip("tracks external URLs when opening in another window", async function (assert) {
|
||||
assert.expect(3);
|
||||
User.currentProp("external_links_in_new_tab", true);
|
||||
const done = assert.async();
|
||||
pretender.post("/clicks/track", (request) => {
|
||||
assert.equal(
|
||||
request.requestBody,
|
||||
"url=http%3A%2F%2Fwww.google.com&post_id=42&topic_id=1337"
|
||||
);
|
||||
done();
|
||||
});
|
||||
|
||||
const done = assert.async();
|
||||
pretender.post("/clicks/track", (request) => {
|
||||
assert.equal(
|
||||
request.requestBody,
|
||||
"url=http%3A%2F%2Fwww.google.com&post_id=42&topic_id=1337"
|
||||
);
|
||||
done();
|
||||
assert.notOk(track(generateClickEventOn("a")));
|
||||
});
|
||||
|
||||
assert.notOk(track(generateClickEventOn("a")));
|
||||
assert.ok(window.open.calledWith("http://www.google.com", "_blank"));
|
||||
skip("tracks external URLs when opening in another window", async function (assert) {
|
||||
assert.expect(3);
|
||||
User.currentProp("external_links_in_new_tab", true);
|
||||
|
||||
const done = assert.async();
|
||||
pretender.post("/clicks/track", (request) => {
|
||||
assert.equal(
|
||||
request.requestBody,
|
||||
"url=http%3A%2F%2Fwww.google.com&post_id=42&topic_id=1337"
|
||||
);
|
||||
done();
|
||||
});
|
||||
|
||||
assert.notOk(track(generateClickEventOn("a")));
|
||||
assert.ok(window.open.calledWith("http://www.google.com", "_blank"));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,8 +5,14 @@ import ClickTrack from "discourse/lib/click-track";
|
||||
import { fixture, logIn } from "discourse/tests/helpers/qunit-helpers";
|
||||
import pretender from "discourse/tests/helpers/create-pretender";
|
||||
|
||||
module("lib:click-track-profile-page", {
|
||||
beforeEach() {
|
||||
const track = ClickTrack.trackClick;
|
||||
|
||||
function generateClickEventOn(selector) {
|
||||
return $.Event("click", { currentTarget: fixture(selector).first() });
|
||||
}
|
||||
|
||||
module("Unit | Utility | click-track-profile-page", function (hooks) {
|
||||
hooks.beforeEach(function () {
|
||||
logIn();
|
||||
|
||||
let win = { focus: function () {} };
|
||||
@@ -44,54 +50,48 @@ module("lib:click-track-profile-page", {
|
||||
<a class="hashtag" href="http://discuss.domain.com">#hashtag</a>
|
||||
</p>`
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
var track = ClickTrack.trackClick;
|
||||
|
||||
function generateClickEventOn(selector) {
|
||||
return $.Event("click", { currentTarget: fixture(selector).first() });
|
||||
}
|
||||
|
||||
skip("tracks internal URLs", async function (assert) {
|
||||
assert.expect(2);
|
||||
sinon.stub(DiscourseURL, "origin").returns("http://discuss.domain.com");
|
||||
|
||||
const done = assert.async();
|
||||
pretender.post("/clicks/track", (request) => {
|
||||
assert.equal(request.requestBody, "url=http%3A%2F%2Fdiscuss.domain.com");
|
||||
done();
|
||||
});
|
||||
|
||||
assert.notOk(track(generateClickEventOn("#same-site")));
|
||||
});
|
||||
skip("tracks internal URLs", async function (assert) {
|
||||
assert.expect(2);
|
||||
sinon.stub(DiscourseURL, "origin").returns("http://discuss.domain.com");
|
||||
|
||||
skip("tracks external URLs", async function (assert) {
|
||||
assert.expect(2);
|
||||
const done = assert.async();
|
||||
pretender.post("/clicks/track", (request) => {
|
||||
assert.equal(request.requestBody, "url=http%3A%2F%2Fdiscuss.domain.com");
|
||||
done();
|
||||
});
|
||||
|
||||
const done = assert.async();
|
||||
pretender.post("/clicks/track", (request) => {
|
||||
assert.equal(
|
||||
request.requestBody,
|
||||
"url=http%3A%2F%2Fwww.google.com&post_id=42&topic_id=1337"
|
||||
);
|
||||
done();
|
||||
assert.notOk(track(generateClickEventOn("#same-site")));
|
||||
});
|
||||
|
||||
assert.notOk(track(generateClickEventOn("a")));
|
||||
});
|
||||
skip("tracks external URLs", async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
skip("tracks external URLs in other posts", async function (assert) {
|
||||
assert.expect(2);
|
||||
const done = assert.async();
|
||||
pretender.post("/clicks/track", (request) => {
|
||||
assert.equal(
|
||||
request.requestBody,
|
||||
"url=http%3A%2F%2Fwww.google.com&post_id=42&topic_id=1337"
|
||||
);
|
||||
done();
|
||||
});
|
||||
|
||||
const done = assert.async();
|
||||
pretender.post("/clicks/track", (request) => {
|
||||
assert.equal(
|
||||
request.requestBody,
|
||||
"url=http%3A%2F%2Fwww.google.com&post_id=24&topic_id=7331"
|
||||
);
|
||||
done();
|
||||
assert.notOk(track(generateClickEventOn("a")));
|
||||
});
|
||||
|
||||
assert.notOk(track(generateClickEventOn(".second a")));
|
||||
skip("tracks external URLs in other posts", async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const done = assert.async();
|
||||
pretender.post("/clicks/track", (request) => {
|
||||
assert.equal(
|
||||
request.requestBody,
|
||||
"url=http%3A%2F%2Fwww.google.com&post_id=24&topic_id=7331"
|
||||
);
|
||||
done();
|
||||
});
|
||||
|
||||
assert.notOk(track(generateClickEventOn(".second a")));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -8,8 +8,14 @@ import { fixture, logIn } from "discourse/tests/helpers/qunit-helpers";
|
||||
import User from "discourse/models/user";
|
||||
import pretender from "discourse/tests/helpers/create-pretender";
|
||||
|
||||
module("lib:click-track", {
|
||||
beforeEach() {
|
||||
const track = ClickTrack.trackClick;
|
||||
|
||||
function generateClickEventOn(selector) {
|
||||
return $.Event("click", { currentTarget: fixture(selector).first() });
|
||||
}
|
||||
|
||||
module("Unit | Utility | click-track", function (hooks) {
|
||||
hooks.beforeEach(function () {
|
||||
logIn();
|
||||
|
||||
let win = { focus: function () {} };
|
||||
@@ -45,182 +51,185 @@ module("lib:click-track", {
|
||||
</article>
|
||||
</div>`
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
var track = ClickTrack.trackClick;
|
||||
|
||||
function generateClickEventOn(selector) {
|
||||
return $.Event("click", { currentTarget: fixture(selector).first() });
|
||||
}
|
||||
|
||||
skip("tracks internal URLs", async function (assert) {
|
||||
assert.expect(2);
|
||||
sinon.stub(DiscourseURL, "origin").returns("http://discuss.domain.com");
|
||||
|
||||
const done = assert.async();
|
||||
pretender.post("/clicks/track", (request) => {
|
||||
assert.ok(
|
||||
request.requestBody,
|
||||
"url=http%3A%2F%2Fdiscuss.domain.com&post_id=42&topic_id=1337"
|
||||
);
|
||||
done();
|
||||
});
|
||||
|
||||
assert.notOk(track(generateClickEventOn("#same-site")));
|
||||
});
|
||||
skip("tracks internal URLs", async function (assert) {
|
||||
assert.expect(2);
|
||||
sinon.stub(DiscourseURL, "origin").returns("http://discuss.domain.com");
|
||||
|
||||
test("does not track elements with no href", async function (assert) {
|
||||
assert.ok(track(generateClickEventOn(".a-without-href")));
|
||||
});
|
||||
const done = assert.async();
|
||||
pretender.post("/clicks/track", (request) => {
|
||||
assert.ok(
|
||||
request.requestBody,
|
||||
"url=http%3A%2F%2Fdiscuss.domain.com&post_id=42&topic_id=1337"
|
||||
);
|
||||
done();
|
||||
});
|
||||
|
||||
test("does not track attachments", async function (assert) {
|
||||
sinon.stub(DiscourseURL, "origin").returns("http://discuss.domain.com");
|
||||
assert.notOk(track(generateClickEventOn("#same-site")));
|
||||
});
|
||||
|
||||
pretender.post("/clicks/track", () => assert.ok(false));
|
||||
test("does not track elements with no href", async function (assert) {
|
||||
assert.ok(track(generateClickEventOn(".a-without-href")));
|
||||
});
|
||||
|
||||
assert.notOk(track(generateClickEventOn(".attachment")));
|
||||
assert.ok(
|
||||
DiscourseURL.redirectTo.calledWith(
|
||||
"http://discuss.domain.com/uploads/default/1234/1532357280.txt"
|
||||
)
|
||||
test("does not track attachments", async function (assert) {
|
||||
sinon.stub(DiscourseURL, "origin").returns("http://discuss.domain.com");
|
||||
|
||||
pretender.post("/clicks/track", () => assert.ok(false));
|
||||
|
||||
assert.notOk(track(generateClickEventOn(".attachment")));
|
||||
assert.ok(
|
||||
DiscourseURL.redirectTo.calledWith(
|
||||
"http://discuss.domain.com/uploads/default/1234/1532357280.txt"
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
skip("tracks external URLs", async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const done = assert.async();
|
||||
pretender.post("/clicks/track", (request) => {
|
||||
assert.ok(
|
||||
request.requestBody,
|
||||
"url=http%3A%2F%2Fwww.google.com&post_id=42&topic_id=1337"
|
||||
);
|
||||
done();
|
||||
});
|
||||
|
||||
assert.notOk(track(generateClickEventOn("a")));
|
||||
});
|
||||
|
||||
skip("tracks external URLs when opening in another window", async function (assert) {
|
||||
assert.expect(3);
|
||||
User.currentProp("external_links_in_new_tab", true);
|
||||
|
||||
const done = assert.async();
|
||||
pretender.post("/clicks/track", (request) => {
|
||||
assert.ok(
|
||||
request.requestBody,
|
||||
"url=http%3A%2F%2Fwww.google.com&post_id=42&topic_id=1337"
|
||||
);
|
||||
done();
|
||||
});
|
||||
|
||||
assert.notOk(track(generateClickEventOn("a")));
|
||||
assert.ok(window.open.calledWith("http://www.google.com", "_blank"));
|
||||
});
|
||||
|
||||
test("does not track clicks on lightboxes", async function (assert) {
|
||||
assert.notOk(track(generateClickEventOn(".lightbox")));
|
||||
});
|
||||
|
||||
test("does not track clicks when forcibly disabled", async function (assert) {
|
||||
assert.notOk(track(generateClickEventOn(".no-track-link")));
|
||||
});
|
||||
|
||||
test("does not track clicks on back buttons", async function (assert) {
|
||||
assert.notOk(track(generateClickEventOn(".back")));
|
||||
});
|
||||
|
||||
test("does not track right clicks inside quotes", async function (assert) {
|
||||
const event = generateClickEventOn(".quote a:first-child");
|
||||
event.which = 3;
|
||||
assert.ok(track(event));
|
||||
});
|
||||
|
||||
test("does not track clicks links in quotes", async function (assert) {
|
||||
User.currentProp("external_links_in_new_tab", true);
|
||||
assert.notOk(track(generateClickEventOn(".quote a:last-child")));
|
||||
assert.ok(window.open.calledWith("https://google.com/", "_blank"));
|
||||
});
|
||||
|
||||
test("does not track clicks on category badges", async function (assert) {
|
||||
assert.notOk(track(generateClickEventOn(".hashtag")));
|
||||
});
|
||||
|
||||
test("does not track clicks on mailto", async function (assert) {
|
||||
assert.ok(track(generateClickEventOn(".mailto")));
|
||||
});
|
||||
|
||||
test("removes the href and put it as a data attribute", async function (assert) {
|
||||
User.currentProp("external_links_in_new_tab", true);
|
||||
|
||||
assert.notOk(track(generateClickEventOn("a")));
|
||||
|
||||
var $link = fixture("a").first();
|
||||
assert.ok($link.hasClass("no-href"));
|
||||
assert.equal($link.data("href"), "http://www.google.com/");
|
||||
assert.blank($link.attr("href"));
|
||||
assert.ok($link.data("auto-route"));
|
||||
assert.ok(window.open.calledWith("http://www.google.com/", "_blank"));
|
||||
});
|
||||
|
||||
test("restores the href after a while", async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
assert.notOk(track(generateClickEventOn("a")));
|
||||
|
||||
assert.timeout(75);
|
||||
|
||||
const done = assert.async();
|
||||
later(() => {
|
||||
assert.equal(fixture("a").attr("href"), "http://www.google.com");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
function badgeClickCount(assert, id, expected) {
|
||||
track(generateClickEventOn("#" + id));
|
||||
var $badge = $("span.badge", fixture("#" + id).first());
|
||||
assert.equal(parseInt($badge.html(), 10), expected);
|
||||
}
|
||||
|
||||
test("does not update badge clicks on my own link", async function (assert) {
|
||||
sinon.stub(User, "currentProp").withArgs("id").returns(314);
|
||||
badgeClickCount(assert, "with-badge", 1);
|
||||
});
|
||||
|
||||
test("does not update badge clicks in my own post", async function (assert) {
|
||||
sinon.stub(User, "currentProp").withArgs("id").returns(3141);
|
||||
badgeClickCount(assert, "with-badge-but-not-mine", 1);
|
||||
});
|
||||
|
||||
test("updates badge counts correctly", async function (assert) {
|
||||
badgeClickCount(assert, "inside-onebox", 1);
|
||||
badgeClickCount(assert, "inside-onebox-forced", 2);
|
||||
badgeClickCount(assert, "with-badge", 2);
|
||||
});
|
||||
|
||||
function testOpenInANewTab(description, clickEventModifier) {
|
||||
test(description, async function (assert) {
|
||||
var clickEvent = generateClickEventOn("a");
|
||||
clickEventModifier(clickEvent);
|
||||
assert.ok(track(clickEvent));
|
||||
assert.notOk(clickEvent.defaultPrevented);
|
||||
});
|
||||
}
|
||||
|
||||
testOpenInANewTab(
|
||||
"it opens in a new tab when pressing shift",
|
||||
(clickEvent) => {
|
||||
clickEvent.shiftKey = true;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
skip("tracks external URLs", async function (assert) {
|
||||
assert.expect(2);
|
||||
testOpenInANewTab(
|
||||
"it opens in a new tab when pressing meta",
|
||||
(clickEvent) => {
|
||||
clickEvent.metaKey = true;
|
||||
}
|
||||
);
|
||||
|
||||
const done = assert.async();
|
||||
pretender.post("/clicks/track", (request) => {
|
||||
assert.ok(
|
||||
request.requestBody,
|
||||
"url=http%3A%2F%2Fwww.google.com&post_id=42&topic_id=1337"
|
||||
);
|
||||
done();
|
||||
});
|
||||
testOpenInANewTab(
|
||||
"it opens in a new tab when pressing ctrl",
|
||||
(clickEvent) => {
|
||||
clickEvent.ctrlKey = true;
|
||||
}
|
||||
);
|
||||
|
||||
assert.notOk(track(generateClickEventOn("a")));
|
||||
});
|
||||
|
||||
skip("tracks external URLs when opening in another window", async function (assert) {
|
||||
assert.expect(3);
|
||||
User.currentProp("external_links_in_new_tab", true);
|
||||
|
||||
const done = assert.async();
|
||||
pretender.post("/clicks/track", (request) => {
|
||||
assert.ok(
|
||||
request.requestBody,
|
||||
"url=http%3A%2F%2Fwww.google.com&post_id=42&topic_id=1337"
|
||||
);
|
||||
done();
|
||||
});
|
||||
|
||||
assert.notOk(track(generateClickEventOn("a")));
|
||||
assert.ok(window.open.calledWith("http://www.google.com", "_blank"));
|
||||
});
|
||||
|
||||
test("does not track clicks on lightboxes", async function (assert) {
|
||||
assert.notOk(track(generateClickEventOn(".lightbox")));
|
||||
});
|
||||
|
||||
test("does not track clicks when forcibly disabled", async function (assert) {
|
||||
assert.notOk(track(generateClickEventOn(".no-track-link")));
|
||||
});
|
||||
|
||||
test("does not track clicks on back buttons", async function (assert) {
|
||||
assert.notOk(track(generateClickEventOn(".back")));
|
||||
});
|
||||
|
||||
test("does not track right clicks inside quotes", async function (assert) {
|
||||
const event = generateClickEventOn(".quote a:first-child");
|
||||
event.which = 3;
|
||||
assert.ok(track(event));
|
||||
});
|
||||
|
||||
test("does not track clicks links in quotes", async function (assert) {
|
||||
User.currentProp("external_links_in_new_tab", true);
|
||||
assert.notOk(track(generateClickEventOn(".quote a:last-child")));
|
||||
assert.ok(window.open.calledWith("https://google.com/", "_blank"));
|
||||
});
|
||||
|
||||
test("does not track clicks on category badges", async function (assert) {
|
||||
assert.notOk(track(generateClickEventOn(".hashtag")));
|
||||
});
|
||||
|
||||
test("does not track clicks on mailto", async function (assert) {
|
||||
assert.ok(track(generateClickEventOn(".mailto")));
|
||||
});
|
||||
|
||||
test("removes the href and put it as a data attribute", async function (assert) {
|
||||
User.currentProp("external_links_in_new_tab", true);
|
||||
|
||||
assert.notOk(track(generateClickEventOn("a")));
|
||||
|
||||
var $link = fixture("a").first();
|
||||
assert.ok($link.hasClass("no-href"));
|
||||
assert.equal($link.data("href"), "http://www.google.com/");
|
||||
assert.blank($link.attr("href"));
|
||||
assert.ok($link.data("auto-route"));
|
||||
assert.ok(window.open.calledWith("http://www.google.com/", "_blank"));
|
||||
});
|
||||
|
||||
test("restores the href after a while", async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
assert.notOk(track(generateClickEventOn("a")));
|
||||
|
||||
assert.timeout(75);
|
||||
|
||||
const done = assert.async();
|
||||
later(() => {
|
||||
assert.equal(fixture("a").attr("href"), "http://www.google.com");
|
||||
done();
|
||||
testOpenInANewTab("it opens in a new tab on middle click", (clickEvent) => {
|
||||
clickEvent.button = 2;
|
||||
});
|
||||
});
|
||||
|
||||
function badgeClickCount(assert, id, expected) {
|
||||
track(generateClickEventOn("#" + id));
|
||||
var $badge = $("span.badge", fixture("#" + id).first());
|
||||
assert.equal(parseInt($badge.html(), 10), expected);
|
||||
}
|
||||
|
||||
test("does not update badge clicks on my own link", async function (assert) {
|
||||
sinon.stub(User, "currentProp").withArgs("id").returns(314);
|
||||
badgeClickCount(assert, "with-badge", 1);
|
||||
});
|
||||
|
||||
test("does not update badge clicks in my own post", async function (assert) {
|
||||
sinon.stub(User, "currentProp").withArgs("id").returns(3141);
|
||||
badgeClickCount(assert, "with-badge-but-not-mine", 1);
|
||||
});
|
||||
|
||||
test("updates badge counts correctly", async function (assert) {
|
||||
badgeClickCount(assert, "inside-onebox", 1);
|
||||
badgeClickCount(assert, "inside-onebox-forced", 2);
|
||||
badgeClickCount(assert, "with-badge", 2);
|
||||
});
|
||||
|
||||
function testOpenInANewTab(description, clickEventModifier) {
|
||||
test(description, async function (assert) {
|
||||
var clickEvent = generateClickEventOn("a");
|
||||
clickEventModifier(clickEvent);
|
||||
assert.ok(track(clickEvent));
|
||||
assert.notOk(clickEvent.defaultPrevented);
|
||||
});
|
||||
}
|
||||
|
||||
testOpenInANewTab("it opens in a new tab when pressing shift", (clickEvent) => {
|
||||
clickEvent.shiftKey = true;
|
||||
});
|
||||
|
||||
testOpenInANewTab("it opens in a new tab when pressing meta", (clickEvent) => {
|
||||
clickEvent.metaKey = true;
|
||||
});
|
||||
|
||||
testOpenInANewTab("it opens in a new tab when pressing ctrl", (clickEvent) => {
|
||||
clickEvent.ctrlKey = true;
|
||||
});
|
||||
|
||||
testOpenInANewTab("it opens in a new tab on middle click", (clickEvent) => {
|
||||
clickEvent.button = 2;
|
||||
});
|
||||
|
||||
@@ -14,159 +14,159 @@ import {
|
||||
import { setPrefix } from "discourse-common/lib/get-url";
|
||||
import { discourseModule } from "discourse/tests/helpers/qunit-helpers";
|
||||
|
||||
discourseModule("lib:computed", {
|
||||
beforeEach() {
|
||||
discourseModule("Unit | Utility | computed", function (hooks) {
|
||||
hooks.beforeEach(function () {
|
||||
sinon.stub(I18n, "t").callsFake(function (scope) {
|
||||
return "%@ translated: " + scope;
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
afterEach() {
|
||||
hooks.afterEach(function () {
|
||||
I18n.t.restore();
|
||||
},
|
||||
});
|
||||
|
||||
test("setting", function (assert) {
|
||||
let t = EmberObject.extend({
|
||||
siteSettings: this.siteSettings,
|
||||
vehicle: setting("vehicle"),
|
||||
missingProp: setting("madeUpThing"),
|
||||
}).create();
|
||||
|
||||
this.siteSettings.vehicle = "airplane";
|
||||
assert.equal(
|
||||
t.get("vehicle"),
|
||||
"airplane",
|
||||
"it has the value of the site setting"
|
||||
);
|
||||
assert.ok(
|
||||
!t.get("missingProp"),
|
||||
"it is falsy when the site setting is not defined"
|
||||
);
|
||||
});
|
||||
|
||||
test("propertyEqual", function (assert) {
|
||||
var t = EmberObject.extend({
|
||||
same: propertyEqual("cookies", "biscuits"),
|
||||
}).create({
|
||||
cookies: 10,
|
||||
biscuits: 10,
|
||||
});
|
||||
|
||||
assert.ok(t.get("same"), "it is true when the properties are the same");
|
||||
t.set("biscuits", 9);
|
||||
assert.ok(!t.get("same"), "it isn't true when one property is different");
|
||||
});
|
||||
test("setting", function (assert) {
|
||||
let t = EmberObject.extend({
|
||||
siteSettings: this.siteSettings,
|
||||
vehicle: setting("vehicle"),
|
||||
missingProp: setting("madeUpThing"),
|
||||
}).create();
|
||||
|
||||
test("propertyNotEqual", function (assert) {
|
||||
var t = EmberObject.extend({
|
||||
diff: propertyNotEqual("cookies", "biscuits"),
|
||||
}).create({
|
||||
cookies: 10,
|
||||
biscuits: 10,
|
||||
this.siteSettings.vehicle = "airplane";
|
||||
assert.equal(
|
||||
t.get("vehicle"),
|
||||
"airplane",
|
||||
"it has the value of the site setting"
|
||||
);
|
||||
assert.ok(
|
||||
!t.get("missingProp"),
|
||||
"it is falsy when the site setting is not defined"
|
||||
);
|
||||
});
|
||||
|
||||
assert.ok(!t.get("diff"), "it isn't true when the properties are the same");
|
||||
t.set("biscuits", 9);
|
||||
assert.ok(t.get("diff"), "it is true when one property is different");
|
||||
});
|
||||
test("propertyEqual", function (assert) {
|
||||
var t = EmberObject.extend({
|
||||
same: propertyEqual("cookies", "biscuits"),
|
||||
}).create({
|
||||
cookies: 10,
|
||||
biscuits: 10,
|
||||
});
|
||||
|
||||
test("fmt", function (assert) {
|
||||
var t = EmberObject.extend({
|
||||
exclaimyUsername: fmt("username", "!!! %@ !!!"),
|
||||
multiple: fmt("username", "mood", "%@ is %@"),
|
||||
}).create({
|
||||
username: "eviltrout",
|
||||
mood: "happy",
|
||||
assert.ok(t.get("same"), "it is true when the properties are the same");
|
||||
t.set("biscuits", 9);
|
||||
assert.ok(!t.get("same"), "it isn't true when one property is different");
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
t.get("exclaimyUsername"),
|
||||
"!!! eviltrout !!!",
|
||||
"it inserts the string"
|
||||
);
|
||||
assert.equal(
|
||||
t.get("multiple"),
|
||||
"eviltrout is happy",
|
||||
"it inserts multiple strings"
|
||||
);
|
||||
test("propertyNotEqual", function (assert) {
|
||||
var t = EmberObject.extend({
|
||||
diff: propertyNotEqual("cookies", "biscuits"),
|
||||
}).create({
|
||||
cookies: 10,
|
||||
biscuits: 10,
|
||||
});
|
||||
|
||||
t.set("username", "codinghorror");
|
||||
assert.equal(
|
||||
t.get("multiple"),
|
||||
"codinghorror is happy",
|
||||
"it supports changing properties"
|
||||
);
|
||||
t.set("mood", "ecstatic");
|
||||
assert.equal(
|
||||
t.get("multiple"),
|
||||
"codinghorror is ecstatic",
|
||||
"it supports changing another property"
|
||||
);
|
||||
});
|
||||
|
||||
test("i18n", function (assert) {
|
||||
var t = EmberObject.extend({
|
||||
exclaimyUsername: i18n("username", "!!! %@ !!!"),
|
||||
multiple: i18n("username", "mood", "%@ is %@"),
|
||||
}).create({
|
||||
username: "eviltrout",
|
||||
mood: "happy",
|
||||
assert.ok(!t.get("diff"), "it isn't true when the properties are the same");
|
||||
t.set("biscuits", 9);
|
||||
assert.ok(t.get("diff"), "it is true when one property is different");
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
t.get("exclaimyUsername"),
|
||||
"%@ translated: !!! eviltrout !!!",
|
||||
"it inserts the string and then translates"
|
||||
);
|
||||
assert.equal(
|
||||
t.get("multiple"),
|
||||
"%@ translated: eviltrout is happy",
|
||||
"it inserts multiple strings and then translates"
|
||||
);
|
||||
test("fmt", function (assert) {
|
||||
var t = EmberObject.extend({
|
||||
exclaimyUsername: fmt("username", "!!! %@ !!!"),
|
||||
multiple: fmt("username", "mood", "%@ is %@"),
|
||||
}).create({
|
||||
username: "eviltrout",
|
||||
mood: "happy",
|
||||
});
|
||||
|
||||
t.set("username", "codinghorror");
|
||||
assert.equal(
|
||||
t.get("multiple"),
|
||||
"%@ translated: codinghorror is happy",
|
||||
"it supports changing properties"
|
||||
);
|
||||
t.set("mood", "ecstatic");
|
||||
assert.equal(
|
||||
t.get("multiple"),
|
||||
"%@ translated: codinghorror is ecstatic",
|
||||
"it supports changing another property"
|
||||
);
|
||||
});
|
||||
assert.equal(
|
||||
t.get("exclaimyUsername"),
|
||||
"!!! eviltrout !!!",
|
||||
"it inserts the string"
|
||||
);
|
||||
assert.equal(
|
||||
t.get("multiple"),
|
||||
"eviltrout is happy",
|
||||
"it inserts multiple strings"
|
||||
);
|
||||
|
||||
test("url", function (assert) {
|
||||
var t, testClass;
|
||||
|
||||
testClass = EmberObject.extend({
|
||||
userUrl: url("username", "/u/%@"),
|
||||
t.set("username", "codinghorror");
|
||||
assert.equal(
|
||||
t.get("multiple"),
|
||||
"codinghorror is happy",
|
||||
"it supports changing properties"
|
||||
);
|
||||
t.set("mood", "ecstatic");
|
||||
assert.equal(
|
||||
t.get("multiple"),
|
||||
"codinghorror is ecstatic",
|
||||
"it supports changing another property"
|
||||
);
|
||||
});
|
||||
|
||||
t = testClass.create({ username: "eviltrout" });
|
||||
assert.equal(
|
||||
t.get("userUrl"),
|
||||
"/u/eviltrout",
|
||||
"it supports urls without a prefix"
|
||||
);
|
||||
test("i18n", function (assert) {
|
||||
var t = EmberObject.extend({
|
||||
exclaimyUsername: i18n("username", "!!! %@ !!!"),
|
||||
multiple: i18n("username", "mood", "%@ is %@"),
|
||||
}).create({
|
||||
username: "eviltrout",
|
||||
mood: "happy",
|
||||
});
|
||||
|
||||
setPrefix("/prefixed");
|
||||
t = testClass.create({ username: "eviltrout" });
|
||||
assert.equal(
|
||||
t.get("userUrl"),
|
||||
"/prefixed/u/eviltrout",
|
||||
"it supports urls with a prefix"
|
||||
);
|
||||
});
|
||||
|
||||
test("htmlSafe", function (assert) {
|
||||
const cookies = "<p>cookies and <b>biscuits</b></p>";
|
||||
const t = EmberObject.extend({
|
||||
desc: htmlSafe("cookies"),
|
||||
}).create({ cookies });
|
||||
|
||||
assert.equal(t.get("desc").string, cookies);
|
||||
assert.equal(
|
||||
t.get("exclaimyUsername"),
|
||||
"%@ translated: !!! eviltrout !!!",
|
||||
"it inserts the string and then translates"
|
||||
);
|
||||
assert.equal(
|
||||
t.get("multiple"),
|
||||
"%@ translated: eviltrout is happy",
|
||||
"it inserts multiple strings and then translates"
|
||||
);
|
||||
|
||||
t.set("username", "codinghorror");
|
||||
assert.equal(
|
||||
t.get("multiple"),
|
||||
"%@ translated: codinghorror is happy",
|
||||
"it supports changing properties"
|
||||
);
|
||||
t.set("mood", "ecstatic");
|
||||
assert.equal(
|
||||
t.get("multiple"),
|
||||
"%@ translated: codinghorror is ecstatic",
|
||||
"it supports changing another property"
|
||||
);
|
||||
});
|
||||
|
||||
test("url", function (assert) {
|
||||
var t, testClass;
|
||||
|
||||
testClass = EmberObject.extend({
|
||||
userUrl: url("username", "/u/%@"),
|
||||
});
|
||||
|
||||
t = testClass.create({ username: "eviltrout" });
|
||||
assert.equal(
|
||||
t.get("userUrl"),
|
||||
"/u/eviltrout",
|
||||
"it supports urls without a prefix"
|
||||
);
|
||||
|
||||
setPrefix("/prefixed");
|
||||
t = testClass.create({ username: "eviltrout" });
|
||||
assert.equal(
|
||||
t.get("userUrl"),
|
||||
"/prefixed/u/eviltrout",
|
||||
"it supports urls with a prefix"
|
||||
);
|
||||
});
|
||||
|
||||
test("htmlSafe", function (assert) {
|
||||
const cookies = "<p>cookies and <b>biscuits</b></p>";
|
||||
const t = EmberObject.extend({
|
||||
desc: htmlSafe("cookies"),
|
||||
}).create({ cookies });
|
||||
|
||||
assert.equal(t.get("desc").string, cookies);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,35 +1,36 @@
|
||||
import { test } from "qunit";
|
||||
import { discourseModule } from "discourse/tests/helpers/qunit-helpers";
|
||||
|
||||
discourseModule("lib:emoji-emojiStore", {
|
||||
beforeEach() {
|
||||
discourseModule("Unit | Utility | emoji-emojiStore", function (hooks) {
|
||||
hooks.beforeEach(function () {
|
||||
this.emojiStore = this.container.lookup("service:emoji-store");
|
||||
this.emojiStore.reset();
|
||||
},
|
||||
afterEach() {
|
||||
});
|
||||
|
||||
hooks.afterEach(function () {
|
||||
this.emojiStore.reset();
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test("defaults", function (assert) {
|
||||
assert.deepEqual(this.emojiStore.favorites, []);
|
||||
assert.equal(this.emojiStore.diversity, 1);
|
||||
});
|
||||
test("defaults", function (assert) {
|
||||
assert.deepEqual(this.emojiStore.favorites, []);
|
||||
assert.equal(this.emojiStore.diversity, 1);
|
||||
});
|
||||
|
||||
test("diversity", function (assert) {
|
||||
this.emojiStore.diversity = 2;
|
||||
assert.equal(this.emojiStore.diversity, 2);
|
||||
});
|
||||
test("diversity", function (assert) {
|
||||
this.emojiStore.diversity = 2;
|
||||
assert.equal(this.emojiStore.diversity, 2);
|
||||
});
|
||||
|
||||
test("favorites", function (assert) {
|
||||
this.emojiStore.favorites = ["smile"];
|
||||
assert.deepEqual(this.emojiStore.favorites, ["smile"]);
|
||||
});
|
||||
test("favorites", function (assert) {
|
||||
this.emojiStore.favorites = ["smile"];
|
||||
assert.deepEqual(this.emojiStore.favorites, ["smile"]);
|
||||
});
|
||||
|
||||
test("track", function (assert) {
|
||||
this.emojiStore.track("woman:t4");
|
||||
assert.deepEqual(this.emojiStore.favorites, ["woman:t4"]);
|
||||
this.emojiStore.track("otter");
|
||||
this.emojiStore.track(":otter:");
|
||||
assert.deepEqual(this.emojiStore.favorites, ["otter", "woman:t4"]);
|
||||
test("track", function (assert) {
|
||||
this.emojiStore.track("woman:t4");
|
||||
assert.deepEqual(this.emojiStore.favorites, ["woman:t4"]);
|
||||
this.emojiStore.track("otter");
|
||||
this.emojiStore.track(":otter:");
|
||||
assert.deepEqual(this.emojiStore.favorites, ["otter", "woman:t4"]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,137 +4,137 @@ import { IMAGE_VERSION as v } from "pretty-text/emoji/version";
|
||||
import { emojiUnescape } from "discourse/lib/text";
|
||||
import { discourseModule } from "discourse/tests/helpers/qunit-helpers";
|
||||
|
||||
discourseModule("lib:emoji");
|
||||
discourseModule("Unit | Utility | emoji", function () {
|
||||
test("emojiUnescape", function (assert) {
|
||||
const testUnescape = (input, expected, description, settings = {}) => {
|
||||
const originalSettings = {};
|
||||
for (const [key, value] of Object.entries(settings)) {
|
||||
originalSettings[key] = this.siteSettings[key];
|
||||
this.siteSettings[key] = value;
|
||||
}
|
||||
|
||||
test("emojiUnescape", function (assert) {
|
||||
const testUnescape = (input, expected, description, settings = {}) => {
|
||||
const originalSettings = {};
|
||||
for (const [key, value] of Object.entries(settings)) {
|
||||
originalSettings[key] = this.siteSettings[key];
|
||||
this.siteSettings[key] = value;
|
||||
}
|
||||
assert.equal(emojiUnescape(input), expected, description);
|
||||
|
||||
assert.equal(emojiUnescape(input), expected, description);
|
||||
for (const [key, value] of Object.entries(originalSettings)) {
|
||||
this.siteSettings[key] = value;
|
||||
}
|
||||
};
|
||||
|
||||
for (const [key, value] of Object.entries(originalSettings)) {
|
||||
this.siteSettings[key] = value;
|
||||
}
|
||||
};
|
||||
testUnescape(
|
||||
"Not emoji :O) :frog) :smile)",
|
||||
"Not emoji :O) :frog) :smile)",
|
||||
"title without emoji"
|
||||
);
|
||||
testUnescape(
|
||||
"Not emoji :frog :smile",
|
||||
"Not emoji :frog :smile",
|
||||
"end colon is not optional"
|
||||
);
|
||||
testUnescape(
|
||||
"emoticons :)",
|
||||
`emoticons <img width=\"20\" height=\"20\" src='/images/emoji/emoji_one/slight_smile.png?v=${v}' title='slight_smile' alt='slight_smile' class='emoji'>`,
|
||||
"emoticons are still supported"
|
||||
);
|
||||
testUnescape(
|
||||
"With emoji :O: :frog: :smile:",
|
||||
`With emoji <img width=\"20\" height=\"20\" src='/images/emoji/emoji_one/o.png?v=${v}' title='O' alt='O' class='emoji'> <img width=\"20\" height=\"20\" src='/images/emoji/emoji_one/frog.png?v=${v}' title='frog' alt='frog' class='emoji'> <img width=\"20\" height=\"20\" src='/images/emoji/emoji_one/smile.png?v=${v}' title='smile' alt='smile' class='emoji'>`,
|
||||
"title with emoji"
|
||||
);
|
||||
testUnescape(
|
||||
"a:smile:a",
|
||||
"a:smile:a",
|
||||
"word characters not allowed next to emoji"
|
||||
);
|
||||
testUnescape(
|
||||
"(:frog:) :)",
|
||||
`(<img width=\"20\" height=\"20\" src='/images/emoji/emoji_one/frog.png?v=${v}' title='frog' alt='frog' class='emoji'>) <img width=\"20\" height=\"20\" src='/images/emoji/emoji_one/slight_smile.png?v=${v}' title='slight_smile' alt='slight_smile' class='emoji'>`,
|
||||
"non-word characters allowed next to emoji"
|
||||
);
|
||||
testUnescape(
|
||||
":smile: hi",
|
||||
`<img width=\"20\" height=\"20\" src='/images/emoji/emoji_one/smile.png?v=${v}' title='smile' alt='smile' class='emoji'> hi`,
|
||||
"start of line"
|
||||
);
|
||||
testUnescape(
|
||||
"hi :smile:",
|
||||
`hi <img width=\"20\" height=\"20\" src='/images/emoji/emoji_one/smile.png?v=${v}' title='smile' alt='smile' class='emoji'>`,
|
||||
"end of line"
|
||||
);
|
||||
testUnescape(
|
||||
"hi :blonde_woman:t4:",
|
||||
`hi <img width=\"20\" height=\"20\" src='/images/emoji/emoji_one/blonde_woman/4.png?v=${v}' title='blonde_woman:t4' alt='blonde_woman:t4' class='emoji'>`,
|
||||
"support for skin tones"
|
||||
);
|
||||
testUnescape(
|
||||
"hi :blonde_woman:t4: :blonde_man:t6:",
|
||||
`hi <img width=\"20\" height=\"20\" src='/images/emoji/emoji_one/blonde_woman/4.png?v=${v}' title='blonde_woman:t4' alt='blonde_woman:t4' class='emoji'> <img width=\"20\" height=\"20\" src='/images/emoji/emoji_one/blonde_man/6.png?v=${v}' title='blonde_man:t6' alt='blonde_man:t6' class='emoji'>`,
|
||||
"support for multiple skin tones"
|
||||
);
|
||||
testUnescape(
|
||||
"hi :blonde_man:t6",
|
||||
"hi :blonde_man:t6",
|
||||
"end colon not optional for skin tones"
|
||||
);
|
||||
testUnescape(
|
||||
"emoticons :)",
|
||||
"emoticons :)",
|
||||
"no emoticons when emojis are disabled",
|
||||
{ enable_emoji: false }
|
||||
);
|
||||
testUnescape(
|
||||
"emoji :smile:",
|
||||
"emoji :smile:",
|
||||
"no emojis when emojis are disabled",
|
||||
{ enable_emoji: false }
|
||||
);
|
||||
testUnescape(
|
||||
"emoticons :)",
|
||||
"emoticons :)",
|
||||
"no emoticons when emoji shortcuts are disabled",
|
||||
{ enable_emoji_shortcuts: false }
|
||||
);
|
||||
testUnescape(
|
||||
"Hello 😊 World",
|
||||
`Hello <img width=\"20\" height=\"20\" src='/images/emoji/emoji_one/blush.png?v=${v}' title='blush' alt='blush' class='emoji'> World`,
|
||||
"emoji from Unicode emoji"
|
||||
);
|
||||
testUnescape(
|
||||
"Hello😊World",
|
||||
"Hello😊World",
|
||||
"keeps Unicode emoji when inline translation disabled",
|
||||
{
|
||||
enable_inline_emoji_translation: false,
|
||||
}
|
||||
);
|
||||
testUnescape(
|
||||
"Hello😊World",
|
||||
`Hello<img width=\"20\" height=\"20\" src='/images/emoji/emoji_one/blush.png?v=${v}' title='blush' alt='blush' class='emoji'>World`,
|
||||
"emoji from Unicode emoji when inline translation enabled",
|
||||
{
|
||||
enable_inline_emoji_translation: true,
|
||||
}
|
||||
);
|
||||
testUnescape(
|
||||
"hi:smile:",
|
||||
"hi:smile:",
|
||||
"no emojis when inline translation disabled",
|
||||
{
|
||||
enable_inline_emoji_translation: false,
|
||||
}
|
||||
);
|
||||
testUnescape(
|
||||
"hi:smile:",
|
||||
`hi<img width=\"20\" height=\"20\" src='/images/emoji/emoji_one/smile.png?v=${v}' title='smile' alt='smile' class='emoji'>`,
|
||||
"emoji when inline translation enabled",
|
||||
{ enable_inline_emoji_translation: true }
|
||||
);
|
||||
});
|
||||
|
||||
testUnescape(
|
||||
"Not emoji :O) :frog) :smile)",
|
||||
"Not emoji :O) :frog) :smile)",
|
||||
"title without emoji"
|
||||
);
|
||||
testUnescape(
|
||||
"Not emoji :frog :smile",
|
||||
"Not emoji :frog :smile",
|
||||
"end colon is not optional"
|
||||
);
|
||||
testUnescape(
|
||||
"emoticons :)",
|
||||
`emoticons <img width=\"20\" height=\"20\" src='/images/emoji/emoji_one/slight_smile.png?v=${v}' title='slight_smile' alt='slight_smile' class='emoji'>`,
|
||||
"emoticons are still supported"
|
||||
);
|
||||
testUnescape(
|
||||
"With emoji :O: :frog: :smile:",
|
||||
`With emoji <img width=\"20\" height=\"20\" src='/images/emoji/emoji_one/o.png?v=${v}' title='O' alt='O' class='emoji'> <img width=\"20\" height=\"20\" src='/images/emoji/emoji_one/frog.png?v=${v}' title='frog' alt='frog' class='emoji'> <img width=\"20\" height=\"20\" src='/images/emoji/emoji_one/smile.png?v=${v}' title='smile' alt='smile' class='emoji'>`,
|
||||
"title with emoji"
|
||||
);
|
||||
testUnescape(
|
||||
"a:smile:a",
|
||||
"a:smile:a",
|
||||
"word characters not allowed next to emoji"
|
||||
);
|
||||
testUnescape(
|
||||
"(:frog:) :)",
|
||||
`(<img width=\"20\" height=\"20\" src='/images/emoji/emoji_one/frog.png?v=${v}' title='frog' alt='frog' class='emoji'>) <img width=\"20\" height=\"20\" src='/images/emoji/emoji_one/slight_smile.png?v=${v}' title='slight_smile' alt='slight_smile' class='emoji'>`,
|
||||
"non-word characters allowed next to emoji"
|
||||
);
|
||||
testUnescape(
|
||||
":smile: hi",
|
||||
`<img width=\"20\" height=\"20\" src='/images/emoji/emoji_one/smile.png?v=${v}' title='smile' alt='smile' class='emoji'> hi`,
|
||||
"start of line"
|
||||
);
|
||||
testUnescape(
|
||||
"hi :smile:",
|
||||
`hi <img width=\"20\" height=\"20\" src='/images/emoji/emoji_one/smile.png?v=${v}' title='smile' alt='smile' class='emoji'>`,
|
||||
"end of line"
|
||||
);
|
||||
testUnescape(
|
||||
"hi :blonde_woman:t4:",
|
||||
`hi <img width=\"20\" height=\"20\" src='/images/emoji/emoji_one/blonde_woman/4.png?v=${v}' title='blonde_woman:t4' alt='blonde_woman:t4' class='emoji'>`,
|
||||
"support for skin tones"
|
||||
);
|
||||
testUnescape(
|
||||
"hi :blonde_woman:t4: :blonde_man:t6:",
|
||||
`hi <img width=\"20\" height=\"20\" src='/images/emoji/emoji_one/blonde_woman/4.png?v=${v}' title='blonde_woman:t4' alt='blonde_woman:t4' class='emoji'> <img width=\"20\" height=\"20\" src='/images/emoji/emoji_one/blonde_man/6.png?v=${v}' title='blonde_man:t6' alt='blonde_man:t6' class='emoji'>`,
|
||||
"support for multiple skin tones"
|
||||
);
|
||||
testUnescape(
|
||||
"hi :blonde_man:t6",
|
||||
"hi :blonde_man:t6",
|
||||
"end colon not optional for skin tones"
|
||||
);
|
||||
testUnescape(
|
||||
"emoticons :)",
|
||||
"emoticons :)",
|
||||
"no emoticons when emojis are disabled",
|
||||
{ enable_emoji: false }
|
||||
);
|
||||
testUnescape(
|
||||
"emoji :smile:",
|
||||
"emoji :smile:",
|
||||
"no emojis when emojis are disabled",
|
||||
{ enable_emoji: false }
|
||||
);
|
||||
testUnescape(
|
||||
"emoticons :)",
|
||||
"emoticons :)",
|
||||
"no emoticons when emoji shortcuts are disabled",
|
||||
{ enable_emoji_shortcuts: false }
|
||||
);
|
||||
testUnescape(
|
||||
"Hello 😊 World",
|
||||
`Hello <img width=\"20\" height=\"20\" src='/images/emoji/emoji_one/blush.png?v=${v}' title='blush' alt='blush' class='emoji'> World`,
|
||||
"emoji from Unicode emoji"
|
||||
);
|
||||
testUnescape(
|
||||
"Hello😊World",
|
||||
"Hello😊World",
|
||||
"keeps Unicode emoji when inline translation disabled",
|
||||
{
|
||||
enable_inline_emoji_translation: false,
|
||||
}
|
||||
);
|
||||
testUnescape(
|
||||
"Hello😊World",
|
||||
`Hello<img width=\"20\" height=\"20\" src='/images/emoji/emoji_one/blush.png?v=${v}' title='blush' alt='blush' class='emoji'>World`,
|
||||
"emoji from Unicode emoji when inline translation enabled",
|
||||
{
|
||||
enable_inline_emoji_translation: true,
|
||||
}
|
||||
);
|
||||
testUnescape(
|
||||
"hi:smile:",
|
||||
"hi:smile:",
|
||||
"no emojis when inline translation disabled",
|
||||
{
|
||||
enable_inline_emoji_translation: false,
|
||||
}
|
||||
);
|
||||
testUnescape(
|
||||
"hi:smile:",
|
||||
`hi<img width=\"20\" height=\"20\" src='/images/emoji/emoji_one/smile.png?v=${v}' title='smile' alt='smile' class='emoji'>`,
|
||||
"emoji when inline translation enabled",
|
||||
{ enable_inline_emoji_translation: true }
|
||||
);
|
||||
});
|
||||
|
||||
test("Emoji search", function (assert) {
|
||||
// able to find an alias
|
||||
assert.equal(emojiSearch("+1").length, 1);
|
||||
|
||||
// able to find middle of line search
|
||||
assert.equal(emojiSearch("check", { maxResults: 3 }).length, 3);
|
||||
test("Emoji search", function (assert) {
|
||||
// able to find an alias
|
||||
assert.equal(emojiSearch("+1").length, 1);
|
||||
|
||||
// able to find middle of line search
|
||||
assert.equal(emojiSearch("check", { maxResults: 3 }).length, 3);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,16 +10,6 @@ import {
|
||||
import { discourseModule } from "discourse/tests/helpers/qunit-helpers";
|
||||
import sinon from "sinon";
|
||||
|
||||
discourseModule("lib:formatter", {
|
||||
beforeEach() {
|
||||
this.clock = sinon.useFakeTimers(new Date(2012, 11, 31, 12, 0).getTime());
|
||||
},
|
||||
|
||||
afterEach() {
|
||||
this.clock.restore();
|
||||
},
|
||||
});
|
||||
|
||||
function formatMins(mins, opts = {}) {
|
||||
let dt = new Date(new Date() - mins * 60 * 1000);
|
||||
return relativeAge(dt, {
|
||||
@@ -50,247 +40,268 @@ function strip(html) {
|
||||
return $(html).text();
|
||||
}
|
||||
|
||||
test("formating medium length dates", function (assert) {
|
||||
let shortDateYear = shortDateTester("MMM D, 'YY");
|
||||
discourseModule("Unit | Utility | formatter", function (hooks) {
|
||||
hooks.beforeEach(function () {
|
||||
this.clock = sinon.useFakeTimers(new Date(2012, 11, 31, 12, 0).getTime());
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
strip(formatMins(1.4, { format: "medium", leaveAgo: true })),
|
||||
"1 min ago"
|
||||
);
|
||||
assert.equal(
|
||||
strip(formatMins(2, { format: "medium", leaveAgo: true })),
|
||||
"2 mins ago"
|
||||
);
|
||||
assert.equal(
|
||||
strip(formatMins(55, { format: "medium", leaveAgo: true })),
|
||||
"55 mins ago"
|
||||
);
|
||||
assert.equal(
|
||||
strip(formatMins(56, { format: "medium", leaveAgo: true })),
|
||||
"1 hour ago"
|
||||
);
|
||||
assert.equal(
|
||||
strip(formatHours(4, { format: "medium", leaveAgo: true })),
|
||||
"4 hours ago"
|
||||
);
|
||||
assert.equal(
|
||||
strip(formatHours(22, { format: "medium", leaveAgo: true })),
|
||||
"22 hours ago"
|
||||
);
|
||||
assert.equal(
|
||||
strip(formatHours(23, { format: "medium", leaveAgo: true })),
|
||||
"23 hours ago"
|
||||
);
|
||||
assert.equal(
|
||||
strip(formatHours(23.5, { format: "medium", leaveAgo: true })),
|
||||
"1 day ago"
|
||||
);
|
||||
assert.equal(
|
||||
strip(formatDays(4.85, { format: "medium", leaveAgo: true })),
|
||||
"4 days ago"
|
||||
);
|
||||
hooks.afterEach(function () {
|
||||
this.clock.restore();
|
||||
});
|
||||
|
||||
assert.equal(strip(formatMins(0, { format: "medium" })), "just now");
|
||||
assert.equal(strip(formatMins(1.4, { format: "medium" })), "1 min");
|
||||
assert.equal(strip(formatMins(2, { format: "medium" })), "2 mins");
|
||||
assert.equal(strip(formatMins(55, { format: "medium" })), "55 mins");
|
||||
assert.equal(strip(formatMins(56, { format: "medium" })), "1 hour");
|
||||
assert.equal(strip(formatHours(4, { format: "medium" })), "4 hours");
|
||||
assert.equal(strip(formatHours(22, { format: "medium" })), "22 hours");
|
||||
assert.equal(strip(formatHours(23, { format: "medium" })), "23 hours");
|
||||
assert.equal(strip(formatHours(23.5, { format: "medium" })), "1 day");
|
||||
assert.equal(strip(formatDays(4.85, { format: "medium" })), "4 days");
|
||||
test("formating medium length dates", function (assert) {
|
||||
let shortDateYear = shortDateTester("MMM D, 'YY");
|
||||
|
||||
assert.equal(strip(formatDays(6, { format: "medium" })), shortDate(6));
|
||||
assert.equal(strip(formatDays(100, { format: "medium" })), shortDate(100)); // eg: Jan 23
|
||||
assert.equal(
|
||||
strip(formatDays(500, { format: "medium" })),
|
||||
shortDateYear(500)
|
||||
);
|
||||
assert.equal(
|
||||
strip(formatMins(1.4, { format: "medium", leaveAgo: true })),
|
||||
"1 min ago"
|
||||
);
|
||||
assert.equal(
|
||||
strip(formatMins(2, { format: "medium", leaveAgo: true })),
|
||||
"2 mins ago"
|
||||
);
|
||||
assert.equal(
|
||||
strip(formatMins(55, { format: "medium", leaveAgo: true })),
|
||||
"55 mins ago"
|
||||
);
|
||||
assert.equal(
|
||||
strip(formatMins(56, { format: "medium", leaveAgo: true })),
|
||||
"1 hour ago"
|
||||
);
|
||||
assert.equal(
|
||||
strip(formatHours(4, { format: "medium", leaveAgo: true })),
|
||||
"4 hours ago"
|
||||
);
|
||||
assert.equal(
|
||||
strip(formatHours(22, { format: "medium", leaveAgo: true })),
|
||||
"22 hours ago"
|
||||
);
|
||||
assert.equal(
|
||||
strip(formatHours(23, { format: "medium", leaveAgo: true })),
|
||||
"23 hours ago"
|
||||
);
|
||||
assert.equal(
|
||||
strip(formatHours(23.5, { format: "medium", leaveAgo: true })),
|
||||
"1 day ago"
|
||||
);
|
||||
assert.equal(
|
||||
strip(formatDays(4.85, { format: "medium", leaveAgo: true })),
|
||||
"4 days ago"
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
$(formatDays(0, { format: "medium" })).attr("title"),
|
||||
longDate(new Date())
|
||||
);
|
||||
assert.equal($(formatDays(0, { format: "medium" })).attr("class"), "date");
|
||||
assert.equal(strip(formatMins(0, { format: "medium" })), "just now");
|
||||
assert.equal(strip(formatMins(1.4, { format: "medium" })), "1 min");
|
||||
assert.equal(strip(formatMins(2, { format: "medium" })), "2 mins");
|
||||
assert.equal(strip(formatMins(55, { format: "medium" })), "55 mins");
|
||||
assert.equal(strip(formatMins(56, { format: "medium" })), "1 hour");
|
||||
assert.equal(strip(formatHours(4, { format: "medium" })), "4 hours");
|
||||
assert.equal(strip(formatHours(22, { format: "medium" })), "22 hours");
|
||||
assert.equal(strip(formatHours(23, { format: "medium" })), "23 hours");
|
||||
assert.equal(strip(formatHours(23.5, { format: "medium" })), "1 day");
|
||||
assert.equal(strip(formatDays(4.85, { format: "medium" })), "4 days");
|
||||
|
||||
this.clock.restore();
|
||||
this.clock = sinon.useFakeTimers(new Date(2012, 0, 9, 12, 0).getTime()); // Jan 9, 2012
|
||||
assert.equal(strip(formatDays(6, { format: "medium" })), shortDate(6));
|
||||
assert.equal(strip(formatDays(100, { format: "medium" })), shortDate(100)); // eg: Jan 23
|
||||
assert.equal(
|
||||
strip(formatDays(500, { format: "medium" })),
|
||||
shortDateYear(500)
|
||||
);
|
||||
|
||||
assert.equal(strip(formatDays(8, { format: "medium" })), shortDate(8));
|
||||
assert.equal(strip(formatDays(10, { format: "medium" })), shortDateYear(10));
|
||||
});
|
||||
|
||||
test("formating tiny dates", function (assert) {
|
||||
let shortDateYear = shortDateTester("MMM 'YY");
|
||||
|
||||
assert.equal(formatMins(0), "1m");
|
||||
assert.equal(formatMins(1), "1m");
|
||||
assert.equal(formatMins(2), "2m");
|
||||
assert.equal(formatMins(60), "1h");
|
||||
assert.equal(formatHours(4), "4h");
|
||||
assert.equal(formatHours(23), "23h");
|
||||
assert.equal(formatHours(23.5), "1d");
|
||||
assert.equal(formatDays(1), "1d");
|
||||
assert.equal(formatDays(14), "14d");
|
||||
assert.equal(formatDays(15), shortDate(15));
|
||||
assert.equal(formatDays(92), shortDate(92));
|
||||
assert.equal(formatDays(364), shortDate(364));
|
||||
assert.equal(formatDays(365), shortDate(365));
|
||||
assert.equal(formatDays(366), shortDateYear(366)); // leap year
|
||||
assert.equal(formatDays(500), shortDateYear(500));
|
||||
assert.equal(formatDays(365 * 2 + 1), shortDateYear(365 * 2 + 1)); // one leap year
|
||||
|
||||
var originalValue = this.siteSettings.relative_date_duration;
|
||||
this.siteSettings.relative_date_duration = 7;
|
||||
assert.equal(formatDays(7), "7d");
|
||||
assert.equal(formatDays(8), shortDate(8));
|
||||
|
||||
this.siteSettings.relative_date_duration = 1;
|
||||
assert.equal(formatDays(1), "1d");
|
||||
assert.equal(formatDays(2), shortDate(2));
|
||||
|
||||
this.siteSettings.relative_date_duration = 0;
|
||||
assert.equal(formatMins(0), "1m");
|
||||
assert.equal(formatMins(1), "1m");
|
||||
assert.equal(formatMins(2), "2m");
|
||||
assert.equal(formatMins(60), "1h");
|
||||
assert.equal(formatDays(1), shortDate(1));
|
||||
assert.equal(formatDays(2), shortDate(2));
|
||||
assert.equal(formatDays(366), shortDateYear(366));
|
||||
|
||||
this.siteSettings.relative_date_duration = null;
|
||||
assert.equal(formatDays(1), "1d");
|
||||
assert.equal(formatDays(14), "14d");
|
||||
assert.equal(formatDays(15), shortDate(15));
|
||||
|
||||
this.siteSettings.relative_date_duration = 14;
|
||||
|
||||
this.clock.restore();
|
||||
this.clock = sinon.useFakeTimers(new Date(2012, 0, 12, 12, 0).getTime()); // Jan 12, 2012
|
||||
|
||||
assert.equal(formatDays(11), "11d");
|
||||
assert.equal(formatDays(14), "14d");
|
||||
assert.equal(formatDays(15), shortDateYear(15));
|
||||
assert.equal(formatDays(366), shortDateYear(366));
|
||||
|
||||
this.clock.restore();
|
||||
this.clock = sinon.useFakeTimers(new Date(2012, 0, 20, 12, 0).getTime()); // Jan 20, 2012
|
||||
|
||||
assert.equal(formatDays(14), "14d");
|
||||
assert.equal(formatDays(15), shortDate(15));
|
||||
assert.equal(formatDays(20), shortDateYear(20));
|
||||
|
||||
this.siteSettings.relative_date_duration = originalValue;
|
||||
});
|
||||
|
||||
test("autoUpdatingRelativeAge", function (assert) {
|
||||
var d = moment().subtract(1, "day").toDate();
|
||||
|
||||
var $elem = $(autoUpdatingRelativeAge(d));
|
||||
assert.equal($elem.data("format"), "tiny");
|
||||
assert.equal($elem.data("time"), d.getTime());
|
||||
assert.equal($elem.attr("title"), undefined);
|
||||
|
||||
$elem = $(autoUpdatingRelativeAge(d, { title: true }));
|
||||
assert.equal($elem.attr("title"), longDate(d));
|
||||
|
||||
$elem = $(
|
||||
autoUpdatingRelativeAge(d, {
|
||||
format: "medium",
|
||||
title: true,
|
||||
leaveAgo: true,
|
||||
})
|
||||
);
|
||||
assert.equal($elem.data("format"), "medium-with-ago");
|
||||
assert.equal($elem.data("time"), d.getTime());
|
||||
assert.equal($elem.attr("title"), longDate(d));
|
||||
assert.equal($elem.html(), "1 day ago");
|
||||
|
||||
$elem = $(autoUpdatingRelativeAge(d, { format: "medium" }));
|
||||
assert.equal($elem.data("format"), "medium");
|
||||
assert.equal($elem.data("time"), d.getTime());
|
||||
assert.equal($elem.attr("title"), undefined);
|
||||
assert.equal($elem.html(), "1 day");
|
||||
});
|
||||
|
||||
test("updateRelativeAge", function (assert) {
|
||||
var d = new Date();
|
||||
var $elem = $(autoUpdatingRelativeAge(d));
|
||||
$elem.data("time", d.getTime() - 2 * 60 * 1000);
|
||||
|
||||
updateRelativeAge($elem);
|
||||
|
||||
assert.equal($elem.html(), "2m");
|
||||
|
||||
d = new Date();
|
||||
$elem = $(autoUpdatingRelativeAge(d, { format: "medium", leaveAgo: true }));
|
||||
$elem.data("time", d.getTime() - 2 * 60 * 1000);
|
||||
|
||||
updateRelativeAge($elem);
|
||||
|
||||
assert.equal($elem.html(), "2 mins ago");
|
||||
});
|
||||
|
||||
test("number", function (assert) {
|
||||
assert.equal(number(123), "123", "it returns a string version of the number");
|
||||
assert.equal(number("123"), "123", "it works with a string command");
|
||||
assert.equal(number(NaN), "0", "it returns 0 for NaN");
|
||||
assert.equal(number(3333), "3.3k", "it abbreviates thousands");
|
||||
assert.equal(number(2499999), "2.5M", "it abbreviates millions");
|
||||
assert.equal(number("2499999.5"), "2.5M", "it abbreviates millions");
|
||||
assert.equal(number(1000000), "1.0M", "it abbreviates a million");
|
||||
assert.equal(number(999999), "999k", "it abbreviates hundreds of thousands");
|
||||
assert.equal(
|
||||
number(18.2),
|
||||
"18",
|
||||
"it returns a float number rounded to an integer as a string"
|
||||
);
|
||||
assert.equal(
|
||||
number(18.6),
|
||||
"19",
|
||||
"it returns a float number rounded to an integer as a string"
|
||||
);
|
||||
assert.equal(
|
||||
number("12.3"),
|
||||
"12",
|
||||
"it returns a string float rounded to an integer as a string"
|
||||
);
|
||||
assert.equal(
|
||||
number("12.6"),
|
||||
"13",
|
||||
"it returns a string float rounded to an integer as a string"
|
||||
);
|
||||
});
|
||||
|
||||
test("durationTiny", function (assert) {
|
||||
assert.equal(durationTiny(), "—", "undefined is a dash");
|
||||
assert.equal(durationTiny(null), "—", "null is a dash");
|
||||
assert.equal(durationTiny(0), "< 1m", "0 seconds shows as < 1m");
|
||||
assert.equal(durationTiny(59), "< 1m", "59 seconds shows as < 1m");
|
||||
assert.equal(durationTiny(60), "1m", "60 seconds shows as 1m");
|
||||
assert.equal(durationTiny(90), "2m", "90 seconds shows as 2m");
|
||||
assert.equal(durationTiny(120), "2m", "120 seconds shows as 2m");
|
||||
assert.equal(durationTiny(60 * 45), "1h", "45 minutes shows as 1h");
|
||||
assert.equal(durationTiny(60 * 60), "1h", "60 minutes shows as 1h");
|
||||
assert.equal(durationTiny(60 * 90), "2h", "90 minutes shows as 2h");
|
||||
assert.equal(durationTiny(3600 * 23), "23h", "23 hours shows as 23h");
|
||||
assert.equal(
|
||||
durationTiny(3600 * 24 - 29),
|
||||
"1d",
|
||||
"23 hours 31 mins shows as 1d"
|
||||
);
|
||||
assert.equal(durationTiny(3600 * 24 * 89), "89d", "89 days shows as 89d");
|
||||
assert.equal(
|
||||
durationTiny(60 * (525600 - 1)),
|
||||
"12mon",
|
||||
"364 days shows as 12mon"
|
||||
);
|
||||
assert.equal(durationTiny(60 * 525600), "1y", "365 days shows as 1y");
|
||||
assert.equal(durationTiny(86400 * 456), "1y", "456 days shows as 1y");
|
||||
assert.equal(durationTiny(86400 * 457), "> 1y", "457 days shows as > 1y");
|
||||
assert.equal(durationTiny(86400 * 638), "> 1y", "638 days shows as > 1y");
|
||||
assert.equal(durationTiny(86400 * 639), "2y", "639 days shows as 2y");
|
||||
assert.equal(durationTiny(86400 * 821), "2y", "821 days shows as 2y");
|
||||
assert.equal(durationTiny(86400 * 822), "> 2y", "822 days shows as > 2y");
|
||||
assert.equal(
|
||||
$(formatDays(0, { format: "medium" })).attr("title"),
|
||||
longDate(new Date())
|
||||
);
|
||||
assert.equal($(formatDays(0, { format: "medium" })).attr("class"), "date");
|
||||
|
||||
this.clock.restore();
|
||||
this.clock = sinon.useFakeTimers(new Date(2012, 0, 9, 12, 0).getTime()); // Jan 9, 2012
|
||||
|
||||
assert.equal(strip(formatDays(8, { format: "medium" })), shortDate(8));
|
||||
assert.equal(
|
||||
strip(formatDays(10, { format: "medium" })),
|
||||
shortDateYear(10)
|
||||
);
|
||||
});
|
||||
|
||||
test("formating tiny dates", function (assert) {
|
||||
let shortDateYear = shortDateTester("MMM 'YY");
|
||||
|
||||
assert.equal(formatMins(0), "1m");
|
||||
assert.equal(formatMins(1), "1m");
|
||||
assert.equal(formatMins(2), "2m");
|
||||
assert.equal(formatMins(60), "1h");
|
||||
assert.equal(formatHours(4), "4h");
|
||||
assert.equal(formatHours(23), "23h");
|
||||
assert.equal(formatHours(23.5), "1d");
|
||||
assert.equal(formatDays(1), "1d");
|
||||
assert.equal(formatDays(14), "14d");
|
||||
assert.equal(formatDays(15), shortDate(15));
|
||||
assert.equal(formatDays(92), shortDate(92));
|
||||
assert.equal(formatDays(364), shortDate(364));
|
||||
assert.equal(formatDays(365), shortDate(365));
|
||||
assert.equal(formatDays(366), shortDateYear(366)); // leap year
|
||||
assert.equal(formatDays(500), shortDateYear(500));
|
||||
assert.equal(formatDays(365 * 2 + 1), shortDateYear(365 * 2 + 1)); // one leap year
|
||||
|
||||
var originalValue = this.siteSettings.relative_date_duration;
|
||||
this.siteSettings.relative_date_duration = 7;
|
||||
assert.equal(formatDays(7), "7d");
|
||||
assert.equal(formatDays(8), shortDate(8));
|
||||
|
||||
this.siteSettings.relative_date_duration = 1;
|
||||
assert.equal(formatDays(1), "1d");
|
||||
assert.equal(formatDays(2), shortDate(2));
|
||||
|
||||
this.siteSettings.relative_date_duration = 0;
|
||||
assert.equal(formatMins(0), "1m");
|
||||
assert.equal(formatMins(1), "1m");
|
||||
assert.equal(formatMins(2), "2m");
|
||||
assert.equal(formatMins(60), "1h");
|
||||
assert.equal(formatDays(1), shortDate(1));
|
||||
assert.equal(formatDays(2), shortDate(2));
|
||||
assert.equal(formatDays(366), shortDateYear(366));
|
||||
|
||||
this.siteSettings.relative_date_duration = null;
|
||||
assert.equal(formatDays(1), "1d");
|
||||
assert.equal(formatDays(14), "14d");
|
||||
assert.equal(formatDays(15), shortDate(15));
|
||||
|
||||
this.siteSettings.relative_date_duration = 14;
|
||||
|
||||
this.clock.restore();
|
||||
this.clock = sinon.useFakeTimers(new Date(2012, 0, 12, 12, 0).getTime()); // Jan 12, 2012
|
||||
|
||||
assert.equal(formatDays(11), "11d");
|
||||
assert.equal(formatDays(14), "14d");
|
||||
assert.equal(formatDays(15), shortDateYear(15));
|
||||
assert.equal(formatDays(366), shortDateYear(366));
|
||||
|
||||
this.clock.restore();
|
||||
this.clock = sinon.useFakeTimers(new Date(2012, 0, 20, 12, 0).getTime()); // Jan 20, 2012
|
||||
|
||||
assert.equal(formatDays(14), "14d");
|
||||
assert.equal(formatDays(15), shortDate(15));
|
||||
assert.equal(formatDays(20), shortDateYear(20));
|
||||
|
||||
this.siteSettings.relative_date_duration = originalValue;
|
||||
});
|
||||
|
||||
test("autoUpdatingRelativeAge", function (assert) {
|
||||
var d = moment().subtract(1, "day").toDate();
|
||||
|
||||
var $elem = $(autoUpdatingRelativeAge(d));
|
||||
assert.equal($elem.data("format"), "tiny");
|
||||
assert.equal($elem.data("time"), d.getTime());
|
||||
assert.equal($elem.attr("title"), undefined);
|
||||
|
||||
$elem = $(autoUpdatingRelativeAge(d, { title: true }));
|
||||
assert.equal($elem.attr("title"), longDate(d));
|
||||
|
||||
$elem = $(
|
||||
autoUpdatingRelativeAge(d, {
|
||||
format: "medium",
|
||||
title: true,
|
||||
leaveAgo: true,
|
||||
})
|
||||
);
|
||||
assert.equal($elem.data("format"), "medium-with-ago");
|
||||
assert.equal($elem.data("time"), d.getTime());
|
||||
assert.equal($elem.attr("title"), longDate(d));
|
||||
assert.equal($elem.html(), "1 day ago");
|
||||
|
||||
$elem = $(autoUpdatingRelativeAge(d, { format: "medium" }));
|
||||
assert.equal($elem.data("format"), "medium");
|
||||
assert.equal($elem.data("time"), d.getTime());
|
||||
assert.equal($elem.attr("title"), undefined);
|
||||
assert.equal($elem.html(), "1 day");
|
||||
});
|
||||
|
||||
test("updateRelativeAge", function (assert) {
|
||||
var d = new Date();
|
||||
var $elem = $(autoUpdatingRelativeAge(d));
|
||||
$elem.data("time", d.getTime() - 2 * 60 * 1000);
|
||||
|
||||
updateRelativeAge($elem);
|
||||
|
||||
assert.equal($elem.html(), "2m");
|
||||
|
||||
d = new Date();
|
||||
$elem = $(autoUpdatingRelativeAge(d, { format: "medium", leaveAgo: true }));
|
||||
$elem.data("time", d.getTime() - 2 * 60 * 1000);
|
||||
|
||||
updateRelativeAge($elem);
|
||||
|
||||
assert.equal($elem.html(), "2 mins ago");
|
||||
});
|
||||
|
||||
test("number", function (assert) {
|
||||
assert.equal(
|
||||
number(123),
|
||||
"123",
|
||||
"it returns a string version of the number"
|
||||
);
|
||||
assert.equal(number("123"), "123", "it works with a string command");
|
||||
assert.equal(number(NaN), "0", "it returns 0 for NaN");
|
||||
assert.equal(number(3333), "3.3k", "it abbreviates thousands");
|
||||
assert.equal(number(2499999), "2.5M", "it abbreviates millions");
|
||||
assert.equal(number("2499999.5"), "2.5M", "it abbreviates millions");
|
||||
assert.equal(number(1000000), "1.0M", "it abbreviates a million");
|
||||
assert.equal(
|
||||
number(999999),
|
||||
"999k",
|
||||
"it abbreviates hundreds of thousands"
|
||||
);
|
||||
assert.equal(
|
||||
number(18.2),
|
||||
"18",
|
||||
"it returns a float number rounded to an integer as a string"
|
||||
);
|
||||
assert.equal(
|
||||
number(18.6),
|
||||
"19",
|
||||
"it returns a float number rounded to an integer as a string"
|
||||
);
|
||||
assert.equal(
|
||||
number("12.3"),
|
||||
"12",
|
||||
"it returns a string float rounded to an integer as a string"
|
||||
);
|
||||
assert.equal(
|
||||
number("12.6"),
|
||||
"13",
|
||||
"it returns a string float rounded to an integer as a string"
|
||||
);
|
||||
});
|
||||
|
||||
test("durationTiny", function (assert) {
|
||||
assert.equal(durationTiny(), "—", "undefined is a dash");
|
||||
assert.equal(durationTiny(null), "—", "null is a dash");
|
||||
assert.equal(durationTiny(0), "< 1m", "0 seconds shows as < 1m");
|
||||
assert.equal(durationTiny(59), "< 1m", "59 seconds shows as < 1m");
|
||||
assert.equal(durationTiny(60), "1m", "60 seconds shows as 1m");
|
||||
assert.equal(durationTiny(90), "2m", "90 seconds shows as 2m");
|
||||
assert.equal(durationTiny(120), "2m", "120 seconds shows as 2m");
|
||||
assert.equal(durationTiny(60 * 45), "1h", "45 minutes shows as 1h");
|
||||
assert.equal(durationTiny(60 * 60), "1h", "60 minutes shows as 1h");
|
||||
assert.equal(durationTiny(60 * 90), "2h", "90 minutes shows as 2h");
|
||||
assert.equal(durationTiny(3600 * 23), "23h", "23 hours shows as 23h");
|
||||
assert.equal(
|
||||
durationTiny(3600 * 24 - 29),
|
||||
"1d",
|
||||
"23 hours 31 mins shows as 1d"
|
||||
);
|
||||
assert.equal(durationTiny(3600 * 24 * 89), "89d", "89 days shows as 89d");
|
||||
assert.equal(
|
||||
durationTiny(60 * (525600 - 1)),
|
||||
"12mon",
|
||||
"364 days shows as 12mon"
|
||||
);
|
||||
assert.equal(durationTiny(60 * 525600), "1y", "365 days shows as 1y");
|
||||
assert.equal(durationTiny(86400 * 456), "1y", "456 days shows as 1y");
|
||||
assert.equal(durationTiny(86400 * 457), "> 1y", "457 days shows as > 1y");
|
||||
assert.equal(durationTiny(86400 * 638), "> 1y", "638 days shows as > 1y");
|
||||
assert.equal(durationTiny(86400 * 639), "2y", "639 days shows as 2y");
|
||||
assert.equal(durationTiny(86400 * 821), "2y", "821 days shows as 2y");
|
||||
assert.equal(durationTiny(86400 * 822), "> 2y", "822 days shows as > 2y");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,86 +10,86 @@ import {
|
||||
withoutPrefix,
|
||||
} from "discourse-common/lib/get-url";
|
||||
|
||||
module("lib:get-url");
|
||||
module("Unit | Utility | get-url", function () {
|
||||
test("isAbsoluteURL", function (assert) {
|
||||
setupURL(null, "https://example.com", "/forum");
|
||||
assert.ok(isAbsoluteURL("https://example.com/test/thing"));
|
||||
assert.ok(!isAbsoluteURL("http://example.com/test/thing"));
|
||||
assert.ok(!isAbsoluteURL("https://discourse.org/test/thing"));
|
||||
});
|
||||
|
||||
test("isAbsoluteURL", function (assert) {
|
||||
setupURL(null, "https://example.com", "/forum");
|
||||
assert.ok(isAbsoluteURL("https://example.com/test/thing"));
|
||||
assert.ok(!isAbsoluteURL("http://example.com/test/thing"));
|
||||
assert.ok(!isAbsoluteURL("https://discourse.org/test/thing"));
|
||||
});
|
||||
|
||||
test("getAbsoluteURL", function (assert) {
|
||||
setupURL(null, "https://example.com", "/forum");
|
||||
assert.equal(getAbsoluteURL("/cool/path"), "https://example.com/cool/path");
|
||||
});
|
||||
|
||||
test("withoutPrefix", function (assert) {
|
||||
setPrefix("/eviltrout");
|
||||
assert.equal(withoutPrefix("/eviltrout/hello"), "/hello");
|
||||
assert.equal(withoutPrefix("/eviltrout/"), "/");
|
||||
assert.equal(withoutPrefix("/eviltrout"), "");
|
||||
|
||||
setPrefix("");
|
||||
assert.equal(withoutPrefix("/eviltrout/hello"), "/eviltrout/hello");
|
||||
assert.equal(withoutPrefix("/eviltrout"), "/eviltrout");
|
||||
assert.equal(withoutPrefix("/"), "/");
|
||||
|
||||
setPrefix(null);
|
||||
assert.equal(withoutPrefix("/eviltrout/hello"), "/eviltrout/hello");
|
||||
assert.equal(withoutPrefix("/eviltrout"), "/eviltrout");
|
||||
assert.equal(withoutPrefix("/"), "/");
|
||||
});
|
||||
|
||||
test("getURL with empty paths", function (assert) {
|
||||
setupURL(null, "https://example.com", "/");
|
||||
assert.equal(getURL("/"), "/");
|
||||
assert.equal(getURL(""), "");
|
||||
setupURL(null, "https://example.com", "");
|
||||
assert.equal(getURL("/"), "/");
|
||||
assert.equal(getURL(""), "");
|
||||
setupURL(null, "https://example.com", undefined);
|
||||
assert.equal(getURL("/"), "/");
|
||||
assert.equal(getURL(""), "");
|
||||
});
|
||||
|
||||
test("getURL on subfolder install", function (assert) {
|
||||
setupURL(null, "", "/forum");
|
||||
assert.equal(getURL("/"), "/forum/", "root url has subfolder");
|
||||
assert.equal(
|
||||
getURL("/u/neil"),
|
||||
"/forum/u/neil",
|
||||
"relative url has subfolder"
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
getURL(""),
|
||||
"/forum",
|
||||
"relative url has subfolder without trailing slash"
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
getURL("/svg-sprite/forum.example.com/svg-sprite.js"),
|
||||
"/forum/svg-sprite/forum.example.com/svg-sprite.js",
|
||||
"works when the url has the prefix in the middle"
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
getURL("/forum/t/123"),
|
||||
"/forum/t/123",
|
||||
"does not prefix if the URL is already prefixed"
|
||||
);
|
||||
});
|
||||
|
||||
test("getURLWithCDN on subfolder install with S3", function (assert) {
|
||||
setupURL(null, "", "/forum");
|
||||
setupS3CDN(
|
||||
"//test.s3-us-west-1.amazonaws.com/site",
|
||||
"https://awesome.cdn/site"
|
||||
);
|
||||
|
||||
let url = "//test.s3-us-west-1.amazonaws.com/site/forum/awesome.png";
|
||||
let expected = "https://awesome.cdn/site/forum/awesome.png";
|
||||
|
||||
assert.equal(getURLWithCDN(url), expected, "at correct path");
|
||||
test("getAbsoluteURL", function (assert) {
|
||||
setupURL(null, "https://example.com", "/forum");
|
||||
assert.equal(getAbsoluteURL("/cool/path"), "https://example.com/cool/path");
|
||||
});
|
||||
|
||||
test("withoutPrefix", function (assert) {
|
||||
setPrefix("/eviltrout");
|
||||
assert.equal(withoutPrefix("/eviltrout/hello"), "/hello");
|
||||
assert.equal(withoutPrefix("/eviltrout/"), "/");
|
||||
assert.equal(withoutPrefix("/eviltrout"), "");
|
||||
|
||||
setPrefix("");
|
||||
assert.equal(withoutPrefix("/eviltrout/hello"), "/eviltrout/hello");
|
||||
assert.equal(withoutPrefix("/eviltrout"), "/eviltrout");
|
||||
assert.equal(withoutPrefix("/"), "/");
|
||||
|
||||
setPrefix(null);
|
||||
assert.equal(withoutPrefix("/eviltrout/hello"), "/eviltrout/hello");
|
||||
assert.equal(withoutPrefix("/eviltrout"), "/eviltrout");
|
||||
assert.equal(withoutPrefix("/"), "/");
|
||||
});
|
||||
|
||||
test("getURL with empty paths", function (assert) {
|
||||
setupURL(null, "https://example.com", "/");
|
||||
assert.equal(getURL("/"), "/");
|
||||
assert.equal(getURL(""), "");
|
||||
setupURL(null, "https://example.com", "");
|
||||
assert.equal(getURL("/"), "/");
|
||||
assert.equal(getURL(""), "");
|
||||
setupURL(null, "https://example.com", undefined);
|
||||
assert.equal(getURL("/"), "/");
|
||||
assert.equal(getURL(""), "");
|
||||
});
|
||||
|
||||
test("getURL on subfolder install", function (assert) {
|
||||
setupURL(null, "", "/forum");
|
||||
assert.equal(getURL("/"), "/forum/", "root url has subfolder");
|
||||
assert.equal(
|
||||
getURL("/u/neil"),
|
||||
"/forum/u/neil",
|
||||
"relative url has subfolder"
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
getURL(""),
|
||||
"/forum",
|
||||
"relative url has subfolder without trailing slash"
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
getURL("/svg-sprite/forum.example.com/svg-sprite.js"),
|
||||
"/forum/svg-sprite/forum.example.com/svg-sprite.js",
|
||||
"works when the url has the prefix in the middle"
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
getURL("/forum/t/123"),
|
||||
"/forum/t/123",
|
||||
"does not prefix if the URL is already prefixed"
|
||||
);
|
||||
});
|
||||
|
||||
test("getURLWithCDN on subfolder install with S3", function (assert) {
|
||||
setupURL(null, "", "/forum");
|
||||
setupS3CDN(
|
||||
"//test.s3-us-west-1.amazonaws.com/site",
|
||||
"https://awesome.cdn/site"
|
||||
);
|
||||
|
||||
let url = "//test.s3-us-west-1.amazonaws.com/site/forum/awesome.png";
|
||||
let expected = "https://awesome.cdn/site/forum/awesome.png";
|
||||
|
||||
assert.equal(getURLWithCDN(url), expected, "at correct path");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,48 +2,48 @@ import highlightSearch, { CLASS_NAME } from "discourse/lib/highlight-search";
|
||||
import { fixture } from "discourse/tests/helpers/qunit-helpers";
|
||||
import { module, test } from "qunit";
|
||||
|
||||
module("lib:highlight-search");
|
||||
module("Unit | Utility | highlight-search", function () {
|
||||
test("highlighting text", function (assert) {
|
||||
fixture().html(
|
||||
`
|
||||
<p>This is some text to highlight</p>
|
||||
`
|
||||
);
|
||||
|
||||
test("highlighting text", function (assert) {
|
||||
fixture().html(
|
||||
`
|
||||
<p>This is some text to highlight</p>
|
||||
`
|
||||
);
|
||||
highlightSearch(fixture()[0], "some text");
|
||||
|
||||
highlightSearch(fixture()[0], "some text");
|
||||
const terms = [];
|
||||
|
||||
const terms = [];
|
||||
fixture(`.${CLASS_NAME}`).each((_, elem) => {
|
||||
terms.push(elem.textContent);
|
||||
});
|
||||
|
||||
fixture(`.${CLASS_NAME}`).each((_, elem) => {
|
||||
terms.push(elem.textContent);
|
||||
assert.equal(
|
||||
terms.join(" "),
|
||||
"some text",
|
||||
"it should highlight the terms correctly"
|
||||
);
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
terms.join(" "),
|
||||
"some text",
|
||||
"it should highlight the terms correctly"
|
||||
);
|
||||
});
|
||||
test("highlighting unicode text", function (assert) {
|
||||
fixture().html(
|
||||
`
|
||||
<p>This is some தமிழ் & русский text to highlight</p>
|
||||
`
|
||||
);
|
||||
|
||||
test("highlighting unicode text", function (assert) {
|
||||
fixture().html(
|
||||
`
|
||||
<p>This is some தமிழ் & русский text to highlight</p>
|
||||
`
|
||||
);
|
||||
highlightSearch(fixture()[0], "தமிழ் & русский");
|
||||
|
||||
highlightSearch(fixture()[0], "தமிழ் & русский");
|
||||
const terms = [];
|
||||
|
||||
const terms = [];
|
||||
fixture(`.${CLASS_NAME}`).each((_, elem) => {
|
||||
terms.push(elem.textContent);
|
||||
});
|
||||
|
||||
fixture(`.${CLASS_NAME}`).each((_, elem) => {
|
||||
terms.push(elem.textContent);
|
||||
assert.equal(
|
||||
terms.join(" "),
|
||||
"தமிழ் & русский",
|
||||
"it should highlight the terms correctly"
|
||||
);
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
terms.join(" "),
|
||||
"தமிழ் & русский",
|
||||
"it should highlight the terms correctly"
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { test, module } from "qunit";
|
||||
import I18n from "I18n";
|
||||
module("lib:i18n", {
|
||||
_locale: I18n.locale,
|
||||
_fallbackLocale: I18n.fallbackLocale,
|
||||
_translations: I18n.translations,
|
||||
_extras: I18n.extras,
|
||||
_pluralizationRules: Object.assign({}, I18n.pluralizationRules),
|
||||
|
||||
beforeEach() {
|
||||
module("Unit | Utility | i18n", function (hooks) {
|
||||
hooks.beforeEach(function () {
|
||||
this._locale = I18n.locale;
|
||||
this._fallbackLocale = I18n.fallbackLocale;
|
||||
this._translations = I18n.translations;
|
||||
this._extras = I18n.extras;
|
||||
this._pluralizationRules = Object.assign({}, I18n.pluralizationRules);
|
||||
|
||||
I18n.locale = "fr";
|
||||
|
||||
I18n.translations = {
|
||||
@@ -88,166 +89,166 @@ module("lib:i18n", {
|
||||
}
|
||||
return "other";
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
afterEach() {
|
||||
hooks.afterEach(function () {
|
||||
I18n.locale = this._locale;
|
||||
I18n.fallbackLocale = this._fallbackLocale;
|
||||
I18n.translations = this._translations;
|
||||
I18n.extras = this._extras;
|
||||
I18n.pluralizationRules = this._pluralizationRules;
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test("defaults", function (assert) {
|
||||
assert.equal(I18n.defaultLocale, "en", "it has English as default locale");
|
||||
assert.ok(I18n.pluralizationRules["en"], "it has English pluralizer");
|
||||
});
|
||||
test("defaults", function (assert) {
|
||||
assert.equal(I18n.defaultLocale, "en", "it has English as default locale");
|
||||
assert.ok(I18n.pluralizationRules["en"], "it has English pluralizer");
|
||||
});
|
||||
|
||||
test("translations", function (assert) {
|
||||
assert.equal(
|
||||
I18n.t("topic.reply.title"),
|
||||
"Répondre",
|
||||
"uses locale translations when they exist"
|
||||
);
|
||||
assert.equal(
|
||||
I18n.t("topic.reply.help"),
|
||||
"begin composing a reply to this topic",
|
||||
"fallbacks to English translations"
|
||||
);
|
||||
assert.equal(
|
||||
I18n.t("hello.world"),
|
||||
"Hello World!",
|
||||
"doesn't break if a key is overriden in a locale"
|
||||
);
|
||||
assert.equal(I18n.t("hello.universe"), "", "allows empty strings");
|
||||
});
|
||||
test("translations", function (assert) {
|
||||
assert.equal(
|
||||
I18n.t("topic.reply.title"),
|
||||
"Répondre",
|
||||
"uses locale translations when they exist"
|
||||
);
|
||||
assert.equal(
|
||||
I18n.t("topic.reply.help"),
|
||||
"begin composing a reply to this topic",
|
||||
"fallbacks to English translations"
|
||||
);
|
||||
assert.equal(
|
||||
I18n.t("hello.world"),
|
||||
"Hello World!",
|
||||
"doesn't break if a key is overriden in a locale"
|
||||
);
|
||||
assert.equal(I18n.t("hello.universe"), "", "allows empty strings");
|
||||
});
|
||||
|
||||
test("extra translations", function (assert) {
|
||||
I18n.locale = "pl_PL";
|
||||
I18n.extras = {
|
||||
en: {
|
||||
admin: {
|
||||
dashboard: {
|
||||
title: "Dashboard",
|
||||
backup_count: {
|
||||
one: "%{count} backup",
|
||||
other: "%{count} backups",
|
||||
test("extra translations", function (assert) {
|
||||
I18n.locale = "pl_PL";
|
||||
I18n.extras = {
|
||||
en: {
|
||||
admin: {
|
||||
dashboard: {
|
||||
title: "Dashboard",
|
||||
backup_count: {
|
||||
one: "%{count} backup",
|
||||
other: "%{count} backups",
|
||||
},
|
||||
},
|
||||
},
|
||||
web_hooks: {
|
||||
events: {
|
||||
incoming: {
|
||||
one: "There is a new event.",
|
||||
other: "There are %{count} new events.",
|
||||
web_hooks: {
|
||||
events: {
|
||||
incoming: {
|
||||
one: "There is a new event.",
|
||||
other: "There are %{count} new events.",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
pl_PL: {
|
||||
admin: {
|
||||
dashboard: {
|
||||
title: "Raporty",
|
||||
},
|
||||
web_hooks: {
|
||||
events: {
|
||||
incoming: {
|
||||
one: "Istnieje nowe wydarzenie",
|
||||
few: "Istnieją %{count} nowe wydarzenia.",
|
||||
many: "Istnieje %{count} nowych wydarzeń.",
|
||||
other: "Istnieje %{count} nowych wydarzeń.",
|
||||
pl_PL: {
|
||||
admin: {
|
||||
dashboard: {
|
||||
title: "Raporty",
|
||||
},
|
||||
web_hooks: {
|
||||
events: {
|
||||
incoming: {
|
||||
one: "Istnieje nowe wydarzenie",
|
||||
few: "Istnieją %{count} nowe wydarzenia.",
|
||||
many: "Istnieje %{count} nowych wydarzeń.",
|
||||
other: "Istnieje %{count} nowych wydarzeń.",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
I18n.pluralizationRules.pl_PL = function (n) {
|
||||
if (n === 1) {
|
||||
return "one";
|
||||
}
|
||||
if (n % 10 >= 2 && n % 10 <= 4) {
|
||||
return "few";
|
||||
}
|
||||
if (n % 10 === 0) {
|
||||
return "many";
|
||||
}
|
||||
return "other";
|
||||
};
|
||||
};
|
||||
I18n.pluralizationRules.pl_PL = function (n) {
|
||||
if (n === 1) {
|
||||
return "one";
|
||||
}
|
||||
if (n % 10 >= 2 && n % 10 <= 4) {
|
||||
return "few";
|
||||
}
|
||||
if (n % 10 === 0) {
|
||||
return "many";
|
||||
}
|
||||
return "other";
|
||||
};
|
||||
|
||||
assert.equal(
|
||||
I18n.t("admin.dashboard.title"),
|
||||
"Raporty",
|
||||
"it uses extra translations when they exists"
|
||||
);
|
||||
assert.equal(
|
||||
I18n.t("admin.dashboard.title"),
|
||||
"Raporty",
|
||||
"it uses extra translations when they exists"
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
I18n.t("admin.web_hooks.events.incoming", { count: 2 }),
|
||||
"Istnieją 2 nowe wydarzenia.",
|
||||
"it uses pluralized extra translation when it exists"
|
||||
);
|
||||
assert.equal(
|
||||
I18n.t("admin.web_hooks.events.incoming", { count: 2 }),
|
||||
"Istnieją 2 nowe wydarzenia.",
|
||||
"it uses pluralized extra translation when it exists"
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
I18n.t("admin.dashboard.backup_count", { count: 2 }),
|
||||
"2 backups",
|
||||
"it falls back to English and uses extra translations when they exists"
|
||||
);
|
||||
});
|
||||
|
||||
test("pluralizations", function (assert) {
|
||||
assert.equal(I18n.t("character_count", { count: 0 }), "0 ZERO");
|
||||
assert.equal(I18n.t("character_count", { count: 1 }), "1 ONE");
|
||||
assert.equal(I18n.t("character_count", { count: 2 }), "2 TWO");
|
||||
assert.equal(I18n.t("character_count", { count: 3 }), "3 FEW");
|
||||
assert.equal(I18n.t("character_count", { count: 10 }), "10 MANY");
|
||||
assert.equal(I18n.t("character_count", { count: 100 }), "100 OTHER");
|
||||
|
||||
assert.equal(I18n.t("word_count", { count: 0 }), "0 words");
|
||||
assert.equal(I18n.t("word_count", { count: 1 }), "1 word");
|
||||
assert.equal(I18n.t("word_count", { count: 2 }), "2 words");
|
||||
assert.equal(I18n.t("word_count", { count: 3 }), "3 words");
|
||||
assert.equal(I18n.t("word_count", { count: 10 }), "10 words");
|
||||
assert.equal(I18n.t("word_count", { count: 100 }), "100 words");
|
||||
});
|
||||
|
||||
test("fallback", function (assert) {
|
||||
assert.equal(
|
||||
I18n.t("days", { count: 1 }),
|
||||
"1 day",
|
||||
"uses fallback locale for missing plural key"
|
||||
);
|
||||
assert.equal(
|
||||
I18n.t("days", { count: 200 }),
|
||||
"200 jours",
|
||||
"uses existing French plural key"
|
||||
);
|
||||
|
||||
I18n.locale = "fr_FOO";
|
||||
I18n.fallbackLocale = "fr";
|
||||
|
||||
assert.equal(
|
||||
I18n.t("topic.reply.title"),
|
||||
"Foo",
|
||||
"uses locale translations when they exist"
|
||||
);
|
||||
assert.equal(
|
||||
I18n.t("topic.share.title"),
|
||||
"Partager",
|
||||
"falls back to fallbackLocale translations when they exist"
|
||||
);
|
||||
assert.equal(
|
||||
I18n.t("topic.reply.help"),
|
||||
"begin composing a reply to this topic",
|
||||
"falls back to English translations"
|
||||
);
|
||||
});
|
||||
|
||||
test("Dollar signs are properly escaped", function (assert) {
|
||||
assert.equal(
|
||||
I18n.t("dollar_sign", {
|
||||
description: "$& $&",
|
||||
}),
|
||||
"Hi $& $&"
|
||||
);
|
||||
assert.equal(
|
||||
I18n.t("admin.dashboard.backup_count", { count: 2 }),
|
||||
"2 backups",
|
||||
"it falls back to English and uses extra translations when they exists"
|
||||
);
|
||||
});
|
||||
|
||||
test("pluralizations", function (assert) {
|
||||
assert.equal(I18n.t("character_count", { count: 0 }), "0 ZERO");
|
||||
assert.equal(I18n.t("character_count", { count: 1 }), "1 ONE");
|
||||
assert.equal(I18n.t("character_count", { count: 2 }), "2 TWO");
|
||||
assert.equal(I18n.t("character_count", { count: 3 }), "3 FEW");
|
||||
assert.equal(I18n.t("character_count", { count: 10 }), "10 MANY");
|
||||
assert.equal(I18n.t("character_count", { count: 100 }), "100 OTHER");
|
||||
|
||||
assert.equal(I18n.t("word_count", { count: 0 }), "0 words");
|
||||
assert.equal(I18n.t("word_count", { count: 1 }), "1 word");
|
||||
assert.equal(I18n.t("word_count", { count: 2 }), "2 words");
|
||||
assert.equal(I18n.t("word_count", { count: 3 }), "3 words");
|
||||
assert.equal(I18n.t("word_count", { count: 10 }), "10 words");
|
||||
assert.equal(I18n.t("word_count", { count: 100 }), "100 words");
|
||||
});
|
||||
|
||||
test("fallback", function (assert) {
|
||||
assert.equal(
|
||||
I18n.t("days", { count: 1 }),
|
||||
"1 day",
|
||||
"uses fallback locale for missing plural key"
|
||||
);
|
||||
assert.equal(
|
||||
I18n.t("days", { count: 200 }),
|
||||
"200 jours",
|
||||
"uses existing French plural key"
|
||||
);
|
||||
|
||||
I18n.locale = "fr_FOO";
|
||||
I18n.fallbackLocale = "fr";
|
||||
|
||||
assert.equal(
|
||||
I18n.t("topic.reply.title"),
|
||||
"Foo",
|
||||
"uses locale translations when they exist"
|
||||
);
|
||||
assert.equal(
|
||||
I18n.t("topic.share.title"),
|
||||
"Partager",
|
||||
"falls back to fallbackLocale translations when they exist"
|
||||
);
|
||||
assert.equal(
|
||||
I18n.t("topic.reply.help"),
|
||||
"begin composing a reply to this topic",
|
||||
"falls back to English translations"
|
||||
);
|
||||
});
|
||||
|
||||
test("Dollar signs are properly escaped", function (assert) {
|
||||
assert.equal(
|
||||
I18n.t("dollar_sign", {
|
||||
description: "$& $&",
|
||||
}),
|
||||
"Hi $& $&"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,23 +5,23 @@ import {
|
||||
convertIconClass,
|
||||
} from "discourse-common/lib/icon-library";
|
||||
|
||||
module("lib:icon-library");
|
||||
module("Unit | Utility | icon-library", function () {
|
||||
test("return icon markup", function (assert) {
|
||||
assert.ok(iconHTML("bars").indexOf('use xlink:href="#bars"') > -1);
|
||||
|
||||
test("return icon markup", function (assert) {
|
||||
assert.ok(iconHTML("bars").indexOf('use xlink:href="#bars"') > -1);
|
||||
const nodeIcon = iconNode("bars");
|
||||
assert.equal(nodeIcon.tagName, "svg");
|
||||
assert.equal(
|
||||
nodeIcon.properties.attributes.class,
|
||||
"fa d-icon d-icon-bars svg-icon svg-node"
|
||||
);
|
||||
});
|
||||
|
||||
const nodeIcon = iconNode("bars");
|
||||
assert.equal(nodeIcon.tagName, "svg");
|
||||
assert.equal(
|
||||
nodeIcon.properties.attributes.class,
|
||||
"fa d-icon d-icon-bars svg-icon svg-node"
|
||||
);
|
||||
});
|
||||
|
||||
test("convert icon names", function (assert) {
|
||||
const fa5Icon = convertIconClass("fab fa-facebook");
|
||||
assert.ok(iconHTML(fa5Icon).indexOf("fab-facebook") > -1, "FA 5 syntax");
|
||||
|
||||
const iconC = convertIconClass(" fab fa-facebook ");
|
||||
assert.ok(iconHTML(iconC).indexOf(" ") === -1, "trims whitespace");
|
||||
test("convert icon names", function (assert) {
|
||||
const fa5Icon = convertIconClass("fab fa-facebook");
|
||||
assert.ok(iconHTML(fa5Icon).indexOf("fab-facebook") > -1, "FA 5 syntax");
|
||||
|
||||
const iconC = convertIconClass(" fab fa-facebook ");
|
||||
assert.ok(iconHTML(iconC).indexOf(" ") === -1, "trims whitespace");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import { test, module } from "qunit";
|
||||
import KeyValueStore from "discourse/lib/key-value-store";
|
||||
|
||||
module("lib:key-value-store");
|
||||
module("Unit | Utility | key-value-store", function () {
|
||||
test("it's able to get the result back from the store", function (assert) {
|
||||
const store = new KeyValueStore("_test");
|
||||
store.set({ key: "bob", value: "uncle" });
|
||||
assert.equal(store.get("bob"), "uncle");
|
||||
});
|
||||
|
||||
test("it's able to get the result back from the store", function (assert) {
|
||||
const store = new KeyValueStore("_test");
|
||||
store.set({ key: "bob", value: "uncle" });
|
||||
assert.equal(store.get("bob"), "uncle");
|
||||
});
|
||||
|
||||
test("is able to nuke the store", function (assert) {
|
||||
const store = new KeyValueStore("_test");
|
||||
store.set({ key: "bob1", value: "uncle" });
|
||||
store.abandonLocal();
|
||||
localStorage.a = 1;
|
||||
assert.equal(store.get("bob1"), void 0);
|
||||
assert.equal(localStorage.a, "1");
|
||||
test("is able to nuke the store", function (assert) {
|
||||
const store = new KeyValueStore("_test");
|
||||
store.set({ key: "bob1", value: "uncle" });
|
||||
store.abandonLocal();
|
||||
localStorage.a = 1;
|
||||
assert.equal(store.get("bob1"), void 0);
|
||||
assert.equal(localStorage.a, "1");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,57 +6,57 @@ import {
|
||||
import { Promise } from "rsvp";
|
||||
import pretender from "discourse/tests/helpers/create-pretender";
|
||||
|
||||
module("lib:link-mentions");
|
||||
module("Unit | Utility | link-mentions", function () {
|
||||
test("linkSeenMentions replaces users and groups", async function (assert) {
|
||||
pretender.get("/u/is_local_username", () => [
|
||||
200,
|
||||
{ "Content-Type": "application/json" },
|
||||
{
|
||||
valid: ["valid_user"],
|
||||
valid_groups: ["valid_group"],
|
||||
mentionable_groups: [
|
||||
{
|
||||
name: "mentionable_group",
|
||||
user_count: 1,
|
||||
},
|
||||
],
|
||||
cannot_see: [],
|
||||
max_users_notified_per_group_mention: 100,
|
||||
},
|
||||
]);
|
||||
|
||||
test("linkSeenMentions replaces users and groups", async function (assert) {
|
||||
pretender.get("/u/is_local_username", () => [
|
||||
200,
|
||||
{ "Content-Type": "application/json" },
|
||||
{
|
||||
valid: ["valid_user"],
|
||||
valid_groups: ["valid_group"],
|
||||
mentionable_groups: [
|
||||
{
|
||||
name: "mentionable_group",
|
||||
user_count: 1,
|
||||
},
|
||||
],
|
||||
cannot_see: [],
|
||||
max_users_notified_per_group_mention: 100,
|
||||
},
|
||||
]);
|
||||
await fetchUnseenMentions([
|
||||
"valid_user",
|
||||
"mentionable_group",
|
||||
"valid_group",
|
||||
"invalid",
|
||||
]);
|
||||
|
||||
await fetchUnseenMentions([
|
||||
"valid_user",
|
||||
"mentionable_group",
|
||||
"valid_group",
|
||||
"invalid",
|
||||
]);
|
||||
let $root = $(`
|
||||
<div>
|
||||
<span class="mention">@invalid</span>
|
||||
<span class="mention">@valid_user</span>
|
||||
<span class="mention">@valid_group</span>
|
||||
<span class="mention">@mentionable_group</span>
|
||||
</div>
|
||||
`);
|
||||
|
||||
let $root = $(`
|
||||
<div>
|
||||
<span class="mention">@invalid</span>
|
||||
<span class="mention">@valid_user</span>
|
||||
<span class="mention">@valid_group</span>
|
||||
<span class="mention">@mentionable_group</span>
|
||||
</div>
|
||||
`);
|
||||
await linkSeenMentions($root);
|
||||
|
||||
await linkSeenMentions($root);
|
||||
// Ember.Test.registerWaiter is not available here, so we are implementing
|
||||
// our own
|
||||
await new Promise((resolve) => {
|
||||
const interval = setInterval(() => {
|
||||
if ($("a", $root).length > 0) {
|
||||
clearInterval(interval);
|
||||
resolve();
|
||||
}
|
||||
}, 500);
|
||||
});
|
||||
|
||||
// Ember.Test.registerWaiter is not available here, so we are implementing
|
||||
// our own
|
||||
await new Promise((resolve) => {
|
||||
const interval = setInterval(() => {
|
||||
if ($("a", $root).length > 0) {
|
||||
clearInterval(interval);
|
||||
resolve();
|
||||
}
|
||||
}, 500);
|
||||
assert.equal($("a", $root)[0].text, "@valid_user");
|
||||
assert.equal($("a", $root)[1].text, "@valid_group");
|
||||
assert.equal($("a.notify", $root).text(), "@mentionable_group");
|
||||
assert.equal($("span.mention", $root)[0].innerHTML, "@invalid");
|
||||
});
|
||||
|
||||
assert.equal($("a", $root)[0].text, "@valid_user");
|
||||
assert.equal($("a", $root)[1].text, "@valid_group");
|
||||
assert.equal($("a.notify", $root).text(), "@mentionable_group");
|
||||
assert.equal($("span.mention", $root)[0].innerHTML, "@invalid");
|
||||
});
|
||||
|
||||
@@ -3,41 +3,41 @@ import { test, module } from "qunit";
|
||||
import { loadScript, cacheBuster } from "discourse/lib/load-script";
|
||||
import { PUBLIC_JS_VERSIONS as jsVersions } from "discourse/lib/public-js-versions";
|
||||
|
||||
module("lib:load-script");
|
||||
module("Unit | Utility | load-script", function () {
|
||||
skip("load with a script tag, and callbacks are only executed after script is loaded", async function (assert) {
|
||||
assert.ok(
|
||||
typeof window.ace === "undefined",
|
||||
"ensures ace is not previously loaded"
|
||||
);
|
||||
|
||||
skip("load with a script tag, and callbacks are only executed after script is loaded", async function (assert) {
|
||||
assert.ok(
|
||||
typeof window.ace === "undefined",
|
||||
"ensures ace is not previously loaded"
|
||||
);
|
||||
const src = "/javascripts/ace/ace.js";
|
||||
|
||||
const src = "/javascripts/ace/ace.js";
|
||||
await loadScript(src);
|
||||
assert.ok(
|
||||
typeof window.ace !== "undefined",
|
||||
"callbacks should only be executed after the script has fully loaded"
|
||||
);
|
||||
});
|
||||
|
||||
await loadScript(src);
|
||||
assert.ok(
|
||||
typeof window.ace !== "undefined",
|
||||
"callbacks should only be executed after the script has fully loaded"
|
||||
);
|
||||
});
|
||||
|
||||
test("works when a value is not present", function (assert) {
|
||||
assert.equal(
|
||||
cacheBuster("/javascripts/my-script.js"),
|
||||
"/javascripts/my-script.js"
|
||||
);
|
||||
assert.equal(
|
||||
cacheBuster("/javascripts/my-project/script.js"),
|
||||
"/javascripts/my-project/script.js"
|
||||
);
|
||||
});
|
||||
|
||||
test("generates URLs with version number in the query params", function (assert) {
|
||||
assert.equal(
|
||||
cacheBuster("/javascripts/pikaday.js"),
|
||||
`/javascripts/${jsVersions["pikaday.js"]}`
|
||||
);
|
||||
assert.equal(
|
||||
cacheBuster("/javascripts/ace/ace.js"),
|
||||
`/javascripts/${jsVersions["ace/ace.js"]}`
|
||||
);
|
||||
test("works when a value is not present", function (assert) {
|
||||
assert.equal(
|
||||
cacheBuster("/javascripts/my-script.js"),
|
||||
"/javascripts/my-script.js"
|
||||
);
|
||||
assert.equal(
|
||||
cacheBuster("/javascripts/my-project/script.js"),
|
||||
"/javascripts/my-project/script.js"
|
||||
);
|
||||
});
|
||||
|
||||
test("generates URLs with version number in the query params", function (assert) {
|
||||
assert.equal(
|
||||
cacheBuster("/javascripts/pikaday.js"),
|
||||
`/javascripts/${jsVersions["pikaday.js"]}`
|
||||
);
|
||||
assert.equal(
|
||||
cacheBuster("/javascripts/ace/ace.js"),
|
||||
`/javascripts/${jsVersions["ace/ace.js"]}`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,28 +15,27 @@ function loadOnebox(element) {
|
||||
});
|
||||
}
|
||||
|
||||
module("lib:oneboxer");
|
||||
module("Unit | Utility | oneboxer", function () {
|
||||
test("load - failed onebox", async function (assert) {
|
||||
let element = document.createElement("A");
|
||||
element.setAttribute("href", "http://somebadurl.com");
|
||||
|
||||
test("load - failed onebox", async function (assert) {
|
||||
let element = document.createElement("A");
|
||||
element.setAttribute("href", "http://somebadurl.com");
|
||||
await loadOnebox(element);
|
||||
|
||||
await loadOnebox(element);
|
||||
assert.equal(
|
||||
failedCache["http://somebadurl.com"],
|
||||
true,
|
||||
"stores the url as failed in a cache"
|
||||
);
|
||||
assert.equal(
|
||||
loadOnebox(element),
|
||||
undefined,
|
||||
"it returns early for a failed cache"
|
||||
);
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
failedCache["http://somebadurl.com"],
|
||||
true,
|
||||
"stores the url as failed in a cache"
|
||||
);
|
||||
assert.equal(
|
||||
loadOnebox(element),
|
||||
undefined,
|
||||
"it returns early for a failed cache"
|
||||
);
|
||||
});
|
||||
|
||||
test("load - successful onebox", async function (assert) {
|
||||
const html = `
|
||||
test("load - successful onebox", async function (assert) {
|
||||
const html = `
|
||||
<aside class="onebox allowlistedgeneric">
|
||||
<header class="source">
|
||||
<a href="http://test.com/somepage" target="_blank">test.com</a>
|
||||
@@ -49,21 +48,22 @@ test("load - successful onebox", async function (assert) {
|
||||
<div class="onebox-metadata"></div>
|
||||
<div style="clear: both"></div>
|
||||
</aside>
|
||||
`;
|
||||
`;
|
||||
|
||||
let element = document.createElement("A");
|
||||
element.setAttribute("href", "http://somegoodurl.com");
|
||||
let element = document.createElement("A");
|
||||
element.setAttribute("href", "http://somegoodurl.com");
|
||||
|
||||
await loadOnebox(element);
|
||||
await loadOnebox(element);
|
||||
|
||||
assert.equal(
|
||||
localCache["http://somegoodurl.com"].prop("outerHTML"),
|
||||
stringToHTML(html).outerHTML,
|
||||
"stores the html of the onebox in a local cache"
|
||||
);
|
||||
assert.equal(
|
||||
loadOnebox(element),
|
||||
html.trim(),
|
||||
"it returns the html from the cache"
|
||||
);
|
||||
assert.equal(
|
||||
localCache["http://somegoodurl.com"].prop("outerHTML"),
|
||||
stringToHTML(html).outerHTML,
|
||||
"stores the html of the onebox in a local cache"
|
||||
);
|
||||
assert.equal(
|
||||
loadOnebox(element),
|
||||
html.trim(),
|
||||
"it returns the html from the cache"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import { test, module } from "qunit";
|
||||
import { parseBBCodeTag } from "pretty-text/engines/discourse-markdown/bbcode-block";
|
||||
|
||||
module("Unit | Utility | parseBBCodeTag", function () {
|
||||
test("block with multiple quoted attributes", function (assert) {
|
||||
const parsed = parseBBCodeTag('[test one="foo" two="bar bar"]', 0, 30);
|
||||
|
||||
assert.equal(parsed.tag, "test");
|
||||
assert.equal(parsed.attrs.one, "foo");
|
||||
assert.equal(parsed.attrs.two, "bar bar");
|
||||
});
|
||||
});
|
||||
@@ -2,53 +2,56 @@ import { test, module } from "qunit";
|
||||
import PreloadStore from "discourse/lib/preload-store";
|
||||
import { Promise } from "rsvp";
|
||||
|
||||
module("preload-store", {
|
||||
beforeEach() {
|
||||
module("Unit | Utility | preload-store", function (hooks) {
|
||||
hooks.beforeEach(function () {
|
||||
PreloadStore.store("bane", "evil");
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test("get", function (assert) {
|
||||
assert.blank(PreloadStore.get("joker"), "returns blank for a missing key");
|
||||
assert.equal(
|
||||
PreloadStore.get("bane"),
|
||||
"evil",
|
||||
"returns the value for that key"
|
||||
);
|
||||
});
|
||||
test("get", function (assert) {
|
||||
assert.blank(PreloadStore.get("joker"), "returns blank for a missing key");
|
||||
assert.equal(
|
||||
PreloadStore.get("bane"),
|
||||
"evil",
|
||||
"returns the value for that key"
|
||||
);
|
||||
});
|
||||
|
||||
test("remove", function (assert) {
|
||||
PreloadStore.remove("bane");
|
||||
assert.blank(PreloadStore.get("bane"), "removes the value if the key exists");
|
||||
});
|
||||
test("remove", function (assert) {
|
||||
PreloadStore.remove("bane");
|
||||
assert.blank(
|
||||
PreloadStore.get("bane"),
|
||||
"removes the value if the key exists"
|
||||
);
|
||||
});
|
||||
|
||||
test("getAndRemove returns a promise that resolves to null", async function (assert) {
|
||||
assert.blank(await PreloadStore.getAndRemove("joker"));
|
||||
});
|
||||
test("getAndRemove returns a promise that resolves to null", async function (assert) {
|
||||
assert.blank(await PreloadStore.getAndRemove("joker"));
|
||||
});
|
||||
|
||||
test("getAndRemove returns a promise that resolves to the result of the finder", async function (assert) {
|
||||
const finder = () => "batdance";
|
||||
const result = await PreloadStore.getAndRemove("joker", finder);
|
||||
test("getAndRemove returns a promise that resolves to the result of the finder", async function (assert) {
|
||||
const finder = () => "batdance";
|
||||
const result = await PreloadStore.getAndRemove("joker", finder);
|
||||
|
||||
assert.equal(result, "batdance");
|
||||
});
|
||||
assert.equal(result, "batdance");
|
||||
});
|
||||
|
||||
test("getAndRemove returns a promise that resolves to the result of the finder's promise", async function (assert) {
|
||||
const finder = () => Promise.resolve("hahahah");
|
||||
const result = await PreloadStore.getAndRemove("joker", finder);
|
||||
test("getAndRemove returns a promise that resolves to the result of the finder's promise", async function (assert) {
|
||||
const finder = () => Promise.resolve("hahahah");
|
||||
const result = await PreloadStore.getAndRemove("joker", finder);
|
||||
|
||||
assert.equal(result, "hahahah");
|
||||
});
|
||||
assert.equal(result, "hahahah");
|
||||
});
|
||||
|
||||
test("returns a promise that rejects with the result of the finder's rejected promise", async function (assert) {
|
||||
const finder = () => Promise.reject("error");
|
||||
test("returns a promise that rejects with the result of the finder's rejected promise", async function (assert) {
|
||||
const finder = () => Promise.reject("error");
|
||||
|
||||
await PreloadStore.getAndRemove("joker", finder).catch((result) => {
|
||||
assert.equal(result, "error");
|
||||
await PreloadStore.getAndRemove("joker", finder).catch((result) => {
|
||||
assert.equal(result, "error");
|
||||
});
|
||||
});
|
||||
|
||||
test("returns a promise that resolves to 'evil'", async function (assert) {
|
||||
const result = await PreloadStore.getAndRemove("bane");
|
||||
assert.equal(result, "evil");
|
||||
});
|
||||
});
|
||||
|
||||
test("returns a promise that resolves to 'evil'", async function (assert) {
|
||||
const result = await PreloadStore.getAndRemove("bane");
|
||||
assert.equal(result, "evil");
|
||||
});
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,203 +2,212 @@ import { test, module } from "qunit";
|
||||
import PrettyText, { buildOptions } from "pretty-text/pretty-text";
|
||||
import { hrefAllowed } from "pretty-text/sanitizer";
|
||||
|
||||
module("lib:sanitizer");
|
||||
module("Unit | Utility | sanitizer", function () {
|
||||
test("sanitize", function (assert) {
|
||||
const pt = new PrettyText(
|
||||
buildOptions({
|
||||
siteSettings: {
|
||||
allowed_iframes:
|
||||
"https://www.google.com/maps/embed?|https://www.openstreetmap.org/export/embed.html?",
|
||||
},
|
||||
})
|
||||
);
|
||||
const cooked = (input, expected, text) =>
|
||||
assert.equal(pt.cook(input), expected.replace(/\/>/g, ">"), text);
|
||||
|
||||
test("sanitize", function (assert) {
|
||||
const pt = new PrettyText(
|
||||
buildOptions({
|
||||
siteSettings: {
|
||||
allowed_iframes:
|
||||
"https://www.google.com/maps/embed?|https://www.openstreetmap.org/export/embed.html?",
|
||||
},
|
||||
})
|
||||
);
|
||||
const cooked = (input, expected, text) =>
|
||||
assert.equal(pt.cook(input), expected.replace(/\/>/g, ">"), text);
|
||||
assert.equal(
|
||||
pt.sanitize('<i class="fa-bug fa-spin">bug</i>'),
|
||||
"<i>bug</i>"
|
||||
);
|
||||
assert.equal(
|
||||
pt.sanitize("<div><script>alert('hi');</script></div>"),
|
||||
"<div></div>"
|
||||
);
|
||||
assert.equal(
|
||||
pt.sanitize("<div><p class=\"funky\" wrong='1'>hello</p></div>"),
|
||||
"<div><p>hello</p></div>"
|
||||
);
|
||||
assert.equal(pt.sanitize("<3 <3"), "<3 <3");
|
||||
assert.equal(pt.sanitize("<_<"), "<_<");
|
||||
|
||||
assert.equal(pt.sanitize('<i class="fa-bug fa-spin">bug</i>'), "<i>bug</i>");
|
||||
assert.equal(
|
||||
pt.sanitize("<div><script>alert('hi');</script></div>"),
|
||||
"<div></div>"
|
||||
);
|
||||
assert.equal(
|
||||
pt.sanitize("<div><p class=\"funky\" wrong='1'>hello</p></div>"),
|
||||
"<div><p>hello</p></div>"
|
||||
);
|
||||
assert.equal(pt.sanitize("<3 <3"), "<3 <3");
|
||||
assert.equal(pt.sanitize("<_<"), "<_<");
|
||||
cooked(
|
||||
"hello<script>alert(42)</script>",
|
||||
"<p>hello</p>",
|
||||
"it sanitizes while cooking"
|
||||
);
|
||||
|
||||
cooked(
|
||||
"hello<script>alert(42)</script>",
|
||||
"<p>hello</p>",
|
||||
"it sanitizes while cooking"
|
||||
);
|
||||
cooked(
|
||||
"<a href='http://disneyland.disney.go.com/'>disney</a> <a href='http://reddit.com'>reddit</a>",
|
||||
'<p><a href="http://disneyland.disney.go.com/">disney</a> <a href="http://reddit.com">reddit</a></p>',
|
||||
"we can embed proper links"
|
||||
);
|
||||
|
||||
cooked(
|
||||
"<a href='http://disneyland.disney.go.com/'>disney</a> <a href='http://reddit.com'>reddit</a>",
|
||||
'<p><a href="http://disneyland.disney.go.com/">disney</a> <a href="http://reddit.com">reddit</a></p>',
|
||||
"we can embed proper links"
|
||||
);
|
||||
cooked("<center>hello</center>", "hello", "it does not allow centering");
|
||||
cooked(
|
||||
"<blockquote>a\n</blockquote>\n",
|
||||
"<blockquote>a\n</blockquote>",
|
||||
"it does not double sanitize"
|
||||
);
|
||||
|
||||
cooked("<center>hello</center>", "hello", "it does not allow centering");
|
||||
cooked(
|
||||
"<blockquote>a\n</blockquote>\n",
|
||||
"<blockquote>a\n</blockquote>",
|
||||
"it does not double sanitize"
|
||||
);
|
||||
cooked(
|
||||
'<iframe src="http://discourse.org" width="100" height="42"></iframe>',
|
||||
"",
|
||||
"it does not allow most iframes"
|
||||
);
|
||||
|
||||
cooked(
|
||||
'<iframe src="http://discourse.org" width="100" height="42"></iframe>',
|
||||
"",
|
||||
"it does not allow most iframes"
|
||||
);
|
||||
cooked(
|
||||
'<iframe src="https://www.google.com/maps/embed?pb=!1m10!1m8!1m3!1d2624.9983685732213!2d2.29432085!3d48.85824149999999!3m2!1i1024!2i768!4f13.1!5e0!3m2!1sen!2s!4v1385737436368" width="100" height="42"></iframe>',
|
||||
'<iframe src="https://www.google.com/maps/embed?pb=!1m10!1m8!1m3!1d2624.9983685732213!2d2.29432085!3d48.85824149999999!3m2!1i1024!2i768!4f13.1!5e0!3m2!1sen!2s!4v1385737436368" width="100" height="42"></iframe>',
|
||||
"it allows iframe to google maps"
|
||||
);
|
||||
|
||||
cooked(
|
||||
'<iframe src="https://www.google.com/maps/embed?pb=!1m10!1m8!1m3!1d2624.9983685732213!2d2.29432085!3d48.85824149999999!3m2!1i1024!2i768!4f13.1!5e0!3m2!1sen!2s!4v1385737436368" width="100" height="42"></iframe>',
|
||||
'<iframe src="https://www.google.com/maps/embed?pb=!1m10!1m8!1m3!1d2624.9983685732213!2d2.29432085!3d48.85824149999999!3m2!1i1024!2i768!4f13.1!5e0!3m2!1sen!2s!4v1385737436368" width="100" height="42"></iframe>',
|
||||
"it allows iframe to google maps"
|
||||
);
|
||||
cooked(
|
||||
'<iframe width="425" height="350" frameborder="0" marginheight="0" marginwidth="0" src="https://www.openstreetmap.org/export/embed.html?bbox=22.49454975128174%2C51.220338322410775%2C22.523088455200195%2C51.23345342732931&layer=mapnik"></iframe>',
|
||||
'<iframe width="425" height="350" frameborder="0" marginheight="0" marginwidth="0" src="https://www.openstreetmap.org/export/embed.html?bbox=22.49454975128174%2C51.220338322410775%2C22.523088455200195%2C51.23345342732931&layer=mapnik"></iframe>',
|
||||
"it allows iframe to OpenStreetMap"
|
||||
);
|
||||
|
||||
cooked(
|
||||
'<iframe width="425" height="350" frameborder="0" marginheight="0" marginwidth="0" src="https://www.openstreetmap.org/export/embed.html?bbox=22.49454975128174%2C51.220338322410775%2C22.523088455200195%2C51.23345342732931&layer=mapnik"></iframe>',
|
||||
'<iframe width="425" height="350" frameborder="0" marginheight="0" marginwidth="0" src="https://www.openstreetmap.org/export/embed.html?bbox=22.49454975128174%2C51.220338322410775%2C22.523088455200195%2C51.23345342732931&layer=mapnik"></iframe>',
|
||||
"it allows iframe to OpenStreetMap"
|
||||
);
|
||||
assert.equal(pt.sanitize("<textarea>hullo</textarea>"), "hullo");
|
||||
assert.equal(pt.sanitize("<button>press me!</button>"), "press me!");
|
||||
assert.equal(pt.sanitize("<canvas>draw me!</canvas>"), "draw me!");
|
||||
assert.equal(pt.sanitize("<progress>hello"), "hello");
|
||||
assert.equal(pt.sanitize("<mark>highlight</mark>"), "highlight");
|
||||
|
||||
assert.equal(pt.sanitize("<textarea>hullo</textarea>"), "hullo");
|
||||
assert.equal(pt.sanitize("<button>press me!</button>"), "press me!");
|
||||
assert.equal(pt.sanitize("<canvas>draw me!</canvas>"), "draw me!");
|
||||
assert.equal(pt.sanitize("<progress>hello"), "hello");
|
||||
assert.equal(pt.sanitize("<mark>highlight</mark>"), "highlight");
|
||||
cooked(
|
||||
"[the answer](javascript:alert(42))",
|
||||
"<p>[the answer](javascript:alert(42))</p>",
|
||||
"it prevents XSS"
|
||||
);
|
||||
|
||||
cooked(
|
||||
"[the answer](javascript:alert(42))",
|
||||
"<p>[the answer](javascript:alert(42))</p>",
|
||||
"it prevents XSS"
|
||||
);
|
||||
cooked(
|
||||
'<i class="fa fa-bug fa-spin" style="font-size:600%"></i>\n<!-- -->',
|
||||
"<p><i></i></p>",
|
||||
"it doesn't circumvent XSS with comments"
|
||||
);
|
||||
|
||||
cooked(
|
||||
'<i class="fa fa-bug fa-spin" style="font-size:600%"></i>\n<!-- -->',
|
||||
"<p><i></i></p>",
|
||||
"it doesn't circumvent XSS with comments"
|
||||
);
|
||||
cooked(
|
||||
'<span class="-bbcode-s fa fa-spin">a</span>',
|
||||
"<p><span>a</span></p>",
|
||||
"it sanitizes spans"
|
||||
);
|
||||
cooked(
|
||||
'<span class="fa fa-spin -bbcode-s">a</span>',
|
||||
"<p><span>a</span></p>",
|
||||
"it sanitizes spans"
|
||||
);
|
||||
cooked(
|
||||
'<span class="bbcode-s">a</span>',
|
||||
'<p><span class="bbcode-s">a</span></p>',
|
||||
"it sanitizes spans"
|
||||
);
|
||||
|
||||
cooked(
|
||||
'<span class="-bbcode-s fa fa-spin">a</span>',
|
||||
"<p><span>a</span></p>",
|
||||
"it sanitizes spans"
|
||||
);
|
||||
cooked(
|
||||
'<span class="fa fa-spin -bbcode-s">a</span>',
|
||||
"<p><span>a</span></p>",
|
||||
"it sanitizes spans"
|
||||
);
|
||||
cooked(
|
||||
'<span class="bbcode-s">a</span>',
|
||||
'<p><span class="bbcode-s">a</span></p>',
|
||||
"it sanitizes spans"
|
||||
);
|
||||
cooked(
|
||||
"<kbd>Ctrl</kbd>+<kbd>C</kbd>",
|
||||
"<p><kbd>Ctrl</kbd>+<kbd>C</kbd></p>"
|
||||
);
|
||||
cooked(
|
||||
"it has been <strike>1 day</strike> 0 days since our last test failure",
|
||||
"<p>it has been <strike>1 day</strike> 0 days since our last test failure</p>"
|
||||
);
|
||||
cooked(
|
||||
`it has been <s>1 day</s> 0 days since our last test failure`,
|
||||
`<p>it has been <s>1 day</s> 0 days since our last test failure</p>`
|
||||
);
|
||||
|
||||
cooked("<kbd>Ctrl</kbd>+<kbd>C</kbd>", "<p><kbd>Ctrl</kbd>+<kbd>C</kbd></p>");
|
||||
cooked(
|
||||
"it has been <strike>1 day</strike> 0 days since our last test failure",
|
||||
"<p>it has been <strike>1 day</strike> 0 days since our last test failure</p>"
|
||||
);
|
||||
cooked(
|
||||
`it has been <s>1 day</s> 0 days since our last test failure`,
|
||||
`<p>it has been <s>1 day</s> 0 days since our last test failure</p>`
|
||||
);
|
||||
cooked(
|
||||
`<div align="center">hello</div>`,
|
||||
`<div align="center">hello</div>`
|
||||
);
|
||||
|
||||
cooked(`<div align="center">hello</div>`, `<div align="center">hello</div>`);
|
||||
cooked(
|
||||
`1 + 1 is <del>3</del> <ins>2</ins>`,
|
||||
`<p>1 + 1 is <del>3</del> <ins>2</ins></p>`
|
||||
);
|
||||
cooked(
|
||||
`<abbr title="JavaScript">JS</abbr>`,
|
||||
`<p><abbr title="JavaScript">JS</abbr></p>`
|
||||
);
|
||||
cooked(
|
||||
`<dl><dt>Forum</dt><dd>Software</dd></dl>`,
|
||||
`<dl><dt>Forum</dt><dd>Software</dd></dl>`
|
||||
);
|
||||
cooked(
|
||||
`<sup>high</sup> <sub>low</sub> <big>HUGE</big>`,
|
||||
`<p><sup>high</sup> <sub>low</sub> <big>HUGE</big></p>`
|
||||
);
|
||||
|
||||
cooked(
|
||||
`1 + 1 is <del>3</del> <ins>2</ins>`,
|
||||
`<p>1 + 1 is <del>3</del> <ins>2</ins></p>`
|
||||
);
|
||||
cooked(
|
||||
`<abbr title="JavaScript">JS</abbr>`,
|
||||
`<p><abbr title="JavaScript">JS</abbr></p>`
|
||||
);
|
||||
cooked(
|
||||
`<dl><dt>Forum</dt><dd>Software</dd></dl>`,
|
||||
`<dl><dt>Forum</dt><dd>Software</dd></dl>`
|
||||
);
|
||||
cooked(
|
||||
`<sup>high</sup> <sub>low</sub> <big>HUGE</big>`,
|
||||
`<p><sup>high</sup> <sub>low</sub> <big>HUGE</big></p>`
|
||||
);
|
||||
cooked(`<div dir="rtl">RTL text</div>`, `<div dir="rtl">RTL text</div>`);
|
||||
});
|
||||
|
||||
cooked(`<div dir="rtl">RTL text</div>`, `<div dir="rtl">RTL text</div>`);
|
||||
});
|
||||
|
||||
test("ids on headings", function (assert) {
|
||||
const pt = new PrettyText(buildOptions({ siteSettings: {} }));
|
||||
assert.equal(pt.sanitize("<h3>Test Heading</h3>"), "<h3>Test Heading</h3>");
|
||||
assert.equal(
|
||||
pt.sanitize(`<h1 id="heading--test">Test Heading</h1>`),
|
||||
`<h1 id="heading--test">Test Heading</h1>`
|
||||
);
|
||||
assert.equal(
|
||||
pt.sanitize(`<h2 id="heading--cool">Test Heading</h2>`),
|
||||
`<h2 id="heading--cool">Test Heading</h2>`
|
||||
);
|
||||
assert.equal(
|
||||
pt.sanitize(`<h3 id="heading--dashed-name">Test Heading</h3>`),
|
||||
`<h3 id="heading--dashed-name">Test Heading</h3>`
|
||||
);
|
||||
assert.equal(
|
||||
pt.sanitize(`<h4 id="heading--underscored_name">Test Heading</h4>`),
|
||||
`<h4 id="heading--underscored_name">Test Heading</h4>`
|
||||
);
|
||||
assert.equal(
|
||||
pt.sanitize(`<h5 id="heading--trout">Test Heading</h5>`),
|
||||
`<h5 id="heading--trout">Test Heading</h5>`
|
||||
);
|
||||
assert.equal(
|
||||
pt.sanitize(`<h6 id="heading--discourse">Test Heading</h6>`),
|
||||
`<h6 id="heading--discourse">Test Heading</h6>`
|
||||
);
|
||||
});
|
||||
|
||||
test("poorly formed ids on headings", function (assert) {
|
||||
let pt = new PrettyText(buildOptions({ siteSettings: {} }));
|
||||
assert.equal(
|
||||
pt.sanitize(`<h1 id="evil-trout">Test Heading</h1>`),
|
||||
`<h1>Test Heading</h1>`
|
||||
);
|
||||
assert.equal(
|
||||
pt.sanitize(`<h1 id="heading--">Test Heading</h1>`),
|
||||
`<h1>Test Heading</h1>`
|
||||
);
|
||||
assert.equal(
|
||||
pt.sanitize(`<h1 id="heading--with space">Test Heading</h1>`),
|
||||
`<h1>Test Heading</h1>`
|
||||
);
|
||||
assert.equal(
|
||||
pt.sanitize(`<h1 id="heading--with*char">Test Heading</h1>`),
|
||||
`<h1>Test Heading</h1>`
|
||||
);
|
||||
assert.equal(
|
||||
pt.sanitize(`<h1 id="heading--">Test Heading</h1>`),
|
||||
`<h1>Test Heading</h1>`
|
||||
);
|
||||
assert.equal(
|
||||
pt.sanitize(`<h1 id="test-heading--cool">Test Heading</h1>`),
|
||||
`<h1>Test Heading</h1>`
|
||||
);
|
||||
});
|
||||
|
||||
test("urlAllowed", function (assert) {
|
||||
const allowed = (url, msg) => assert.equal(hrefAllowed(url), url, msg);
|
||||
|
||||
allowed("/foo/bar.html", "allows relative urls");
|
||||
allowed("http://eviltrout.com/evil/trout", "allows full urls");
|
||||
allowed("https://eviltrout.com/evil/trout", "allows https urls");
|
||||
allowed("//eviltrout.com/evil/trout", "allows protocol relative urls");
|
||||
|
||||
assert.equal(
|
||||
hrefAllowed("http://google.com/test'onmouseover=alert('XSS!');//.swf"),
|
||||
"http://google.com/test%27onmouseover=alert(%27XSS!%27);//.swf",
|
||||
"escape single quotes"
|
||||
);
|
||||
test("ids on headings", function (assert) {
|
||||
const pt = new PrettyText(buildOptions({ siteSettings: {} }));
|
||||
assert.equal(pt.sanitize("<h3>Test Heading</h3>"), "<h3>Test Heading</h3>");
|
||||
assert.equal(
|
||||
pt.sanitize(`<h1 id="heading--test">Test Heading</h1>`),
|
||||
`<h1 id="heading--test">Test Heading</h1>`
|
||||
);
|
||||
assert.equal(
|
||||
pt.sanitize(`<h2 id="heading--cool">Test Heading</h2>`),
|
||||
`<h2 id="heading--cool">Test Heading</h2>`
|
||||
);
|
||||
assert.equal(
|
||||
pt.sanitize(`<h3 id="heading--dashed-name">Test Heading</h3>`),
|
||||
`<h3 id="heading--dashed-name">Test Heading</h3>`
|
||||
);
|
||||
assert.equal(
|
||||
pt.sanitize(`<h4 id="heading--underscored_name">Test Heading</h4>`),
|
||||
`<h4 id="heading--underscored_name">Test Heading</h4>`
|
||||
);
|
||||
assert.equal(
|
||||
pt.sanitize(`<h5 id="heading--trout">Test Heading</h5>`),
|
||||
`<h5 id="heading--trout">Test Heading</h5>`
|
||||
);
|
||||
assert.equal(
|
||||
pt.sanitize(`<h6 id="heading--discourse">Test Heading</h6>`),
|
||||
`<h6 id="heading--discourse">Test Heading</h6>`
|
||||
);
|
||||
});
|
||||
|
||||
test("poorly formed ids on headings", function (assert) {
|
||||
let pt = new PrettyText(buildOptions({ siteSettings: {} }));
|
||||
assert.equal(
|
||||
pt.sanitize(`<h1 id="evil-trout">Test Heading</h1>`),
|
||||
`<h1>Test Heading</h1>`
|
||||
);
|
||||
assert.equal(
|
||||
pt.sanitize(`<h1 id="heading--">Test Heading</h1>`),
|
||||
`<h1>Test Heading</h1>`
|
||||
);
|
||||
assert.equal(
|
||||
pt.sanitize(`<h1 id="heading--with space">Test Heading</h1>`),
|
||||
`<h1>Test Heading</h1>`
|
||||
);
|
||||
assert.equal(
|
||||
pt.sanitize(`<h1 id="heading--with*char">Test Heading</h1>`),
|
||||
`<h1>Test Heading</h1>`
|
||||
);
|
||||
assert.equal(
|
||||
pt.sanitize(`<h1 id="heading--">Test Heading</h1>`),
|
||||
`<h1>Test Heading</h1>`
|
||||
);
|
||||
assert.equal(
|
||||
pt.sanitize(`<h1 id="test-heading--cool">Test Heading</h1>`),
|
||||
`<h1>Test Heading</h1>`
|
||||
);
|
||||
});
|
||||
|
||||
test("urlAllowed", function (assert) {
|
||||
const allowed = (url, msg) => assert.equal(hrefAllowed(url), url, msg);
|
||||
|
||||
allowed("/foo/bar.html", "allows relative urls");
|
||||
allowed("http://eviltrout.com/evil/trout", "allows full urls");
|
||||
allowed("https://eviltrout.com/evil/trout", "allows https urls");
|
||||
allowed("//eviltrout.com/evil/trout", "allows protocol relative urls");
|
||||
|
||||
assert.equal(
|
||||
hrefAllowed("http://google.com/test'onmouseover=alert('XSS!');//.swf"),
|
||||
"http://google.com/test%27onmouseover=alert(%27XSS!%27);//.swf",
|
||||
"escape single quotes"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
import { test, module } from "qunit";
|
||||
import ScreenTrack from "discourse/lib/screen-track";
|
||||
|
||||
module("lib:screen-track");
|
||||
module("Unit | Utility | screen-track", function () {
|
||||
test("consolidateTimings", function (assert) {
|
||||
const tracker = new ScreenTrack();
|
||||
|
||||
test("consolidateTimings", function (assert) {
|
||||
const tracker = new ScreenTrack();
|
||||
tracker.consolidateTimings({ 1: 10, 2: 5 }, 10, 1);
|
||||
tracker.consolidateTimings({ 1: 5, 3: 1 }, 3, 1);
|
||||
const consolidated = tracker.consolidateTimings({ 1: 5, 3: 1 }, 3, 2);
|
||||
|
||||
tracker.consolidateTimings({ 1: 10, 2: 5 }, 10, 1);
|
||||
tracker.consolidateTimings({ 1: 5, 3: 1 }, 3, 1);
|
||||
const consolidated = tracker.consolidateTimings({ 1: 5, 3: 1 }, 3, 2);
|
||||
|
||||
assert.deepEqual(
|
||||
consolidated,
|
||||
[
|
||||
{ timings: { 1: 15, 2: 5, 3: 1 }, topicTime: 13, topicId: 1 },
|
||||
{ timings: { 1: 5, 3: 1 }, topicTime: 3, topicId: 2 },
|
||||
],
|
||||
"expecting consolidated timings to match correctly"
|
||||
);
|
||||
assert.deepEqual(
|
||||
consolidated,
|
||||
[
|
||||
{ timings: { 1: 15, 2: 5, 3: 1 }, topicTime: 13, topicId: 1 },
|
||||
{ timings: { 1: 5, 3: 1 }, topicTime: 3, topicId: 2 },
|
||||
],
|
||||
"expecting consolidated timings to match correctly"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,58 +5,58 @@ import {
|
||||
searchContextDescription,
|
||||
} from "discourse/lib/search";
|
||||
|
||||
module("lib:search");
|
||||
module("Unit | Utility | search", function () {
|
||||
test("unescapesEmojisInBlurbs", function (assert) {
|
||||
const source = {
|
||||
posts: [
|
||||
{
|
||||
id: 160,
|
||||
username: "pmusaraj",
|
||||
avatar_template: "/user_avatar/localhost/pmusaraj/{size}/3_2.png",
|
||||
created_at: "2019-07-22T03:47:04.864Z",
|
||||
like_count: 1,
|
||||
blurb: ":thinking: This here is a test of emojis in search blurbs.",
|
||||
post_number: 1,
|
||||
topic_id: 41,
|
||||
},
|
||||
],
|
||||
topics: [],
|
||||
users: [],
|
||||
categories: [],
|
||||
tags: [],
|
||||
groups: [],
|
||||
grouped_search_result: false,
|
||||
};
|
||||
|
||||
test("unescapesEmojisInBlurbs", function (assert) {
|
||||
const source = {
|
||||
posts: [
|
||||
{
|
||||
id: 160,
|
||||
username: "pmusaraj",
|
||||
avatar_template: "/user_avatar/localhost/pmusaraj/{size}/3_2.png",
|
||||
created_at: "2019-07-22T03:47:04.864Z",
|
||||
like_count: 1,
|
||||
blurb: ":thinking: This here is a test of emojis in search blurbs.",
|
||||
post_number: 1,
|
||||
topic_id: 41,
|
||||
},
|
||||
],
|
||||
topics: [],
|
||||
users: [],
|
||||
categories: [],
|
||||
tags: [],
|
||||
groups: [],
|
||||
grouped_search_result: false,
|
||||
};
|
||||
const results = translateResults(source);
|
||||
const blurb = results.posts[0].get("blurb");
|
||||
|
||||
const results = translateResults(source);
|
||||
const blurb = results.posts[0].get("blurb");
|
||||
assert.ok(blurb.indexOf("thinking.png"));
|
||||
assert.ok(blurb.indexOf('<img width="20" height="20" src') === 0);
|
||||
assert.ok(blurb.indexOf(":thinking:") === -1);
|
||||
});
|
||||
|
||||
assert.ok(blurb.indexOf("thinking.png"));
|
||||
assert.ok(blurb.indexOf('<img width="20" height="20" src') === 0);
|
||||
assert.ok(blurb.indexOf(":thinking:") === -1);
|
||||
});
|
||||
|
||||
test("searchContextDescription", function (assert) {
|
||||
assert.equal(
|
||||
searchContextDescription("topic"),
|
||||
I18n.t("search.context.topic")
|
||||
);
|
||||
assert.equal(
|
||||
searchContextDescription("user", "silvio.dante"),
|
||||
I18n.t("search.context.user", { username: "silvio.dante" })
|
||||
);
|
||||
assert.equal(
|
||||
searchContextDescription("category", "staff"),
|
||||
I18n.t("search.context.category", { category: "staff" })
|
||||
);
|
||||
assert.equal(
|
||||
searchContextDescription("tag", "important"),
|
||||
I18n.t("search.context.tag", { tag: "important" })
|
||||
);
|
||||
assert.equal(
|
||||
searchContextDescription("private_messages"),
|
||||
I18n.t("search.context.private_messages")
|
||||
);
|
||||
assert.equal(searchContextDescription("bad_type"), null);
|
||||
test("searchContextDescription", function (assert) {
|
||||
assert.equal(
|
||||
searchContextDescription("topic"),
|
||||
I18n.t("search.context.topic")
|
||||
);
|
||||
assert.equal(
|
||||
searchContextDescription("user", "silvio.dante"),
|
||||
I18n.t("search.context.user", { username: "silvio.dante" })
|
||||
);
|
||||
assert.equal(
|
||||
searchContextDescription("category", "staff"),
|
||||
I18n.t("search.context.category", { category: "staff" })
|
||||
);
|
||||
assert.equal(
|
||||
searchContextDescription("tag", "important"),
|
||||
I18n.t("search.context.tag", { tag: "important" })
|
||||
);
|
||||
assert.equal(
|
||||
searchContextDescription("private_messages"),
|
||||
I18n.t("search.context.private_messages")
|
||||
);
|
||||
assert.equal(searchContextDescription("bad_type"), null);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,71 +1,72 @@
|
||||
import { test, module } from "qunit";
|
||||
import Sharing from "discourse/lib/sharing";
|
||||
|
||||
module("lib:sharing", {
|
||||
beforeEach() {
|
||||
module("Unit | Utility | sharing", function (hooks) {
|
||||
hooks.beforeEach(function () {
|
||||
Sharing._reset();
|
||||
},
|
||||
afterEach() {
|
||||
});
|
||||
|
||||
hooks.afterEach(function () {
|
||||
Sharing._reset();
|
||||
},
|
||||
});
|
||||
|
||||
test("addSource", function (assert) {
|
||||
const sharingSettings = "facebook|twitter";
|
||||
|
||||
assert.blank(Sharing.activeSources(sharingSettings));
|
||||
|
||||
Sharing.addSource({
|
||||
id: "facebook",
|
||||
});
|
||||
|
||||
assert.equal(Sharing.activeSources(sharingSettings).length, 1);
|
||||
});
|
||||
|
||||
test("addSharingId", function (assert) {
|
||||
const sharingSettings = "";
|
||||
|
||||
assert.blank(Sharing.activeSources(sharingSettings));
|
||||
|
||||
Sharing.addSource({
|
||||
id: "new-source",
|
||||
});
|
||||
|
||||
assert.blank(
|
||||
Sharing.activeSources(sharingSettings),
|
||||
"it doesn’t activate a source not in settings"
|
||||
);
|
||||
|
||||
Sharing.addSharingId("new-source");
|
||||
|
||||
assert.equal(
|
||||
Sharing.activeSources(sharingSettings).length,
|
||||
1,
|
||||
"it adds sharing id to existing sharing settings"
|
||||
);
|
||||
|
||||
const privateContext = true;
|
||||
|
||||
Sharing.addSource({
|
||||
id: "another-source",
|
||||
});
|
||||
Sharing.addSharingId("another-source");
|
||||
|
||||
assert.equal(
|
||||
Sharing.activeSources(sharingSettings, privateContext).length,
|
||||
0,
|
||||
"it does not add a regular source to sources in a private context"
|
||||
);
|
||||
|
||||
Sharing.addSource({
|
||||
id: "a-private-friendly-source",
|
||||
showInPrivateContext: true,
|
||||
});
|
||||
Sharing.addSharingId("a-private-friendly-source");
|
||||
|
||||
assert.equal(
|
||||
Sharing.activeSources(sharingSettings, privateContext).length,
|
||||
1,
|
||||
"it does not add a regular source to sources in a private context"
|
||||
);
|
||||
});
|
||||
|
||||
test("addSource", function (assert) {
|
||||
const sharingSettings = "facebook|twitter";
|
||||
|
||||
assert.blank(Sharing.activeSources(sharingSettings));
|
||||
|
||||
Sharing.addSource({
|
||||
id: "facebook",
|
||||
});
|
||||
|
||||
assert.equal(Sharing.activeSources(sharingSettings).length, 1);
|
||||
});
|
||||
|
||||
test("addSharingId", function (assert) {
|
||||
const sharingSettings = "";
|
||||
|
||||
assert.blank(Sharing.activeSources(sharingSettings));
|
||||
|
||||
Sharing.addSource({
|
||||
id: "new-source",
|
||||
});
|
||||
|
||||
assert.blank(
|
||||
Sharing.activeSources(sharingSettings),
|
||||
"it doesn’t activate a source not in settings"
|
||||
);
|
||||
|
||||
Sharing.addSharingId("new-source");
|
||||
|
||||
assert.equal(
|
||||
Sharing.activeSources(sharingSettings).length,
|
||||
1,
|
||||
"it adds sharing id to existing sharing settings"
|
||||
);
|
||||
|
||||
const privateContext = true;
|
||||
|
||||
Sharing.addSource({
|
||||
id: "another-source",
|
||||
});
|
||||
Sharing.addSharingId("another-source");
|
||||
|
||||
assert.equal(
|
||||
Sharing.activeSources(sharingSettings, privateContext).length,
|
||||
0,
|
||||
"it does not add a regular source to sources in a private context"
|
||||
);
|
||||
|
||||
Sharing.addSource({
|
||||
id: "a-private-friendly-source",
|
||||
showInPrivateContext: true,
|
||||
});
|
||||
Sharing.addSharingId("a-private-friendly-source");
|
||||
|
||||
assert.equal(
|
||||
Sharing.activeSources(sharingSettings, privateContext).length,
|
||||
1,
|
||||
"it does not add a regular source to sources in a private context"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import { test, module } from "qunit";
|
||||
import { isRTL, isLTR } from "discourse/lib/text-direction";
|
||||
|
||||
module("lib:text-direction");
|
||||
module("Unit | Utility | text-direction", function () {
|
||||
test("isRTL", function (assert) {
|
||||
// Hebrew
|
||||
assert.equal(isRTL("זה מבחן"), true);
|
||||
|
||||
test("isRTL", function (assert) {
|
||||
// Hebrew
|
||||
assert.equal(isRTL("זה מבחן"), true);
|
||||
// Arabic
|
||||
assert.equal(isRTL("هذا اختبار"), true);
|
||||
|
||||
// Arabic
|
||||
assert.equal(isRTL("هذا اختبار"), true);
|
||||
// Persian
|
||||
assert.equal(isRTL("این یک امتحان است"), true);
|
||||
|
||||
// Persian
|
||||
assert.equal(isRTL("این یک امتحان است"), true);
|
||||
assert.equal(isRTL("This is a test"), false);
|
||||
assert.equal(isRTL(""), false);
|
||||
});
|
||||
|
||||
assert.equal(isRTL("This is a test"), false);
|
||||
assert.equal(isRTL(""), false);
|
||||
});
|
||||
|
||||
test("isLTR", function (assert) {
|
||||
assert.equal(isLTR("This is a test"), true);
|
||||
assert.equal(isLTR("זה מבחן"), false);
|
||||
test("isLTR", function (assert) {
|
||||
assert.equal(isLTR("This is a test"), true);
|
||||
assert.equal(isLTR("זה מבחן"), false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,406 +1,405 @@
|
||||
import { test, module } from "qunit";
|
||||
import toMarkdown from "discourse/lib/to-markdown";
|
||||
|
||||
module("lib:to-markdown");
|
||||
module("Unit | Utility | to-markdown", function () {
|
||||
test("converts styles between normal words", function (assert) {
|
||||
const html = `Line with <s>styles</s> <b><i>between</i></b> words.`;
|
||||
const markdown = `Line with ~~styles~~ ***between*** words.`;
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
|
||||
test("converts styles between normal words", function (assert) {
|
||||
const html = `Line with <s>styles</s> <b><i>between</i></b> words.`;
|
||||
const markdown = `Line with ~~styles~~ ***between*** words.`;
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
assert.equal(toMarkdown("A <b>bold </b>word"), "A **bold** word");
|
||||
assert.equal(toMarkdown("A <b>bold</b>, word"), "A **bold**, word");
|
||||
});
|
||||
|
||||
assert.equal(toMarkdown("A <b>bold </b>word"), "A **bold** word");
|
||||
assert.equal(toMarkdown("A <b>bold</b>, word"), "A **bold**, word");
|
||||
});
|
||||
test("converts inline nested styles", function (assert) {
|
||||
let html = `<em>Italicised line with <strong>some random</strong> <b>bold</b> words.</em>`;
|
||||
let markdown = `*Italicised line with **some random** **bold** words.*`;
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
|
||||
test("converts inline nested styles", function (assert) {
|
||||
let html = `<em>Italicised line with <strong>some random</strong> <b>bold</b> words.</em>`;
|
||||
let markdown = `*Italicised line with **some random** **bold** words.*`;
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
html = `<i class="fa">Italicised line
|
||||
with <b title="strong">some<br>
|
||||
random</b> <s>bold</s> words.</i>`;
|
||||
markdown = `<i>Italicised line with <b>some\nrandom</b> ~~bold~~ words.</i>`;
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
});
|
||||
|
||||
html = `<i class="fa">Italicised line
|
||||
with <b title="strong">some<br>
|
||||
random</b> <s>bold</s> words.</i>`;
|
||||
markdown = `<i>Italicised line with <b>some\nrandom</b> ~~bold~~ words.</i>`;
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
});
|
||||
test("converts a link", function (assert) {
|
||||
let html = `<a href="https://discourse.org">Discourse</a>`;
|
||||
let markdown = `[Discourse](https://discourse.org)`;
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
|
||||
test("converts a link", function (assert) {
|
||||
let html = `<a href="https://discourse.org">Discourse</a>`;
|
||||
let markdown = `[Discourse](https://discourse.org)`;
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
html = `<a href="https://discourse.org">Disc\n\n\nour\n\nse</a>`;
|
||||
markdown = `[Disc our se](https://discourse.org)`;
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
});
|
||||
|
||||
html = `<a href="https://discourse.org">Disc\n\n\nour\n\nse</a>`;
|
||||
markdown = `[Disc our se](https://discourse.org)`;
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
});
|
||||
test("put raw URL instead of converting the link", function (assert) {
|
||||
let url = "https://discourse.org";
|
||||
const html = () => `<a href="${url}">${url}</a>`;
|
||||
|
||||
test("put raw URL instead of converting the link", function (assert) {
|
||||
let url = "https://discourse.org";
|
||||
const html = () => `<a href="${url}">${url}</a>`;
|
||||
assert.equal(toMarkdown(html()), url);
|
||||
|
||||
assert.equal(toMarkdown(html()), url);
|
||||
url = "discourse.org/t/topic-slug/1";
|
||||
assert.equal(toMarkdown(html()), url);
|
||||
});
|
||||
|
||||
url = "discourse.org/t/topic-slug/1";
|
||||
assert.equal(toMarkdown(html()), url);
|
||||
});
|
||||
test("skip empty link", function (assert) {
|
||||
assert.equal(toMarkdown(`<a href="https://example.com"></a>`), "");
|
||||
});
|
||||
|
||||
test("skip empty link", function (assert) {
|
||||
assert.equal(toMarkdown(`<a href="https://example.com"></a>`), "");
|
||||
});
|
||||
test("converts heading tags", function (assert) {
|
||||
const html = `
|
||||
<h1>Heading 1</h1>
|
||||
<h2>Heading 2</h2>
|
||||
|
||||
test("converts heading tags", function (assert) {
|
||||
const html = `
|
||||
<h1>Heading 1</h1>
|
||||
<h2>Heading 2</h2>
|
||||
|
||||
\t <h3>Heading 3</h3>
|
||||
\t <h3>Heading 3</h3>
|
||||
|
||||
|
||||
<h4>Heading 4</h4>
|
||||
<h4>Heading 4</h4>
|
||||
|
||||
|
||||
|
||||
<h5>Heading 5</h5>
|
||||
<h5>Heading 5</h5>
|
||||
|
||||
|
||||
|
||||
|
||||
<h6>Heading 6</h6>
|
||||
`;
|
||||
const markdown = `# Heading 1\n\n## Heading 2\n\n### Heading 3\n\n#### Heading 4\n\n##### Heading 5\n\n###### Heading 6`;
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
});
|
||||
<h6>Heading 6</h6>
|
||||
`;
|
||||
const markdown = `# Heading 1\n\n## Heading 2\n\n### Heading 3\n\n#### Heading 4\n\n##### Heading 5\n\n###### Heading 6`;
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
});
|
||||
|
||||
test("converts ul list tag", function (assert) {
|
||||
let html = `
|
||||
test("converts ul list tag", function (assert) {
|
||||
let html = `
|
||||
<ul>
|
||||
<li>Item 1</li>
|
||||
<li>
|
||||
Item 2
|
||||
<ul>
|
||||
<li>Sub Item 1</li>
|
||||
<li><p>Sub Item 2</p></li>
|
||||
<li>Sub Item 3<ul><li>Sub <i>Sub</i> Item 1</li><li>Sub <b>Sub</b> Item 2</li></ul></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Item 3</li>
|
||||
</ul>
|
||||
`;
|
||||
let markdown = `* Item 1\n* Item 2\n * Sub Item 1\n * Sub Item 2\n * Sub Item 3\n * Sub *Sub* Item 1\n * Sub **Sub** Item 2\n* Item 3`;
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
|
||||
html = `
|
||||
<ul>
|
||||
<li>Item 1</li>
|
||||
<li>
|
||||
Item 2
|
||||
<ul>
|
||||
<li>Sub Item 1</li>
|
||||
<li><p>Sub Item 2</p></li>
|
||||
<li>Sub Item 3<ul><li>Sub <i>Sub</i> Item 1</li><li>Sub <b>Sub</b> Item 2</li></ul></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Item 3</li>
|
||||
</ul>
|
||||
`;
|
||||
let markdown = `* Item 1\n* Item 2\n * Sub Item 1\n * Sub Item 2\n * Sub Item 3\n * Sub *Sub* Item 1\n * Sub **Sub** Item 2\n* Item 3`;
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
|
||||
html = `
|
||||
<ul>
|
||||
<li><p><span>Bullets at level 1</span></p></li>
|
||||
<li><p><span>Bullets at level 1</span></p></li> <ul> <li><p><span>Bullets at level 2</span></p></li> <li><p><span>Bullets at level 2</span></p></li> <ul> <li><p><span>Bullets at level 3</span></p></li> </ul> <li><p><span>Bullets at level 2</span></p></li> </ul> <li><p><span>Bullets at level 1</span></p></li></ul> `;
|
||||
markdown = `* Bullets at level 1
|
||||
<li><p><span>Bullets at level 1</span></p></li>
|
||||
<li><p><span>Bullets at level 1</span></p></li> <ul> <li><p><span>Bullets at level 2</span></p></li> <li><p><span>Bullets at level 2</span></p></li> <ul> <li><p><span>Bullets at level 3</span></p></li> </ul> <li><p><span>Bullets at level 2</span></p></li> </ul> <li><p><span>Bullets at level 1</span></p></li></ul> `;
|
||||
markdown = `* Bullets at level 1
|
||||
* Bullets at level 1
|
||||
* Bullets at level 2
|
||||
* Bullets at level 2
|
||||
* Bullets at level 3
|
||||
* Bullets at level 2
|
||||
* Bullets at level 1`;
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
});
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
});
|
||||
|
||||
test("stripes unwanted inline tags", function (assert) {
|
||||
const html = `
|
||||
<p>Lorem ipsum <span>dolor sit amet, consectetur</span> <strike>elit.</strike></p>
|
||||
<p>Ut minim veniam, <label>quis nostrud</label> laboris <nisi> ut aliquip ex ea</nisi> commodo.</p>
|
||||
`;
|
||||
const markdown = `Lorem ipsum dolor sit amet, consectetur ~~elit.~~\n\nUt minim veniam, quis nostrud laboris ut aliquip ex ea commodo.`;
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
});
|
||||
test("stripes unwanted inline tags", function (assert) {
|
||||
const html = `
|
||||
<p>Lorem ipsum <span>dolor sit amet, consectetur</span> <strike>elit.</strike></p>
|
||||
<p>Ut minim veniam, <label>quis nostrud</label> laboris <nisi> ut aliquip ex ea</nisi> commodo.</p>
|
||||
`;
|
||||
const markdown = `Lorem ipsum dolor sit amet, consectetur ~~elit.~~\n\nUt minim veniam, quis nostrud laboris ut aliquip ex ea commodo.`;
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
});
|
||||
|
||||
test("converts table tags", function (assert) {
|
||||
let html = `<address>Discourse Avenue</address><b>laboris</b>
|
||||
<table>
|
||||
<thead> <tr><th>Heading 1</th><th>Head 2</th></tr> </thead>
|
||||
<tbody>
|
||||
<tr><td>Lorem</td><td>ipsum</td></tr>
|
||||
<tr><td><b>dolor</b></td> <td><i>sit amet</i></td> </tr>
|
||||
test("converts table tags", function (assert) {
|
||||
let html = `<address>Discourse Avenue</address><b>laboris</b>
|
||||
<table>
|
||||
<thead> <tr><th>Heading 1</th><th>Head 2</th></tr> </thead>
|
||||
<tbody>
|
||||
<tr><td>Lorem</td><td>ipsum</td></tr>
|
||||
<tr><td><b>dolor</b></td> <td><i>sit amet</i></td> </tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
`;
|
||||
let markdown = `Discourse Avenue\n\n**laboris**\n\n|Heading 1|Head 2|\n| --- | --- |\n|Lorem|ipsum|\n|**dolor**|*sit amet*|`;
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
</tbody>
|
||||
</table>
|
||||
`;
|
||||
let markdown = `Discourse Avenue\n\n**laboris**\n\n|Heading 1|Head 2|\n| --- | --- |\n|Lorem|ipsum|\n|**dolor**|*sit amet*|`;
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
|
||||
html = `<table>
|
||||
<tr><th>Heading 1</th><th>Head 2</th></tr>
|
||||
<tr><td><a href="http://example.com"><img src="http://example.com/image.png" alt="Lorem" width="45" height="45"></a></td><td>ipsum</td></tr>
|
||||
</table>`;
|
||||
markdown = `|Heading 1|Head 2|\n| --- | --- |\n|[](http://example.com)|ipsum|`;
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
});
|
||||
html = `<table>
|
||||
<tr><th>Heading 1</th><th>Head 2</th></tr>
|
||||
<tr><td><a href="http://example.com"><img src="http://example.com/image.png" alt="Lorem" width="45" height="45"></a></td><td>ipsum</td></tr>
|
||||
</table>`;
|
||||
markdown = `|Heading 1|Head 2|\n| --- | --- |\n|[](http://example.com)|ipsum|`;
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
});
|
||||
|
||||
test("replace pipes with spaces if table format not supported", function (assert) {
|
||||
let html = `<table>
|
||||
<thead> <tr><th>Headi<br><br>ng 1</th><th>Head 2</th></tr> </thead>
|
||||
<tbody>
|
||||
<tr><td>Lorem</td><td>ipsum</td></tr>
|
||||
<tr><td><a href="http://example.com"><img src="http://dolor.com/image.png" /></a></td> <td><i>sit amet</i></td></tr></tbody>
|
||||
</table>
|
||||
`;
|
||||
let markdown = `Headi\n\nng 1 Head 2\nLorem ipsum\n[](http://example.com) *sit amet*`;
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
test("replace pipes with spaces if table format not supported", function (assert) {
|
||||
let html = `<table>
|
||||
<thead> <tr><th>Headi<br><br>ng 1</th><th>Head 2</th></tr> </thead>
|
||||
<tbody>
|
||||
<tr><td>Lorem</td><td>ipsum</td></tr>
|
||||
<tr><td><a href="http://example.com"><img src="http://dolor.com/image.png" /></a></td> <td><i>sit amet</i></td></tr></tbody>
|
||||
</table>
|
||||
`;
|
||||
let markdown = `Headi\n\nng 1 Head 2\nLorem ipsum\n[](http://example.com) *sit amet*`;
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
|
||||
html = `<table>
|
||||
<thead> <tr><th>Heading 1</th></tr> </thead>
|
||||
<tbody>
|
||||
<tr><td>Lorem</td></tr>
|
||||
<tr><td><i>sit amet</i></td></tr></tbody>
|
||||
</table>
|
||||
`;
|
||||
markdown = `Heading 1\nLorem\n*sit amet*`;
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
html = `<table>
|
||||
<thead> <tr><th>Heading 1</th></tr> </thead>
|
||||
<tbody>
|
||||
<tr><td>Lorem</td></tr>
|
||||
<tr><td><i>sit amet</i></td></tr></tbody>
|
||||
</table>
|
||||
`;
|
||||
markdown = `Heading 1\nLorem\n*sit amet*`;
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
|
||||
html = `<table><tr><td>Lorem</td><td><strong>sit amet</strong></td></tr></table>`;
|
||||
markdown = `Lorem **sit amet**`;
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
});
|
||||
html = `<table><tr><td>Lorem</td><td><strong>sit amet</strong></td></tr></table>`;
|
||||
markdown = `Lorem **sit amet**`;
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
});
|
||||
|
||||
test("converts img tag", function (assert) {
|
||||
const url = "https://example.com/image.png";
|
||||
const base62SHA1 = "q16M6GR110R47Z9p9Dk3PMXOJoE";
|
||||
let html = `<img src="${url}" width="100" height="50">`;
|
||||
assert.equal(toMarkdown(html), ``);
|
||||
test("converts img tag", function (assert) {
|
||||
const url = "https://example.com/image.png";
|
||||
const base62SHA1 = "q16M6GR110R47Z9p9Dk3PMXOJoE";
|
||||
let html = `<img src="${url}" width="100" height="50">`;
|
||||
assert.equal(toMarkdown(html), ``);
|
||||
|
||||
html = `<img src="${url}" width="100" height="50" title="some title">`;
|
||||
assert.equal(toMarkdown(html), ``);
|
||||
html = `<img src="${url}" width="100" height="50" title="some title">`;
|
||||
assert.equal(toMarkdown(html), ``);
|
||||
|
||||
html = `<img src="${url}" width="100" height="50" title="some title" data-base62-sha1="${base62SHA1}">`;
|
||||
assert.equal(
|
||||
toMarkdown(html),
|
||||
``
|
||||
);
|
||||
html = `<img src="${url}" width="100" height="50" title="some title" data-base62-sha1="${base62SHA1}">`;
|
||||
assert.equal(
|
||||
toMarkdown(html),
|
||||
``
|
||||
);
|
||||
|
||||
html = `<div><span><img src="${url}" alt="description" width="50" height="100" /></span></div>`;
|
||||
assert.equal(toMarkdown(html), ``);
|
||||
html = `<div><span><img src="${url}" alt="description" width="50" height="100" /></span></div>`;
|
||||
assert.equal(toMarkdown(html), ``);
|
||||
|
||||
html = `<a href="http://example.com"><img src="${url}" alt="description" /></a>`;
|
||||
assert.equal(
|
||||
toMarkdown(html),
|
||||
`[](http://example.com)`
|
||||
);
|
||||
html = `<a href="http://example.com"><img src="${url}" alt="description" /></a>`;
|
||||
assert.equal(
|
||||
toMarkdown(html),
|
||||
`[](http://example.com)`
|
||||
);
|
||||
|
||||
html = `<a href="http://example.com">description <img src="${url}" /></a>`;
|
||||
assert.equal(
|
||||
toMarkdown(html),
|
||||
`[description ](http://example.com)`
|
||||
);
|
||||
html = `<a href="http://example.com">description <img src="${url}" /></a>`;
|
||||
assert.equal(
|
||||
toMarkdown(html),
|
||||
`[description ](http://example.com)`
|
||||
);
|
||||
|
||||
html = `<img alt="description" />`;
|
||||
assert.equal(toMarkdown(html), "");
|
||||
html = `<img alt="description" />`;
|
||||
assert.equal(toMarkdown(html), "");
|
||||
|
||||
html = `<a><img src="${url}" alt="description" /></a>`;
|
||||
assert.equal(toMarkdown(html), ``);
|
||||
});
|
||||
html = `<a><img src="${url}" alt="description" /></a>`;
|
||||
assert.equal(toMarkdown(html), ``);
|
||||
});
|
||||
|
||||
test("supporting html tags by keeping them", function (assert) {
|
||||
let html =
|
||||
"Lorem <del>ipsum dolor</del> sit <big>amet, <ins>consectetur</ins></big>";
|
||||
let output = html;
|
||||
assert.equal(toMarkdown(html), output);
|
||||
test("supporting html tags by keeping them", function (assert) {
|
||||
let html =
|
||||
"Lorem <del>ipsum dolor</del> sit <big>amet, <ins>consectetur</ins></big>";
|
||||
let output = html;
|
||||
assert.equal(toMarkdown(html), output);
|
||||
|
||||
html = `Lorem <del style="font-weight: bold">ipsum dolor</del> sit <big>amet, <ins onclick="alert('hello')">consectetur</ins></big>`;
|
||||
assert.equal(toMarkdown(html), output);
|
||||
html = `Lorem <del style="font-weight: bold">ipsum dolor</del> sit <big>amet, <ins onclick="alert('hello')">consectetur</ins></big>`;
|
||||
assert.equal(toMarkdown(html), output);
|
||||
|
||||
html = `<a href="http://example.com" onload="">Lorem <del style="font-weight: bold">ipsum dolor</del> sit</a>.`;
|
||||
output = `[Lorem <del>ipsum dolor</del> sit](http://example.com).`;
|
||||
assert.equal(toMarkdown(html), output);
|
||||
html = `<a href="http://example.com" onload="">Lorem <del style="font-weight: bold">ipsum dolor</del> sit</a>.`;
|
||||
output = `[Lorem <del>ipsum dolor</del> sit](http://example.com).`;
|
||||
assert.equal(toMarkdown(html), output);
|
||||
|
||||
html = `Lorem <del>ipsum dolor</del> sit.`;
|
||||
assert.equal(toMarkdown(html), html);
|
||||
html = `Lorem <del>ipsum dolor</del> sit.`;
|
||||
assert.equal(toMarkdown(html), html);
|
||||
|
||||
html = `Have you tried clicking the <kbd>Help Me!</kbd> button?`;
|
||||
assert.equal(toMarkdown(html), html);
|
||||
html = `Have you tried clicking the <kbd>Help Me!</kbd> button?`;
|
||||
assert.equal(toMarkdown(html), html);
|
||||
|
||||
html = `Lorem <a href="http://example.com"><del>ipsum \n\n\n dolor</del> sit.</a>`;
|
||||
output = `Lorem [<del>ipsum dolor</del> sit.](http://example.com)`;
|
||||
assert.equal(toMarkdown(html), output);
|
||||
});
|
||||
html = `Lorem <a href="http://example.com"><del>ipsum \n\n\n dolor</del> sit.</a>`;
|
||||
output = `Lorem [<del>ipsum dolor</del> sit.](http://example.com)`;
|
||||
assert.equal(toMarkdown(html), output);
|
||||
});
|
||||
|
||||
test("converts code tags", function (assert) {
|
||||
let html = `Lorem ipsum dolor sit amet,
|
||||
test("converts code tags", function (assert) {
|
||||
let html = `Lorem ipsum dolor sit amet,
|
||||
<pre><code>var helloWorld = () => {
|
||||
alert(' hello \t\t world ');
|
||||
return;
|
||||
}
|
||||
helloWorld();</code></pre>
|
||||
consectetur.`;
|
||||
let output = `Lorem ipsum dolor sit amet,\n\n\`\`\`\nvar helloWorld = () => {\n alert(' hello \t\t world ');\n return;\n}\nhelloWorld();\n\`\`\`\n\nconsectetur.`;
|
||||
let output = `Lorem ipsum dolor sit amet,\n\n\`\`\`\nvar helloWorld = () => {\n alert(' hello \t\t world ');\n return;\n}\nhelloWorld();\n\`\`\`\n\nconsectetur.`;
|
||||
|
||||
assert.equal(toMarkdown(html), output);
|
||||
assert.equal(toMarkdown(html), output);
|
||||
|
||||
html = `Lorem ipsum dolor sit amet, <code>var helloWorld = () => {
|
||||
html = `Lorem ipsum dolor sit amet, <code>var helloWorld = () => {
|
||||
alert(' hello \t\t world ');
|
||||
return;
|
||||
}
|
||||
helloWorld();</code>consectetur.`;
|
||||
output = `Lorem ipsum dolor sit amet, \`var helloWorld = () => {\n alert(' hello \t\t world ');\n return;\n}\nhelloWorld();\`consectetur.`;
|
||||
output = `Lorem ipsum dolor sit amet, \`var helloWorld = () => {\n alert(' hello \t\t world ');\n return;\n}\nhelloWorld();\`consectetur.`;
|
||||
|
||||
assert.equal(toMarkdown(html), output);
|
||||
});
|
||||
assert.equal(toMarkdown(html), output);
|
||||
});
|
||||
|
||||
test("converts blockquote tag", function (assert) {
|
||||
let html = "<blockquote>Lorem ipsum</blockquote>";
|
||||
let output = "> Lorem ipsum";
|
||||
assert.equal(toMarkdown(html), output);
|
||||
test("converts blockquote tag", function (assert) {
|
||||
let html = "<blockquote>Lorem ipsum</blockquote>";
|
||||
let output = "> Lorem ipsum";
|
||||
assert.equal(toMarkdown(html), output);
|
||||
|
||||
html =
|
||||
"<blockquote>Lorem ipsum</blockquote><blockquote><p>dolor sit amet</p></blockquote>";
|
||||
output = "> Lorem ipsum\n\n> dolor sit amet";
|
||||
assert.equal(toMarkdown(html), output);
|
||||
html =
|
||||
"<blockquote>Lorem ipsum</blockquote><blockquote><p>dolor sit amet</p></blockquote>";
|
||||
output = "> Lorem ipsum\n\n> dolor sit amet";
|
||||
assert.equal(toMarkdown(html), output);
|
||||
|
||||
html =
|
||||
"<blockquote>\nLorem ipsum\n<blockquote><p>dolor <blockquote>sit</blockquote> amet</p></blockquote></blockquote>";
|
||||
output = "> Lorem ipsum\n> > dolor\n> > > sit\n> > amet";
|
||||
assert.equal(toMarkdown(html), output);
|
||||
});
|
||||
html =
|
||||
"<blockquote>\nLorem ipsum\n<blockquote><p>dolor <blockquote>sit</blockquote> amet</p></blockquote></blockquote>";
|
||||
output = "> Lorem ipsum\n> > dolor\n> > > sit\n> > amet";
|
||||
assert.equal(toMarkdown(html), output);
|
||||
});
|
||||
|
||||
test("converts ol list tag", function (assert) {
|
||||
const html = `Testing
|
||||
<ol>
|
||||
<li>Item 1</li>
|
||||
<li>
|
||||
Item 2
|
||||
<ol start="100">
|
||||
<li>Sub Item 1</li>
|
||||
<li>Sub Item 2</li>
|
||||
</ol>
|
||||
</li>
|
||||
<li>Item 3</li>
|
||||
</ol>
|
||||
`;
|
||||
const markdown = `Testing\n\n1. Item 1\n2. Item 2\n 100. Sub Item 1\n 101. Sub Item 2\n3. Item 3`;
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
});
|
||||
test("converts ol list tag", function (assert) {
|
||||
const html = `Testing
|
||||
<ol>
|
||||
<li>Item 1</li>
|
||||
<li>
|
||||
Item 2
|
||||
<ol start="100">
|
||||
<li>Sub Item 1</li>
|
||||
<li>Sub Item 2</li>
|
||||
</ol>
|
||||
</li>
|
||||
<li>Item 3</li>
|
||||
</ol>
|
||||
`;
|
||||
const markdown = `Testing\n\n1. Item 1\n2. Item 2\n 100. Sub Item 1\n 101. Sub Item 2\n3. Item 3`;
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
});
|
||||
|
||||
test("converts list tag from word", function (assert) {
|
||||
const html = `Sample<!--StartFragment-->
|
||||
<p class=MsoListParagraphCxSpFirst style='text-indent:-.25in;mso-list:l0 level1 lfo1'>
|
||||
<![if !supportLists]>
|
||||
<span style='font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-family: Symbol;mso-bidi-font-weight:bold'>
|
||||
<span style='mso-list:Ignore'>·
|
||||
<span style='font:7.0pt "Times New Roman"'> </span>
|
||||
test("converts list tag from word", function (assert) {
|
||||
const html = `Sample<!--StartFragment-->
|
||||
<p class=MsoListParagraphCxSpFirst style='text-indent:-.25in;mso-list:l0 level1 lfo1'>
|
||||
<![if !supportLists]>
|
||||
<span style='font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-family: Symbol;mso-bidi-font-weight:bold'>
|
||||
<span style='mso-list:Ignore'>·
|
||||
<span style='font:7.0pt "Times New Roman"'> </span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<![endif]>
|
||||
<b>Item 1
|
||||
<o:p></o:p>
|
||||
</b>
|
||||
</p>
|
||||
<p class=MsoListParagraphCxSpMiddle style='text-indent:-.25in;mso-list:l0 level2 lfo1'>
|
||||
<![if !supportLists]>
|
||||
<span style='font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-family: Symbol;mso-bidi-font-style:italic'>
|
||||
<span style='mso-list:Ignore'>·
|
||||
<span style='font:7.0pt "Times New Roman"'> </span>
|
||||
</span>
|
||||
</span>
|
||||
<![endif]>
|
||||
<i>Item 2
|
||||
<o:p></o:p>
|
||||
</i>
|
||||
</p>
|
||||
<p class=MsoListParagraphCxSpMiddle style='text-indent:-.25in;mso-list:l0 level3 lfo1'>
|
||||
<![if !supportLists]>
|
||||
<span style='font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-family: Symbol'>
|
||||
<span style='mso-list:Ignore'>·
|
||||
<span style='font:7.0pt "Times New Roman"'> </span>
|
||||
</span>
|
||||
</span>
|
||||
<![endif]>Item 3 </p>
|
||||
<p class=MsoListParagraphCxSpLast style='text-indent:-.25in;mso-list:l0 level1 lfo1'>
|
||||
<![if !supportLists]>
|
||||
<span style='font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-family: Symbol'>
|
||||
<span style='mso-list:Ignore'>·
|
||||
<span style='font:7.0pt "Times New Roman"'> </span>
|
||||
</span>
|
||||
</span>
|
||||
<![endif]>Item 4</p>
|
||||
<!--EndFragment-->List`;
|
||||
const markdown = `Sample\n\n* **Item 1**\n * *Item 2*\n * Item 3\n* Item 4\n\nList`;
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
});
|
||||
|
||||
test("keeps mention/hash class", function (assert) {
|
||||
const html = `
|
||||
<p>User mention: <a class="mention" href="/u/discourse">@discourse</a></p>
|
||||
<p>Group mention: <a class="mention-group" href="/groups/discourse">@discourse-group</a></p>
|
||||
<p>Category link: <a class="hashtag" href="/c/foo/1">#<span>foo</span></a></p>
|
||||
<p>Sub-category link: <a class="hashtag" href="/c/foo/bar/2">#<span>foo:bar</span></a></p>
|
||||
`;
|
||||
|
||||
const markdown = `User mention: @discourse\n\nGroup mention: @discourse-group\n\nCategory link: #foo\n\nSub-category link: #foo:bar`;
|
||||
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
});
|
||||
|
||||
test("keeps emoji and removes click count", function (assert) {
|
||||
const html = `
|
||||
<p>
|
||||
A <a href="http://example.com">link</a><span class="badge badge-notification clicks" title="1 click">1</span> with click count
|
||||
and <img class="emoji" title=":boom:" src="https://d11a6trkgmumsb.cloudfront.net/images/emoji/twitter/boom.png?v=5" alt=":boom:" /> emoji.
|
||||
<![endif]>
|
||||
<b>Item 1
|
||||
<o:p></o:p>
|
||||
</b>
|
||||
</p>
|
||||
`;
|
||||
|
||||
const markdown = `A [link](http://example.com) with click count and :boom: emoji.`;
|
||||
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
});
|
||||
|
||||
test("keeps emoji syntax for custom emoji", function (assert) {
|
||||
const html = `
|
||||
<p>
|
||||
<img class="emoji emoji-custom" title=":custom_emoji:" src="https://d11a6trkgmumsb.cloudfront.net/images/emoji/custom_emoji" alt=":custom_emoji:" />
|
||||
<p class=MsoListParagraphCxSpMiddle style='text-indent:-.25in;mso-list:l0 level2 lfo1'>
|
||||
<![if !supportLists]>
|
||||
<span style='font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-family: Symbol;mso-bidi-font-style:italic'>
|
||||
<span style='mso-list:Ignore'>·
|
||||
<span style='font:7.0pt "Times New Roman"'> </span>
|
||||
</span>
|
||||
</span>
|
||||
<![endif]>
|
||||
<i>Item 2
|
||||
<o:p></o:p>
|
||||
</i>
|
||||
</p>
|
||||
`;
|
||||
<p class=MsoListParagraphCxSpMiddle style='text-indent:-.25in;mso-list:l0 level3 lfo1'>
|
||||
<![if !supportLists]>
|
||||
<span style='font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-family: Symbol'>
|
||||
<span style='mso-list:Ignore'>·
|
||||
<span style='font:7.0pt "Times New Roman"'> </span>
|
||||
</span>
|
||||
</span>
|
||||
<![endif]>Item 3 </p>
|
||||
<p class=MsoListParagraphCxSpLast style='text-indent:-.25in;mso-list:l0 level1 lfo1'>
|
||||
<![if !supportLists]>
|
||||
<span style='font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-family: Symbol'>
|
||||
<span style='mso-list:Ignore'>·
|
||||
<span style='font:7.0pt "Times New Roman"'> </span>
|
||||
</span>
|
||||
</span>
|
||||
<![endif]>Item 4</p>
|
||||
<!--EndFragment-->List`;
|
||||
const markdown = `Sample\n\n* **Item 1**\n * *Item 2*\n * Item 3\n* Item 4\n\nList`;
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
});
|
||||
|
||||
const markdown = `:custom_emoji:`;
|
||||
test("keeps mention/hash class", function (assert) {
|
||||
const html = `
|
||||
<p>User mention: <a class="mention" href="/u/discourse">@discourse</a></p>
|
||||
<p>Group mention: <a class="mention-group" href="/groups/discourse">@discourse-group</a></p>
|
||||
<p>Category link: <a class="hashtag" href="/c/foo/1">#<span>foo</span></a></p>
|
||||
<p>Sub-category link: <a class="hashtag" href="/c/foo/bar/2">#<span>foo:bar</span></a></p>
|
||||
`;
|
||||
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
});
|
||||
const markdown = `User mention: @discourse\n\nGroup mention: @discourse-group\n\nCategory link: #foo\n\nSub-category link: #foo:bar`;
|
||||
|
||||
test("converts image lightboxes to markdown", function (assert) {
|
||||
let html = `
|
||||
<a class="lightbox" href="https://d11a6trkgmumsb.cloudfront.net/uploads/default/original/1X/8hkjhk7692f6afed3cb99d43ab2abd4e30aa8cba.jpeg" data-download-href="https://d11a6trkgmumsb.cloudfront.net/uploads/default/8hkjhk7692f6afed3cb99d43ab2abd4e30aa8cba" title="sherlock3_sig.jpg" rel="nofollow noopener"><img src="https://d11a6trkgmumsb.cloudfront.net/uploads/default/optimized/1X/8hkjhk7692f6afed3cb99d43ab2abd4e30aa8cba_2_689x459.jpeg" alt="sherlock3_sig" width="689" height="459" class="d-lazyload" srcset="https://d11a6trkgmumsb.cloudfront.net/uploads/default/optimized/1X/8hkjhk7692f6afed3cb99d43ab2abd4e30aa8cba_2_689x459.jpeg, https://d11a6trkgmumsb.cloudfront.net/uploads/default/optimized/1X/8hkjhk7692f6afed3cb99d43ab2abd4e30aa8cba_2_1033x688.jpeg 1.5x, https://d11a6trkgmumsb.cloudfront.net/uploads/default/optimized/1X/8hkjhk7692f6afed3cb99d43ab2abd4e30aa8cba_2_1378x918.jpeg 2x"><div class="meta">
|
||||
<span class="filename">sherlock3_sig.jpg</span><span class="informations">5496×3664 2 MB</span><span class="expand"></span>
|
||||
</div></a>
|
||||
`;
|
||||
let markdown = ``;
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
});
|
||||
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
test("keeps emoji and removes click count", function (assert) {
|
||||
const html = `
|
||||
<p>
|
||||
A <a href="http://example.com">link</a><span class="badge badge-notification clicks" title="1 click">1</span> with click count
|
||||
and <img class="emoji" title=":boom:" src="https://d11a6trkgmumsb.cloudfront.net/images/emoji/twitter/boom.png?v=5" alt=":boom:" /> emoji.
|
||||
</p>
|
||||
`;
|
||||
|
||||
html = `<a class="lightbox" href="https://d11a6trkgmumsb.cloudfront.net/uploads/default/original/1X/8hkjhk7692f6afed3cb99d43ab2abd4e30aa8cba.jpeg" data-download-href="https://d11a6trkgmumsb.cloudfront.net/uploads/default/8hkjhk7692f6afed3cb99d43ab2abd4e30aa8cba" title="sherlock3_sig.jpg" rel="nofollow noopener"><img src="https://d11a6trkgmumsb.cloudfront.net/uploads/default/optimized/1X/8hkjhk7692f6afed3cb99d43ab2abd4e30aa8cba_2_689x459.jpeg" alt="sherlock3_sig" width="689" height="459" class="d-lazyload" srcset="https://d11a6trkgmumsb.cloudfront.net/uploads/default/optimized/1X/8hkjhk7692f6afed3cb99d43ab2abd4e30aa8cba_2_689x459.jpeg, https://d11a6trkgmumsb.cloudfront.net/uploads/default/optimized/1X/8hkjhk7692f6afed3cb99d43ab2abd4e30aa8cba_2_1033x688.jpeg 1.5x, https://d11a6trkgmumsb.cloudfront.net/uploads/default/optimized/1X/8hkjhk7692f6afed3cb99d43ab2abd4e30aa8cba_2_1378x918.jpeg 2x"></a>`;
|
||||
const markdown = `A [link](http://example.com) with click count and :boom: emoji.`;
|
||||
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
});
|
||||
|
||||
html = `
|
||||
<a class="lightbox" href="https://d11a6trkgmumsb.cloudfront.net/uploads/default/original/1X/8hkjhk7692f6afed3cb99d43ab2abd4e30aa8cba.jpeg" data-download-href="https://d11a6trkgmumsb.cloudfront.net/uploads/default/8hkjhk7692f6afed3cb99d43ab2abd4e30aa8cba" title="sherlock3_sig.jpg" rel="nofollow noopener"><img src="https://d11a6trkgmumsb.cloudfront.net/uploads/default/optimized/1X/8hkjhk7692f6afed3cb99d43ab2abd4e30aa8cba_2_689x459.jpeg" data-base62-sha1="1frsimI7TOtFJyD2LLyKSHM8JWe" alt="sherlock3_sig" width="689" height="459" class="d-lazyload" srcset="https://d11a6trkgmumsb.cloudfront.net/uploads/default/optimized/1X/8hkjhk7692f6afed3cb99d43ab2abd4e30aa8cba_2_689x459.jpeg, https://d11a6trkgmumsb.cloudfront.net/uploads/default/optimized/1X/8hkjhk7692f6afed3cb99d43ab2abd4e30aa8cba_2_1033x688.jpeg 1.5x, https://d11a6trkgmumsb.cloudfront.net/uploads/default/optimized/1X/8hkjhk7692f6afed3cb99d43ab2abd4e30aa8cba_2_1378x918.jpeg 2x"><div class="meta">
|
||||
<span class="filename">sherlock3_sig.jpg</span><span class="informations">5496×3664 2 MB</span><span class="expand"></span>
|
||||
</div></a>
|
||||
`;
|
||||
markdown = ``;
|
||||
test("keeps emoji syntax for custom emoji", function (assert) {
|
||||
const html = `
|
||||
<p>
|
||||
<img class="emoji emoji-custom" title=":custom_emoji:" src="https://d11a6trkgmumsb.cloudfront.net/images/emoji/custom_emoji" alt=":custom_emoji:" />
|
||||
</p>
|
||||
`;
|
||||
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
});
|
||||
const markdown = `:custom_emoji:`;
|
||||
|
||||
test("converts quotes to markdown", function (assert) {
|
||||
let html = `
|
||||
<p>there is a quote below</p>
|
||||
<aside class="quote no-group" data-username="foo" data-post="1" data-topic="2">
|
||||
<div class="title" style="cursor: pointer;">
|
||||
<div class="quote-controls"><span class="svg-icon-title" title="expand/collapse"><svg class="fa d-icon d-icon-chevron-down svg-icon svg-string" xmlns="http://www.w3.org/2000/svg"><use xlink:href="#chevron-down"></use></svg></span><a href="/t/hello-world-i-am-posting-an-image/158/1" title="go to the quoted post" class="back"><svg class="fa d-icon d-icon-arrow-up svg-icon svg-string" xmlns="http://www.w3.org/2000/svg"><use xlink:href="#arrow-up"></use></svg></a></div>
|
||||
<img alt="" width="20" height="20" src="" class="avatar"> foo:</div>
|
||||
<blockquote>
|
||||
<p>this is a quote</p>
|
||||
</blockquote>
|
||||
</aside>
|
||||
<p>there is a quote above</p>
|
||||
`;
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
});
|
||||
|
||||
let markdown = `
|
||||
test("converts image lightboxes to markdown", function (assert) {
|
||||
let html = `
|
||||
<a class="lightbox" href="https://d11a6trkgmumsb.cloudfront.net/uploads/default/original/1X/8hkjhk7692f6afed3cb99d43ab2abd4e30aa8cba.jpeg" data-download-href="https://d11a6trkgmumsb.cloudfront.net/uploads/default/8hkjhk7692f6afed3cb99d43ab2abd4e30aa8cba" title="sherlock3_sig.jpg" rel="nofollow noopener"><img src="https://d11a6trkgmumsb.cloudfront.net/uploads/default/optimized/1X/8hkjhk7692f6afed3cb99d43ab2abd4e30aa8cba_2_689x459.jpeg" alt="sherlock3_sig" width="689" height="459" class="d-lazyload" srcset="https://d11a6trkgmumsb.cloudfront.net/uploads/default/optimized/1X/8hkjhk7692f6afed3cb99d43ab2abd4e30aa8cba_2_689x459.jpeg, https://d11a6trkgmumsb.cloudfront.net/uploads/default/optimized/1X/8hkjhk7692f6afed3cb99d43ab2abd4e30aa8cba_2_1033x688.jpeg 1.5x, https://d11a6trkgmumsb.cloudfront.net/uploads/default/optimized/1X/8hkjhk7692f6afed3cb99d43ab2abd4e30aa8cba_2_1378x918.jpeg 2x"><div class="meta">
|
||||
<span class="filename">sherlock3_sig.jpg</span><span class="informations">5496×3664 2 MB</span><span class="expand"></span>
|
||||
</div></a>
|
||||
`;
|
||||
let markdown = ``;
|
||||
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
|
||||
html = `<a class="lightbox" href="https://d11a6trkgmumsb.cloudfront.net/uploads/default/original/1X/8hkjhk7692f6afed3cb99d43ab2abd4e30aa8cba.jpeg" data-download-href="https://d11a6trkgmumsb.cloudfront.net/uploads/default/8hkjhk7692f6afed3cb99d43ab2abd4e30aa8cba" title="sherlock3_sig.jpg" rel="nofollow noopener"><img src="https://d11a6trkgmumsb.cloudfront.net/uploads/default/optimized/1X/8hkjhk7692f6afed3cb99d43ab2abd4e30aa8cba_2_689x459.jpeg" alt="sherlock3_sig" width="689" height="459" class="d-lazyload" srcset="https://d11a6trkgmumsb.cloudfront.net/uploads/default/optimized/1X/8hkjhk7692f6afed3cb99d43ab2abd4e30aa8cba_2_689x459.jpeg, https://d11a6trkgmumsb.cloudfront.net/uploads/default/optimized/1X/8hkjhk7692f6afed3cb99d43ab2abd4e30aa8cba_2_1033x688.jpeg 1.5x, https://d11a6trkgmumsb.cloudfront.net/uploads/default/optimized/1X/8hkjhk7692f6afed3cb99d43ab2abd4e30aa8cba_2_1378x918.jpeg 2x"></a>`;
|
||||
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
|
||||
html = `
|
||||
<a class="lightbox" href="https://d11a6trkgmumsb.cloudfront.net/uploads/default/original/1X/8hkjhk7692f6afed3cb99d43ab2abd4e30aa8cba.jpeg" data-download-href="https://d11a6trkgmumsb.cloudfront.net/uploads/default/8hkjhk7692f6afed3cb99d43ab2abd4e30aa8cba" title="sherlock3_sig.jpg" rel="nofollow noopener"><img src="https://d11a6trkgmumsb.cloudfront.net/uploads/default/optimized/1X/8hkjhk7692f6afed3cb99d43ab2abd4e30aa8cba_2_689x459.jpeg" data-base62-sha1="1frsimI7TOtFJyD2LLyKSHM8JWe" alt="sherlock3_sig" width="689" height="459" class="d-lazyload" srcset="https://d11a6trkgmumsb.cloudfront.net/uploads/default/optimized/1X/8hkjhk7692f6afed3cb99d43ab2abd4e30aa8cba_2_689x459.jpeg, https://d11a6trkgmumsb.cloudfront.net/uploads/default/optimized/1X/8hkjhk7692f6afed3cb99d43ab2abd4e30aa8cba_2_1033x688.jpeg 1.5x, https://d11a6trkgmumsb.cloudfront.net/uploads/default/optimized/1X/8hkjhk7692f6afed3cb99d43ab2abd4e30aa8cba_2_1378x918.jpeg 2x"><div class="meta">
|
||||
<span class="filename">sherlock3_sig.jpg</span><span class="informations">5496×3664 2 MB</span><span class="expand"></span>
|
||||
</div></a>
|
||||
`;
|
||||
markdown = ``;
|
||||
|
||||
assert.equal(toMarkdown(html), markdown);
|
||||
});
|
||||
|
||||
test("converts quotes to markdown", function (assert) {
|
||||
let html = `
|
||||
<p>there is a quote below</p>
|
||||
<aside class="quote no-group" data-username="foo" data-post="1" data-topic="2">
|
||||
<div class="title" style="cursor: pointer;">
|
||||
<div class="quote-controls"><span class="svg-icon-title" title="expand/collapse"><svg class="fa d-icon d-icon-chevron-down svg-icon svg-string" xmlns="http://www.w3.org/2000/svg"><use xlink:href="#chevron-down"></use></svg></span><a href="/t/hello-world-i-am-posting-an-image/158/1" title="go to the quoted post" class="back"><svg class="fa d-icon d-icon-arrow-up svg-icon svg-string" xmlns="http://www.w3.org/2000/svg"><use xlink:href="#arrow-up"></use></svg></a></div>
|
||||
<img alt="" width="20" height="20" src="" class="avatar"> foo:</div>
|
||||
<blockquote>
|
||||
<p>this is a quote</p>
|
||||
</blockquote>
|
||||
</aside>
|
||||
<p>there is a quote above</p>
|
||||
`;
|
||||
|
||||
let markdown = `
|
||||
there is a quote below
|
||||
|
||||
[quote="foo, post:1, topic:2"]
|
||||
@@ -410,11 +409,12 @@ this is a quote
|
||||
there is a quote above
|
||||
`;
|
||||
|
||||
assert.equal(toMarkdown(html), markdown.trim());
|
||||
});
|
||||
assert.equal(toMarkdown(html), markdown.trim());
|
||||
});
|
||||
|
||||
test("strips base64 image URLs", function (assert) {
|
||||
const html =
|
||||
'<img src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAgAAZABkAAD/7AARRHVja3kAAQAEAAAAPAAA/+4AJkFkb2JlAGTAAAAAAQMAFQQDBgoNAAABywAAAgsAAAJpAAACyf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoKDBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8IAEQgAEAAQAwERAAIRAQMRAf/EAJQAAQEBAAAAAAAAAAAAAAAAAAMFBwEAAwEAAAAAAAAAAAAAAAAAAAEDAhAAAQUBAQAAAAAAAAAAAAAAAgABAwQFESARAAIBAwIHAAAAAAAAAAAAAAERAgAhMRIDQWGRocEiIxIBAAAAAAAAAAAAAAAAAAAAIBMBAAMAAQQDAQAAAAAAAAAAAQARITHwQVGBYXGR4f/aAAwDAQACEQMRAAAB0UlMciEJn//aAAgBAQABBQK5bGtFn6pWi2K12wWTRkjb/9oACAECAAEFAvH/2gAIAQMAAQUCIuIJOqRndRiv/9oACAECAgY/Ah//2gAIAQMCBj8CH//aAAgBAQEGPwLWQzwHepfNbcUNfM4tUIbA9QL4AvnxTlAxacpWJReOlf/aAAgBAQMBPyHZDveuCyu4B4lz2lDKto2ca5uclPK0aoq32x8xgTSLeSgbyzT65n//2gAIAQIDAT8hlQjP/9oACAEDAwE/IaE9GcZFJ//aAAwDAQACEQMRAAAQ5F//2gAIAQEDAT8Q1oowKccI3KTdAWkPLw2ssIrwKYUzuJoUJsIHOCoG23ISlja+rU9QvCx//9oACAECAwE/EAuNIiKf/9oACAEDAwE/ECujJzHf7iwHOv5NhK+8efH50z//2Q==" />';
|
||||
assert.equal(toMarkdown(html), "[image]");
|
||||
test("strips base64 image URLs", function (assert) {
|
||||
const html =
|
||||
'<img src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAgAAZABkAAD/7AARRHVja3kAAQAEAAAAPAAA/+4AJkFkb2JlAGTAAAAAAQMAFQQDBgoNAAABywAAAgsAAAJpAAACyf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoKDBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8IAEQgAEAAQAwERAAIRAQMRAf/EAJQAAQEBAAAAAAAAAAAAAAAAAAMFBwEAAwEAAAAAAAAAAAAAAAAAAAEDAhAAAQUBAQAAAAAAAAAAAAAAAgABAwQFESARAAIBAwIHAAAAAAAAAAAAAAERAgAhMRIDQWGRocEiIxIBAAAAAAAAAAAAAAAAAAAAIBMBAAMAAQQDAQAAAAAAAAAAAQARITHwQVGBYXGR4f/aAAwDAQACEQMRAAAB0UlMciEJn//aAAgBAQABBQK5bGtFn6pWi2K12wWTRkjb/9oACAECAAEFAvH/2gAIAQMAAQUCIuIJOqRndRiv/9oACAECAgY/Ah//2gAIAQMCBj8CH//aAAgBAQEGPwLWQzwHepfNbcUNfM4tUIbA9QL4AvnxTlAxacpWJReOlf/aAAgBAQMBPyHZDveuCyu4B4lz2lDKto2ca5uclPK0aoq32x8xgTSLeSgbyzT65n//2gAIAQIDAT8hlQjP/9oACAEDAwE/IaE9GcZFJ//aAAwDAQACEQMRAAAQ5F//2gAIAQEDAT8Q1oowKccI3KTdAWkPLw2ssIrwKYUzuJoUJsIHOCoG23ISlja+rU9QvCx//9oACAECAwE/EAuNIiKf/9oACAEDAwE/ECujJzHf7iwHOv5NhK+8efH50z//2Q==" />';
|
||||
assert.equal(toMarkdown(html), "[image]");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,12 +6,9 @@ import {
|
||||
} from "pretty-text/upload-short-url";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import { fixture } from "discourse/tests/helpers/qunit-helpers";
|
||||
import pretender from "discourse/tests/helpers/create-pretender";
|
||||
import pretender, { response } from "discourse/tests/helpers/create-pretender";
|
||||
|
||||
function stubUrls(imageSrcs, attachmentSrcs, otherMediaSrcs) {
|
||||
const response = (object) => {
|
||||
return [200, { "Content-Type": "application/json" }, object];
|
||||
};
|
||||
if (!imageSrcs) {
|
||||
imageSrcs = [
|
||||
{
|
||||
@@ -61,10 +58,10 @@ function stubUrls(imageSrcs, attachmentSrcs, otherMediaSrcs) {
|
||||
},
|
||||
];
|
||||
}
|
||||
// prettier-ignore
|
||||
pretender.post("/uploads/lookup-urls", () => {
|
||||
return response(imageSrcs.concat(attachmentSrcs.concat(otherMediaSrcs)));
|
||||
});
|
||||
|
||||
pretender.post("/uploads/lookup-urls", () =>
|
||||
response(imageSrcs.concat(attachmentSrcs.concat(otherMediaSrcs)))
|
||||
);
|
||||
|
||||
fixture().html(
|
||||
imageSrcs.map((src) => `<img data-orig-src="${src.short_url}"/>`).join("") +
|
||||
@@ -86,137 +83,138 @@ function stubUrls(imageSrcs, attachmentSrcs, otherMediaSrcs) {
|
||||
.join("")
|
||||
);
|
||||
}
|
||||
module("lib:pretty-text/upload-short-url", {
|
||||
afterEach() {
|
||||
|
||||
module("Unit | Utility | pretty-text/upload-short-url", function (hooks) {
|
||||
hooks.afterEach(function () {
|
||||
resetCache();
|
||||
},
|
||||
});
|
||||
|
||||
test("resolveAllShortUrls", async function (assert) {
|
||||
stubUrls();
|
||||
let lookup;
|
||||
|
||||
lookup = lookupCachedUploadUrl("upload://a.jpeg");
|
||||
assert.deepEqual(lookup, {});
|
||||
|
||||
await resolveAllShortUrls(ajax, { secure_media: false }, fixture()[0]);
|
||||
|
||||
lookup = lookupCachedUploadUrl("upload://a.jpeg");
|
||||
|
||||
assert.deepEqual(lookup, {
|
||||
url: "/images/avatar.png?a",
|
||||
short_path: "/uploads/short-url/a.jpeg",
|
||||
});
|
||||
|
||||
lookup = lookupCachedUploadUrl("upload://b.jpeg");
|
||||
test("resolveAllShortUrls", async function (assert) {
|
||||
stubUrls();
|
||||
let lookup;
|
||||
|
||||
assert.deepEqual(lookup, {
|
||||
url: "/images/avatar.png?b",
|
||||
short_path: "/uploads/short-url/b.jpeg",
|
||||
lookup = lookupCachedUploadUrl("upload://a.jpeg");
|
||||
assert.deepEqual(lookup, {});
|
||||
|
||||
await resolveAllShortUrls(ajax, { secure_media: false }, fixture()[0]);
|
||||
|
||||
lookup = lookupCachedUploadUrl("upload://a.jpeg");
|
||||
|
||||
assert.deepEqual(lookup, {
|
||||
url: "/images/avatar.png?a",
|
||||
short_path: "/uploads/short-url/a.jpeg",
|
||||
});
|
||||
|
||||
lookup = lookupCachedUploadUrl("upload://b.jpeg");
|
||||
|
||||
assert.deepEqual(lookup, {
|
||||
url: "/images/avatar.png?b",
|
||||
short_path: "/uploads/short-url/b.jpeg",
|
||||
});
|
||||
|
||||
lookup = lookupCachedUploadUrl("upload://c.jpeg");
|
||||
assert.deepEqual(lookup, {});
|
||||
|
||||
lookup = lookupCachedUploadUrl("upload://c.pdf");
|
||||
assert.deepEqual(lookup, {
|
||||
url: "/uploads/default/original/3X/c/b/3.pdf",
|
||||
short_path: "/uploads/short-url/c.pdf",
|
||||
});
|
||||
|
||||
lookup = lookupCachedUploadUrl("upload://d.mp4");
|
||||
assert.deepEqual(lookup, {
|
||||
url: "/uploads/default/original/3X/c/b/4.mp4",
|
||||
short_path: "/uploads/short-url/d.mp4",
|
||||
});
|
||||
|
||||
lookup = lookupCachedUploadUrl("upload://e.mp3");
|
||||
assert.deepEqual(lookup, {
|
||||
url: "/uploads/default/original/3X/c/b/5.mp3",
|
||||
short_path: "/uploads/short-url/e.mp3",
|
||||
});
|
||||
|
||||
lookup = lookupCachedUploadUrl("upload://f.mp4");
|
||||
assert.deepEqual(lookup, {
|
||||
url: "http://localhost:3000/uploads/default/original/3X/c/b/6.mp4",
|
||||
short_path: "/uploads/short-url/f.mp4",
|
||||
});
|
||||
});
|
||||
|
||||
lookup = lookupCachedUploadUrl("upload://c.jpeg");
|
||||
assert.deepEqual(lookup, {});
|
||||
test("resolveAllShortUrls - href + src replaced correctly", async function (assert) {
|
||||
stubUrls();
|
||||
await resolveAllShortUrls(ajax, { secure_media: false }, fixture()[0]);
|
||||
|
||||
lookup = lookupCachedUploadUrl("upload://c.pdf");
|
||||
assert.deepEqual(lookup, {
|
||||
url: "/uploads/default/original/3X/c/b/3.pdf",
|
||||
short_path: "/uploads/short-url/c.pdf",
|
||||
let image1 = fixture().find("img").eq(0);
|
||||
let image2 = fixture().find("img").eq(1);
|
||||
let link = fixture().find("a");
|
||||
let audio = fixture().find("audio").eq(0);
|
||||
let video = fixture().find("video").eq(0);
|
||||
|
||||
assert.equal(image1.attr("src"), "/images/avatar.png?a");
|
||||
assert.equal(image2.attr("src"), "/images/avatar.png?b");
|
||||
assert.equal(link.attr("href"), "/uploads/short-url/c.pdf");
|
||||
assert.equal(
|
||||
video.find("source").attr("src"),
|
||||
"/uploads/default/original/3X/c/b/4.mp4"
|
||||
);
|
||||
assert.equal(
|
||||
audio.find("source").attr("src"),
|
||||
"/uploads/default/original/3X/c/b/5.mp3"
|
||||
);
|
||||
});
|
||||
|
||||
lookup = lookupCachedUploadUrl("upload://d.mp4");
|
||||
assert.deepEqual(lookup, {
|
||||
url: "/uploads/default/original/3X/c/b/4.mp4",
|
||||
short_path: "/uploads/short-url/d.mp4",
|
||||
test("resolveAllShortUrls - url with full origin replaced correctly", async function (assert) {
|
||||
stubUrls();
|
||||
await resolveAllShortUrls(ajax, { secure_media: false }, fixture()[0]);
|
||||
let video = fixture().find("video").eq(1);
|
||||
|
||||
assert.equal(
|
||||
video.find("source").attr("src"),
|
||||
"http://localhost:3000/uploads/default/original/3X/c/b/6.mp4"
|
||||
);
|
||||
});
|
||||
|
||||
lookup = lookupCachedUploadUrl("upload://e.mp3");
|
||||
assert.deepEqual(lookup, {
|
||||
url: "/uploads/default/original/3X/c/b/5.mp3",
|
||||
short_path: "/uploads/short-url/e.mp3",
|
||||
test("resolveAllShortUrls - when secure media is enabled use the attachment full URL", async function (assert) {
|
||||
stubUrls(
|
||||
null,
|
||||
[
|
||||
{
|
||||
short_url: "upload://c.pdf",
|
||||
url: "/secure-media-uploads/default/original/3X/c/b/3.pdf",
|
||||
short_path: "/uploads/short-url/c.pdf",
|
||||
},
|
||||
],
|
||||
null
|
||||
);
|
||||
await resolveAllShortUrls(ajax, { secure_media: true }, fixture()[0]);
|
||||
|
||||
let link = fixture().find("a");
|
||||
assert.equal(
|
||||
link.attr("href"),
|
||||
"/secure-media-uploads/default/original/3X/c/b/3.pdf"
|
||||
);
|
||||
});
|
||||
|
||||
lookup = lookupCachedUploadUrl("upload://f.mp4");
|
||||
assert.deepEqual(lookup, {
|
||||
url: "http://localhost:3000/uploads/default/original/3X/c/b/6.mp4",
|
||||
short_path: "/uploads/short-url/f.mp4",
|
||||
test("resolveAllShortUrls - scoped", async function (assert) {
|
||||
stubUrls();
|
||||
let lookup;
|
||||
|
||||
let scopedElement = fixture()[0].querySelector(".scoped-area");
|
||||
await resolveAllShortUrls(ajax, {}, scopedElement);
|
||||
|
||||
lookup = lookupCachedUploadUrl("upload://z.jpeg");
|
||||
|
||||
assert.deepEqual(lookup, {
|
||||
url: "/images/avatar.png?z",
|
||||
short_path: "/uploads/short-url/z.jpeg",
|
||||
});
|
||||
|
||||
// do this because the pretender caches ALL the urls, not
|
||||
// just the ones being looked up (like the normal behaviour)
|
||||
resetCache();
|
||||
await resolveAllShortUrls(ajax, {}, scopedElement);
|
||||
|
||||
lookup = lookupCachedUploadUrl("upload://a.jpeg");
|
||||
assert.deepEqual(lookup, {});
|
||||
});
|
||||
});
|
||||
|
||||
test("resolveAllShortUrls - href + src replaced correctly", async function (assert) {
|
||||
stubUrls();
|
||||
await resolveAllShortUrls(ajax, { secure_media: false }, fixture()[0]);
|
||||
|
||||
let image1 = fixture().find("img").eq(0);
|
||||
let image2 = fixture().find("img").eq(1);
|
||||
let link = fixture().find("a");
|
||||
let audio = fixture().find("audio").eq(0);
|
||||
let video = fixture().find("video").eq(0);
|
||||
|
||||
assert.equal(image1.attr("src"), "/images/avatar.png?a");
|
||||
assert.equal(image2.attr("src"), "/images/avatar.png?b");
|
||||
assert.equal(link.attr("href"), "/uploads/short-url/c.pdf");
|
||||
assert.equal(
|
||||
video.find("source").attr("src"),
|
||||
"/uploads/default/original/3X/c/b/4.mp4"
|
||||
);
|
||||
assert.equal(
|
||||
audio.find("source").attr("src"),
|
||||
"/uploads/default/original/3X/c/b/5.mp3"
|
||||
);
|
||||
});
|
||||
|
||||
test("resolveAllShortUrls - url with full origin replaced correctly", async function (assert) {
|
||||
stubUrls();
|
||||
await resolveAllShortUrls(ajax, { secure_media: false }, fixture()[0]);
|
||||
let video = fixture().find("video").eq(1);
|
||||
|
||||
assert.equal(
|
||||
video.find("source").attr("src"),
|
||||
"http://localhost:3000/uploads/default/original/3X/c/b/6.mp4"
|
||||
);
|
||||
});
|
||||
|
||||
test("resolveAllShortUrls - when secure media is enabled use the attachment full URL", async function (assert) {
|
||||
stubUrls(
|
||||
null,
|
||||
[
|
||||
{
|
||||
short_url: "upload://c.pdf",
|
||||
url: "/secure-media-uploads/default/original/3X/c/b/3.pdf",
|
||||
short_path: "/uploads/short-url/c.pdf",
|
||||
},
|
||||
],
|
||||
null
|
||||
);
|
||||
await resolveAllShortUrls(ajax, { secure_media: true }, fixture()[0]);
|
||||
|
||||
let link = fixture().find("a");
|
||||
assert.equal(
|
||||
link.attr("href"),
|
||||
"/secure-media-uploads/default/original/3X/c/b/3.pdf"
|
||||
);
|
||||
});
|
||||
|
||||
test("resolveAllShortUrls - scoped", async function (assert) {
|
||||
stubUrls();
|
||||
let lookup;
|
||||
|
||||
let scopedElement = fixture()[0].querySelector(".scoped-area");
|
||||
await resolveAllShortUrls(ajax, {}, scopedElement);
|
||||
|
||||
lookup = lookupCachedUploadUrl("upload://z.jpeg");
|
||||
|
||||
assert.deepEqual(lookup, {
|
||||
url: "/images/avatar.png?z",
|
||||
short_path: "/uploads/short-url/z.jpeg",
|
||||
});
|
||||
|
||||
// do this because the pretender caches ALL the urls, not
|
||||
// just the ones being looked up (like the normal behaviour)
|
||||
resetCache();
|
||||
await resolveAllShortUrls(ajax, {}, scopedElement);
|
||||
|
||||
lookup = lookupCachedUploadUrl("upload://a.jpeg");
|
||||
assert.deepEqual(lookup, {});
|
||||
});
|
||||
|
||||
@@ -14,299 +14,299 @@ import User from "discourse/models/user";
|
||||
import { discourseModule } from "discourse/tests/helpers/qunit-helpers";
|
||||
import bootbox from "bootbox";
|
||||
|
||||
discourseModule("lib:uploads");
|
||||
|
||||
test("validateUploadedFiles", function (assert) {
|
||||
assert.not(
|
||||
validateUploadedFiles(null, { siteSettings: this.siteSettings }),
|
||||
"no files are invalid"
|
||||
);
|
||||
assert.not(
|
||||
validateUploadedFiles(undefined, { siteSettings: this.siteSettings }),
|
||||
"undefined files are invalid"
|
||||
);
|
||||
assert.not(
|
||||
validateUploadedFiles([], { siteSettings: this.siteSettings }),
|
||||
"empty array of files is invalid"
|
||||
);
|
||||
});
|
||||
|
||||
test("uploading one file", function (assert) {
|
||||
sinon.stub(bootbox, "alert");
|
||||
|
||||
assert.not(
|
||||
validateUploadedFiles([1, 2], { siteSettings: this.siteSettings })
|
||||
);
|
||||
assert.ok(bootbox.alert.calledWith(I18n.t("post.errors.too_many_uploads")));
|
||||
});
|
||||
|
||||
test("new user cannot upload images", function (assert) {
|
||||
this.siteSettings.newuser_max_embedded_media = 0;
|
||||
sinon.stub(bootbox, "alert");
|
||||
|
||||
assert.not(
|
||||
validateUploadedFiles([{ name: "image.png" }], {
|
||||
user: User.create(),
|
||||
siteSettings: this.siteSettings,
|
||||
}),
|
||||
"the upload is not valid"
|
||||
);
|
||||
assert.ok(
|
||||
bootbox.alert.calledWith(
|
||||
I18n.t("post.errors.image_upload_not_allowed_for_new_user")
|
||||
),
|
||||
"the alert is called"
|
||||
);
|
||||
});
|
||||
|
||||
test("new user can upload images if allowed", function (assert) {
|
||||
this.siteSettings.newuser_max_embedded_media = 1;
|
||||
this.siteSettings.default_trust_level = 0;
|
||||
sinon.stub(bootbox, "alert");
|
||||
|
||||
assert.ok(
|
||||
validateUploadedFiles([{ name: "image.png" }], {
|
||||
user: User.create(),
|
||||
siteSettings: this.siteSettings,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test("TL1 can upload images", function (assert) {
|
||||
this.siteSettings.newuser_max_embedded_media = 0;
|
||||
sinon.stub(bootbox, "alert");
|
||||
|
||||
assert.ok(
|
||||
validateUploadedFiles([{ name: "image.png" }], {
|
||||
user: User.create({ trust_level: 1 }),
|
||||
siteSettings: this.siteSettings,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test("new user cannot upload attachments", function (assert) {
|
||||
this.siteSettings.newuser_max_attachments = 0;
|
||||
sinon.stub(bootbox, "alert");
|
||||
|
||||
assert.not(
|
||||
validateUploadedFiles([{ name: "roman.txt" }], {
|
||||
user: User.create(),
|
||||
siteSettings: this.siteSettings,
|
||||
})
|
||||
);
|
||||
assert.ok(
|
||||
bootbox.alert.calledWith(
|
||||
I18n.t("post.errors.attachment_upload_not_allowed_for_new_user")
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
test("ensures an authorized upload", function (assert) {
|
||||
sinon.stub(bootbox, "alert");
|
||||
assert.not(
|
||||
validateUploadedFiles([{ name: "unauthorized.html" }], {
|
||||
siteSettings: this.siteSettings,
|
||||
})
|
||||
);
|
||||
assert.ok(
|
||||
bootbox.alert.calledWith(
|
||||
I18n.t("post.errors.upload_not_authorized", {
|
||||
authorized_extensions: authorizedExtensions(false, this.siteSettings),
|
||||
})
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
test("skipping validation works", function (assert) {
|
||||
const files = [{ name: "backup.tar.gz" }];
|
||||
sinon.stub(bootbox, "alert");
|
||||
|
||||
assert.not(
|
||||
validateUploadedFiles(files, {
|
||||
skipValidation: false,
|
||||
siteSettings: this.siteSettings,
|
||||
})
|
||||
);
|
||||
assert.ok(
|
||||
validateUploadedFiles(files, {
|
||||
skipValidation: true,
|
||||
siteSettings: this.siteSettings,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test("staff can upload anything in PM", function (assert) {
|
||||
const files = [{ name: "some.docx" }];
|
||||
this.siteSettings.authorized_extensions = "jpeg";
|
||||
sinon.stub(bootbox, "alert");
|
||||
|
||||
let user = User.create({ moderator: true });
|
||||
assert.not(
|
||||
validateUploadedFiles(files, { user, siteSettings: this.siteSettings })
|
||||
);
|
||||
assert.ok(
|
||||
validateUploadedFiles(files, {
|
||||
isPrivateMessage: true,
|
||||
allowStaffToUploadAnyFileInPm: true,
|
||||
siteSettings: this.siteSettings,
|
||||
user,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
const imageSize = 10 * 1024;
|
||||
|
||||
const dummyBlob = function () {
|
||||
const BlobBuilder =
|
||||
window.BlobBuilder ||
|
||||
window.WebKitBlobBuilder ||
|
||||
window.MozBlobBuilder ||
|
||||
window.MSBlobBuilder;
|
||||
if (BlobBuilder) {
|
||||
let bb = new BlobBuilder();
|
||||
bb.append([new Int8Array(imageSize)]);
|
||||
return bb.getBlob("image/png");
|
||||
} else {
|
||||
return new Blob([new Int8Array(imageSize)], { type: "image/png" });
|
||||
}
|
||||
};
|
||||
|
||||
test("allows valid uploads to go through", function (assert) {
|
||||
sinon.stub(bootbox, "alert");
|
||||
|
||||
let user = User.create({ trust_level: 1 });
|
||||
|
||||
// image
|
||||
let image = { name: "image.png", size: imageSize };
|
||||
assert.ok(
|
||||
validateUploadedFiles([image], { user, siteSettings: this.siteSettings })
|
||||
);
|
||||
// pasted image
|
||||
let pastedImage = dummyBlob();
|
||||
assert.ok(
|
||||
validateUploadedFiles([pastedImage], {
|
||||
user,
|
||||
siteSettings: this.siteSettings,
|
||||
})
|
||||
);
|
||||
|
||||
assert.not(bootbox.alert.calledOnce);
|
||||
});
|
||||
|
||||
test("isImage", function (assert) {
|
||||
["png", "webp", "jpg", "jpeg", "gif", "ico"].forEach((extension) => {
|
||||
var image = "image." + extension;
|
||||
assert.ok(isImage(image), image + " is recognized as an image");
|
||||
assert.ok(
|
||||
isImage("http://foo.bar/path/to/" + image),
|
||||
image + " is recognized as an image"
|
||||
discourseModule("Unit | Utility | uploads", function () {
|
||||
test("validateUploadedFiles", function (assert) {
|
||||
assert.not(
|
||||
validateUploadedFiles(null, { siteSettings: this.siteSettings }),
|
||||
"no files are invalid"
|
||||
);
|
||||
assert.not(
|
||||
validateUploadedFiles(undefined, { siteSettings: this.siteSettings }),
|
||||
"undefined files are invalid"
|
||||
);
|
||||
assert.not(
|
||||
validateUploadedFiles([], { siteSettings: this.siteSettings }),
|
||||
"empty array of files is invalid"
|
||||
);
|
||||
});
|
||||
|
||||
test("uploading one file", function (assert) {
|
||||
sinon.stub(bootbox, "alert");
|
||||
|
||||
assert.not(
|
||||
validateUploadedFiles([1, 2], { siteSettings: this.siteSettings })
|
||||
);
|
||||
assert.ok(bootbox.alert.calledWith(I18n.t("post.errors.too_many_uploads")));
|
||||
});
|
||||
|
||||
test("new user cannot upload images", function (assert) {
|
||||
this.siteSettings.newuser_max_embedded_media = 0;
|
||||
sinon.stub(bootbox, "alert");
|
||||
|
||||
assert.not(
|
||||
validateUploadedFiles([{ name: "image.png" }], {
|
||||
user: User.create(),
|
||||
siteSettings: this.siteSettings,
|
||||
}),
|
||||
"the upload is not valid"
|
||||
);
|
||||
assert.ok(
|
||||
bootbox.alert.calledWith(
|
||||
I18n.t("post.errors.image_upload_not_allowed_for_new_user")
|
||||
),
|
||||
"the alert is called"
|
||||
);
|
||||
});
|
||||
|
||||
test("new user can upload images if allowed", function (assert) {
|
||||
this.siteSettings.newuser_max_embedded_media = 1;
|
||||
this.siteSettings.default_trust_level = 0;
|
||||
sinon.stub(bootbox, "alert");
|
||||
|
||||
assert.ok(
|
||||
validateUploadedFiles([{ name: "image.png" }], {
|
||||
user: User.create(),
|
||||
siteSettings: this.siteSettings,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test("TL1 can upload images", function (assert) {
|
||||
this.siteSettings.newuser_max_embedded_media = 0;
|
||||
sinon.stub(bootbox, "alert");
|
||||
|
||||
assert.ok(
|
||||
validateUploadedFiles([{ name: "image.png" }], {
|
||||
user: User.create({ trust_level: 1 }),
|
||||
siteSettings: this.siteSettings,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test("new user cannot upload attachments", function (assert) {
|
||||
this.siteSettings.newuser_max_attachments = 0;
|
||||
sinon.stub(bootbox, "alert");
|
||||
|
||||
assert.not(
|
||||
validateUploadedFiles([{ name: "roman.txt" }], {
|
||||
user: User.create(),
|
||||
siteSettings: this.siteSettings,
|
||||
})
|
||||
);
|
||||
assert.ok(
|
||||
bootbox.alert.calledWith(
|
||||
I18n.t("post.errors.attachment_upload_not_allowed_for_new_user")
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
test("ensures an authorized upload", function (assert) {
|
||||
sinon.stub(bootbox, "alert");
|
||||
assert.not(
|
||||
validateUploadedFiles([{ name: "unauthorized.html" }], {
|
||||
siteSettings: this.siteSettings,
|
||||
})
|
||||
);
|
||||
assert.ok(
|
||||
bootbox.alert.calledWith(
|
||||
I18n.t("post.errors.upload_not_authorized", {
|
||||
authorized_extensions: authorizedExtensions(false, this.siteSettings),
|
||||
})
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
test("skipping validation works", function (assert) {
|
||||
const files = [{ name: "backup.tar.gz" }];
|
||||
sinon.stub(bootbox, "alert");
|
||||
|
||||
assert.not(
|
||||
validateUploadedFiles(files, {
|
||||
skipValidation: false,
|
||||
siteSettings: this.siteSettings,
|
||||
})
|
||||
);
|
||||
assert.ok(
|
||||
validateUploadedFiles(files, {
|
||||
skipValidation: true,
|
||||
siteSettings: this.siteSettings,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test("staff can upload anything in PM", function (assert) {
|
||||
const files = [{ name: "some.docx" }];
|
||||
this.siteSettings.authorized_extensions = "jpeg";
|
||||
sinon.stub(bootbox, "alert");
|
||||
|
||||
let user = User.create({ moderator: true });
|
||||
assert.not(
|
||||
validateUploadedFiles(files, { user, siteSettings: this.siteSettings })
|
||||
);
|
||||
assert.ok(
|
||||
validateUploadedFiles(files, {
|
||||
isPrivateMessage: true,
|
||||
allowStaffToUploadAnyFileInPm: true,
|
||||
siteSettings: this.siteSettings,
|
||||
user,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
const imageSize = 10 * 1024;
|
||||
|
||||
const dummyBlob = function () {
|
||||
const BlobBuilder =
|
||||
window.BlobBuilder ||
|
||||
window.WebKitBlobBuilder ||
|
||||
window.MozBlobBuilder ||
|
||||
window.MSBlobBuilder;
|
||||
if (BlobBuilder) {
|
||||
let bb = new BlobBuilder();
|
||||
bb.append([new Int8Array(imageSize)]);
|
||||
return bb.getBlob("image/png");
|
||||
} else {
|
||||
return new Blob([new Int8Array(imageSize)], { type: "image/png" });
|
||||
}
|
||||
};
|
||||
|
||||
test("allows valid uploads to go through", function (assert) {
|
||||
sinon.stub(bootbox, "alert");
|
||||
|
||||
let user = User.create({ trust_level: 1 });
|
||||
|
||||
// image
|
||||
let image = { name: "image.png", size: imageSize };
|
||||
assert.ok(
|
||||
validateUploadedFiles([image], { user, siteSettings: this.siteSettings })
|
||||
);
|
||||
// pasted image
|
||||
let pastedImage = dummyBlob();
|
||||
assert.ok(
|
||||
validateUploadedFiles([pastedImage], {
|
||||
user,
|
||||
siteSettings: this.siteSettings,
|
||||
})
|
||||
);
|
||||
|
||||
assert.not(bootbox.alert.calledOnce);
|
||||
});
|
||||
|
||||
test("isImage", function (assert) {
|
||||
["png", "webp", "jpg", "jpeg", "gif", "ico"].forEach((extension) => {
|
||||
var image = "image." + extension;
|
||||
assert.ok(isImage(image), image + " is recognized as an image");
|
||||
assert.ok(
|
||||
isImage("http://foo.bar/path/to/" + image),
|
||||
image + " is recognized as an image"
|
||||
);
|
||||
});
|
||||
assert.not(isImage("file.txt"));
|
||||
assert.not(isImage("http://foo.bar/path/to/file.txt"));
|
||||
assert.not(isImage(""));
|
||||
});
|
||||
|
||||
test("allowsImages", function (assert) {
|
||||
this.siteSettings.authorized_extensions = "jpg|jpeg|gif";
|
||||
assert.ok(allowsImages(false, this.siteSettings), "works");
|
||||
|
||||
this.siteSettings.authorized_extensions = ".jpg|.jpeg|.gif";
|
||||
assert.ok(
|
||||
allowsImages(false, this.siteSettings),
|
||||
"works with old extensions syntax"
|
||||
);
|
||||
|
||||
this.siteSettings.authorized_extensions = "txt|pdf|*";
|
||||
assert.ok(
|
||||
allowsImages(false, this.siteSettings),
|
||||
"images are allowed when all extensions are allowed"
|
||||
);
|
||||
|
||||
this.siteSettings.authorized_extensions = "json|jpg|pdf|txt";
|
||||
assert.ok(
|
||||
allowsImages(false, this.siteSettings),
|
||||
"images are allowed when at least one extension is an image extension"
|
||||
);
|
||||
});
|
||||
|
||||
test("allowsAttachments", function (assert) {
|
||||
this.siteSettings.authorized_extensions = "jpg|jpeg|gif";
|
||||
assert.not(
|
||||
allowsAttachments(false, this.siteSettings),
|
||||
"no attachments allowed by default"
|
||||
);
|
||||
|
||||
this.siteSettings.authorized_extensions = "jpg|jpeg|gif|*";
|
||||
assert.ok(
|
||||
allowsAttachments(false, this.siteSettings),
|
||||
"attachments are allowed when all extensions are allowed"
|
||||
);
|
||||
|
||||
this.siteSettings.authorized_extensions = "jpg|jpeg|gif|pdf";
|
||||
assert.ok(
|
||||
allowsAttachments(false, this.siteSettings),
|
||||
"attachments are allowed when at least one extension is not an image extension"
|
||||
);
|
||||
|
||||
this.siteSettings.authorized_extensions = ".jpg|.jpeg|.gif|.pdf";
|
||||
assert.ok(
|
||||
allowsAttachments(false, this.siteSettings),
|
||||
"works with old extensions syntax"
|
||||
);
|
||||
});
|
||||
|
||||
function testUploadMarkdown(filename, opts = {}) {
|
||||
return getUploadMarkdown(
|
||||
Object.assign(
|
||||
{
|
||||
original_filename: filename,
|
||||
filesize: 42,
|
||||
thumbnail_width: 100,
|
||||
thumbnail_height: 200,
|
||||
url: "/uploads/123/abcdef.ext",
|
||||
},
|
||||
opts
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
test("getUploadMarkdown", function (assert) {
|
||||
assert.equal(
|
||||
testUploadMarkdown("lolcat.gif"),
|
||||
""
|
||||
);
|
||||
assert.equal(
|
||||
testUploadMarkdown("[foo|bar].png"),
|
||||
""
|
||||
);
|
||||
assert.equal(
|
||||
testUploadMarkdown("file name with space.png"),
|
||||
""
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
testUploadMarkdown("image.file.name.with.dots.png"),
|
||||
""
|
||||
);
|
||||
|
||||
const short_url = "uploads://asdaasd.ext";
|
||||
|
||||
assert.equal(
|
||||
testUploadMarkdown("important.txt", { short_url }),
|
||||
`[important.txt|attachment](${short_url}) (42 Bytes)`
|
||||
);
|
||||
});
|
||||
|
||||
test("getUploadMarkdown - replaces GUID in image alt text on iOS", function (assert) {
|
||||
assert.equal(
|
||||
testUploadMarkdown("8F2B469B-6B2C-4213-BC68-57B4876365A0.jpeg"),
|
||||
""
|
||||
);
|
||||
|
||||
sinon.stub(Utilities, "isAppleDevice").returns(true);
|
||||
assert.equal(
|
||||
testUploadMarkdown("8F2B469B-6B2C-4213-BC68-57B4876365A0.jpeg"),
|
||||
""
|
||||
);
|
||||
});
|
||||
assert.not(isImage("file.txt"));
|
||||
assert.not(isImage("http://foo.bar/path/to/file.txt"));
|
||||
assert.not(isImage(""));
|
||||
});
|
||||
|
||||
test("allowsImages", function (assert) {
|
||||
this.siteSettings.authorized_extensions = "jpg|jpeg|gif";
|
||||
assert.ok(allowsImages(false, this.siteSettings), "works");
|
||||
|
||||
this.siteSettings.authorized_extensions = ".jpg|.jpeg|.gif";
|
||||
assert.ok(
|
||||
allowsImages(false, this.siteSettings),
|
||||
"works with old extensions syntax"
|
||||
);
|
||||
|
||||
this.siteSettings.authorized_extensions = "txt|pdf|*";
|
||||
assert.ok(
|
||||
allowsImages(false, this.siteSettings),
|
||||
"images are allowed when all extensions are allowed"
|
||||
);
|
||||
|
||||
this.siteSettings.authorized_extensions = "json|jpg|pdf|txt";
|
||||
assert.ok(
|
||||
allowsImages(false, this.siteSettings),
|
||||
"images are allowed when at least one extension is an image extension"
|
||||
);
|
||||
});
|
||||
|
||||
test("allowsAttachments", function (assert) {
|
||||
this.siteSettings.authorized_extensions = "jpg|jpeg|gif";
|
||||
assert.not(
|
||||
allowsAttachments(false, this.siteSettings),
|
||||
"no attachments allowed by default"
|
||||
);
|
||||
|
||||
this.siteSettings.authorized_extensions = "jpg|jpeg|gif|*";
|
||||
assert.ok(
|
||||
allowsAttachments(false, this.siteSettings),
|
||||
"attachments are allowed when all extensions are allowed"
|
||||
);
|
||||
|
||||
this.siteSettings.authorized_extensions = "jpg|jpeg|gif|pdf";
|
||||
assert.ok(
|
||||
allowsAttachments(false, this.siteSettings),
|
||||
"attachments are allowed when at least one extension is not an image extension"
|
||||
);
|
||||
|
||||
this.siteSettings.authorized_extensions = ".jpg|.jpeg|.gif|.pdf";
|
||||
assert.ok(
|
||||
allowsAttachments(false, this.siteSettings),
|
||||
"works with old extensions syntax"
|
||||
);
|
||||
});
|
||||
|
||||
function testUploadMarkdown(filename, opts = {}) {
|
||||
return getUploadMarkdown(
|
||||
Object.assign(
|
||||
{
|
||||
original_filename: filename,
|
||||
filesize: 42,
|
||||
thumbnail_width: 100,
|
||||
thumbnail_height: 200,
|
||||
url: "/uploads/123/abcdef.ext",
|
||||
},
|
||||
opts
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
test("getUploadMarkdown", function (assert) {
|
||||
assert.equal(
|
||||
testUploadMarkdown("lolcat.gif"),
|
||||
""
|
||||
);
|
||||
assert.equal(
|
||||
testUploadMarkdown("[foo|bar].png"),
|
||||
""
|
||||
);
|
||||
assert.equal(
|
||||
testUploadMarkdown("file name with space.png"),
|
||||
""
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
testUploadMarkdown("image.file.name.with.dots.png"),
|
||||
""
|
||||
);
|
||||
|
||||
const short_url = "uploads://asdaasd.ext";
|
||||
|
||||
assert.equal(
|
||||
testUploadMarkdown("important.txt", { short_url }),
|
||||
`[important.txt|attachment](${short_url}) (42 Bytes)`
|
||||
);
|
||||
});
|
||||
|
||||
test("getUploadMarkdown - replaces GUID in image alt text on iOS", function (assert) {
|
||||
assert.equal(
|
||||
testUploadMarkdown("8F2B469B-6B2C-4213-BC68-57B4876365A0.jpeg"),
|
||||
""
|
||||
);
|
||||
|
||||
sinon.stub(Utilities, "isAppleDevice").returns(true);
|
||||
assert.equal(
|
||||
testUploadMarkdown("8F2B469B-6B2C-4213-BC68-57B4876365A0.jpeg"),
|
||||
""
|
||||
);
|
||||
});
|
||||
|
||||
@@ -5,95 +5,95 @@ import { setPrefix } from "discourse-common/lib/get-url";
|
||||
import { logIn } from "discourse/tests/helpers/qunit-helpers";
|
||||
import User from "discourse/models/user";
|
||||
|
||||
module("lib:url");
|
||||
module("Unit | Utility | url", function () {
|
||||
test("isInternal with a HTTP url", function (assert) {
|
||||
sinon.stub(DiscourseURL, "origin").returns("http://eviltrout.com");
|
||||
|
||||
test("isInternal with a HTTP url", function (assert) {
|
||||
sinon.stub(DiscourseURL, "origin").returns("http://eviltrout.com");
|
||||
assert.not(DiscourseURL.isInternal(null), "a blank URL is not internal");
|
||||
assert.ok(DiscourseURL.isInternal("/test"), "relative URLs are internal");
|
||||
assert.ok(
|
||||
DiscourseURL.isInternal("//eviltrout.com"),
|
||||
"a url on the same host is internal (protocol-less)"
|
||||
);
|
||||
assert.ok(
|
||||
DiscourseURL.isInternal("http://eviltrout.com/tophat"),
|
||||
"a url on the same host is internal"
|
||||
);
|
||||
assert.ok(
|
||||
DiscourseURL.isInternal("https://eviltrout.com/moustache"),
|
||||
"a url on a HTTPS of the same host is internal"
|
||||
);
|
||||
assert.not(
|
||||
DiscourseURL.isInternal("//twitter.com.com"),
|
||||
"a different host is not internal (protocol-less)"
|
||||
);
|
||||
assert.not(
|
||||
DiscourseURL.isInternal("http://twitter.com"),
|
||||
"a different host is not internal"
|
||||
);
|
||||
});
|
||||
|
||||
assert.not(DiscourseURL.isInternal(null), "a blank URL is not internal");
|
||||
assert.ok(DiscourseURL.isInternal("/test"), "relative URLs are internal");
|
||||
assert.ok(
|
||||
DiscourseURL.isInternal("//eviltrout.com"),
|
||||
"a url on the same host is internal (protocol-less)"
|
||||
);
|
||||
assert.ok(
|
||||
DiscourseURL.isInternal("http://eviltrout.com/tophat"),
|
||||
"a url on the same host is internal"
|
||||
);
|
||||
assert.ok(
|
||||
DiscourseURL.isInternal("https://eviltrout.com/moustache"),
|
||||
"a url on a HTTPS of the same host is internal"
|
||||
);
|
||||
assert.not(
|
||||
DiscourseURL.isInternal("//twitter.com.com"),
|
||||
"a different host is not internal (protocol-less)"
|
||||
);
|
||||
assert.not(
|
||||
DiscourseURL.isInternal("http://twitter.com"),
|
||||
"a different host is not internal"
|
||||
);
|
||||
});
|
||||
|
||||
test("isInternal with a HTTPS url", function (assert) {
|
||||
sinon.stub(DiscourseURL, "origin").returns("https://eviltrout.com");
|
||||
assert.ok(
|
||||
DiscourseURL.isInternal("http://eviltrout.com/monocle"),
|
||||
"HTTPS urls match HTTP urls"
|
||||
);
|
||||
});
|
||||
|
||||
test("isInternal on subfolder install", function (assert) {
|
||||
sinon.stub(DiscourseURL, "origin").returns("http://eviltrout.com/forum");
|
||||
assert.not(
|
||||
DiscourseURL.isInternal("http://eviltrout.com"),
|
||||
"the host root is not internal"
|
||||
);
|
||||
assert.not(
|
||||
DiscourseURL.isInternal("http://eviltrout.com/tophat"),
|
||||
"a url on the same host but on a different folder is not internal"
|
||||
);
|
||||
assert.ok(
|
||||
DiscourseURL.isInternal("http://eviltrout.com/forum/moustache"),
|
||||
"a url on the same host and on the same folder is internal"
|
||||
);
|
||||
});
|
||||
|
||||
test("userPath", function (assert) {
|
||||
assert.equal(userPath(), "/u");
|
||||
assert.equal(userPath("eviltrout"), "/u/eviltrout");
|
||||
});
|
||||
|
||||
test("userPath with prefix", function (assert) {
|
||||
setPrefix("/forum");
|
||||
assert.equal(userPath(), "/forum/u");
|
||||
assert.equal(userPath("eviltrout"), "/forum/u/eviltrout");
|
||||
});
|
||||
|
||||
test("routeTo with prefix", async function (assert) {
|
||||
setPrefix("/forum");
|
||||
logIn();
|
||||
const user = User.current();
|
||||
|
||||
sinon.stub(DiscourseURL, "handleURL");
|
||||
DiscourseURL.routeTo("/my/messages");
|
||||
assert.ok(
|
||||
DiscourseURL.handleURL.calledWith(`/u/${user.username}/messages`),
|
||||
"it should navigate to the messages page"
|
||||
);
|
||||
});
|
||||
|
||||
test("prefixProtocol", async function (assert) {
|
||||
assert.equal(
|
||||
prefixProtocol("mailto:mr-beaver@aol.com"),
|
||||
"mailto:mr-beaver@aol.com"
|
||||
);
|
||||
assert.equal(prefixProtocol("discourse.org"), "https://discourse.org");
|
||||
assert.equal(
|
||||
prefixProtocol("www.discourse.org"),
|
||||
"https://www.discourse.org"
|
||||
);
|
||||
assert.equal(
|
||||
prefixProtocol("www.discourse.org/mailto:foo"),
|
||||
"https://www.discourse.org/mailto:foo"
|
||||
);
|
||||
test("isInternal with a HTTPS url", function (assert) {
|
||||
sinon.stub(DiscourseURL, "origin").returns("https://eviltrout.com");
|
||||
assert.ok(
|
||||
DiscourseURL.isInternal("http://eviltrout.com/monocle"),
|
||||
"HTTPS urls match HTTP urls"
|
||||
);
|
||||
});
|
||||
|
||||
test("isInternal on subfolder install", function (assert) {
|
||||
sinon.stub(DiscourseURL, "origin").returns("http://eviltrout.com/forum");
|
||||
assert.not(
|
||||
DiscourseURL.isInternal("http://eviltrout.com"),
|
||||
"the host root is not internal"
|
||||
);
|
||||
assert.not(
|
||||
DiscourseURL.isInternal("http://eviltrout.com/tophat"),
|
||||
"a url on the same host but on a different folder is not internal"
|
||||
);
|
||||
assert.ok(
|
||||
DiscourseURL.isInternal("http://eviltrout.com/forum/moustache"),
|
||||
"a url on the same host and on the same folder is internal"
|
||||
);
|
||||
});
|
||||
|
||||
test("userPath", function (assert) {
|
||||
assert.equal(userPath(), "/u");
|
||||
assert.equal(userPath("eviltrout"), "/u/eviltrout");
|
||||
});
|
||||
|
||||
test("userPath with prefix", function (assert) {
|
||||
setPrefix("/forum");
|
||||
assert.equal(userPath(), "/forum/u");
|
||||
assert.equal(userPath("eviltrout"), "/forum/u/eviltrout");
|
||||
});
|
||||
|
||||
test("routeTo with prefix", async function (assert) {
|
||||
setPrefix("/forum");
|
||||
logIn();
|
||||
const user = User.current();
|
||||
|
||||
sinon.stub(DiscourseURL, "handleURL");
|
||||
DiscourseURL.routeTo("/my/messages");
|
||||
assert.ok(
|
||||
DiscourseURL.handleURL.calledWith(`/u/${user.username}/messages`),
|
||||
"it should navigate to the messages page"
|
||||
);
|
||||
});
|
||||
|
||||
test("prefixProtocol", async function (assert) {
|
||||
assert.equal(
|
||||
prefixProtocol("mailto:mr-beaver@aol.com"),
|
||||
"mailto:mr-beaver@aol.com"
|
||||
);
|
||||
assert.equal(prefixProtocol("discourse.org"), "https://discourse.org");
|
||||
assert.equal(
|
||||
prefixProtocol("www.discourse.org"),
|
||||
"https://www.discourse.org"
|
||||
);
|
||||
assert.equal(
|
||||
prefixProtocol("www.discourse.org/mailto:foo"),
|
||||
"https://www.discourse.org/mailto:foo"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
import { test, module } from "qunit";
|
||||
import userSearch from "discourse/lib/user-search";
|
||||
import { CANCELLED_STATUS } from "discourse/lib/autocomplete";
|
||||
import pretender from "discourse/tests/helpers/create-pretender";
|
||||
|
||||
module("lib:user-search", {
|
||||
beforeEach() {
|
||||
const response = (object) => {
|
||||
return [200, { "Content-Type": "application/json" }, object];
|
||||
};
|
||||
import pretender, { response } from "discourse/tests/helpers/create-pretender";
|
||||
|
||||
module("Unit | Utility | user-search", function (hooks) {
|
||||
hooks.beforeEach(function () {
|
||||
pretender.get("/u/search/users", (request) => {
|
||||
// special responder for per category search
|
||||
const categoryMatch = request.url.match(/category_id=([0-9]+)/);
|
||||
@@ -83,99 +79,99 @@ module("lib:user-search", {
|
||||
],
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test("it flushes cache when switching categories", async function (assert) {
|
||||
let results = await userSearch({ term: "hello", categoryId: 1 });
|
||||
assert.equal(results[0].username, "category_1");
|
||||
assert.equal(results.length, 1);
|
||||
|
||||
// this is cached ... so let's check the cache is good
|
||||
results = await userSearch({ term: "hello", categoryId: 1 });
|
||||
assert.equal(results[0].username, "category_1");
|
||||
assert.equal(results.length, 1);
|
||||
|
||||
results = await userSearch({ term: "hello", categoryId: 2 });
|
||||
assert.equal(results[0].username, "category_2");
|
||||
assert.equal(results.length, 1);
|
||||
});
|
||||
|
||||
test("it returns cancel when eager completing with no results", async function (assert) {
|
||||
// Do everything twice, to check the cache works correctly
|
||||
|
||||
for (let i = 0; i < 2; i++) {
|
||||
// No topic or category, will always cancel
|
||||
let result = await userSearch({ term: "" });
|
||||
assert.equal(result, CANCELLED_STATUS);
|
||||
}
|
||||
|
||||
for (let i = 0; i < 2; i++) {
|
||||
// Unsecured category, so has no recommendations
|
||||
let result = await userSearch({ term: "", categoryId: 3 });
|
||||
assert.equal(result, CANCELLED_STATUS);
|
||||
}
|
||||
|
||||
for (let i = 0; i < 2; i++) {
|
||||
// Secured category, will have 1 recommendation
|
||||
let results = await userSearch({ term: "", categoryId: 1 });
|
||||
test("it flushes cache when switching categories", async function (assert) {
|
||||
let results = await userSearch({ term: "hello", categoryId: 1 });
|
||||
assert.equal(results[0].username, "category_1");
|
||||
assert.equal(results.length, 1);
|
||||
}
|
||||
});
|
||||
|
||||
test("it places groups unconditionally for exact match", async function (assert) {
|
||||
let results = await userSearch({ term: "Team" });
|
||||
assert.equal(results[results.length - 1]["name"], "team");
|
||||
});
|
||||
// this is cached ... so let's check the cache is good
|
||||
results = await userSearch({ term: "hello", categoryId: 1 });
|
||||
assert.equal(results[0].username, "category_1");
|
||||
assert.equal(results.length, 1);
|
||||
|
||||
test("it strips @ from the beginning", async function (assert) {
|
||||
let results = await userSearch({ term: "@Team" });
|
||||
assert.equal(results[results.length - 1]["name"], "team");
|
||||
});
|
||||
|
||||
test("it skips a search depending on punctuations", async function (assert) {
|
||||
let results;
|
||||
let skippedTerms = [
|
||||
"@sam s", // double space is not allowed
|
||||
"@sam;",
|
||||
"@sam,",
|
||||
"@sam:",
|
||||
];
|
||||
|
||||
for (let term of skippedTerms) {
|
||||
results = await userSearch({ term });
|
||||
assert.equal(results.length, 0);
|
||||
}
|
||||
|
||||
let allowedTerms = [
|
||||
"@sam sam", // double space is not allowed
|
||||
"@sam.sam",
|
||||
"@sam_sam",
|
||||
"@sam-sam",
|
||||
"@",
|
||||
];
|
||||
|
||||
let topicId = 100;
|
||||
|
||||
for (let term of allowedTerms) {
|
||||
results = await userSearch({ term, topicId });
|
||||
assert.equal(results.length, 6);
|
||||
}
|
||||
|
||||
results = await userSearch({ term: "sam@sam.com", allowEmails: true });
|
||||
// 6 + email
|
||||
assert.equal(results.length, 7);
|
||||
|
||||
results = await userSearch({ term: "sam+test@sam.com", allowEmails: true });
|
||||
assert.equal(results.length, 7);
|
||||
|
||||
results = await userSearch({ term: "sam@sam.com" });
|
||||
assert.equal(results.length, 0);
|
||||
|
||||
results = await userSearch({
|
||||
term: "no-results@example.com",
|
||||
allowEmails: true,
|
||||
results = await userSearch({ term: "hello", categoryId: 2 });
|
||||
assert.equal(results[0].username, "category_2");
|
||||
assert.equal(results.length, 1);
|
||||
});
|
||||
|
||||
test("it returns cancel when eager completing with no results", async function (assert) {
|
||||
// Do everything twice, to check the cache works correctly
|
||||
|
||||
for (let i = 0; i < 2; i++) {
|
||||
// No topic or category, will always cancel
|
||||
let result = await userSearch({ term: "" });
|
||||
assert.equal(result, CANCELLED_STATUS);
|
||||
}
|
||||
|
||||
for (let i = 0; i < 2; i++) {
|
||||
// Unsecured category, so has no recommendations
|
||||
let result = await userSearch({ term: "", categoryId: 3 });
|
||||
assert.equal(result, CANCELLED_STATUS);
|
||||
}
|
||||
|
||||
for (let i = 0; i < 2; i++) {
|
||||
// Secured category, will have 1 recommendation
|
||||
let results = await userSearch({ term: "", categoryId: 1 });
|
||||
assert.equal(results[0].username, "category_1");
|
||||
assert.equal(results.length, 1);
|
||||
}
|
||||
});
|
||||
|
||||
test("it places groups unconditionally for exact match", async function (assert) {
|
||||
let results = await userSearch({ term: "Team" });
|
||||
assert.equal(results[results.length - 1]["name"], "team");
|
||||
});
|
||||
|
||||
test("it strips @ from the beginning", async function (assert) {
|
||||
let results = await userSearch({ term: "@Team" });
|
||||
assert.equal(results[results.length - 1]["name"], "team");
|
||||
});
|
||||
|
||||
test("it skips a search depending on punctuations", async function (assert) {
|
||||
let results;
|
||||
let skippedTerms = [
|
||||
"@sam s", // double space is not allowed
|
||||
"@sam;",
|
||||
"@sam,",
|
||||
"@sam:",
|
||||
];
|
||||
|
||||
for (let term of skippedTerms) {
|
||||
results = await userSearch({ term });
|
||||
assert.equal(results.length, 0);
|
||||
}
|
||||
|
||||
let allowedTerms = [
|
||||
"@sam sam", // double space is not allowed
|
||||
"@sam.sam",
|
||||
"@sam_sam",
|
||||
"@sam-sam",
|
||||
"@",
|
||||
];
|
||||
|
||||
let topicId = 100;
|
||||
|
||||
for (let term of allowedTerms) {
|
||||
results = await userSearch({ term, topicId });
|
||||
assert.equal(results.length, 6);
|
||||
}
|
||||
|
||||
results = await userSearch({ term: "sam@sam.com", allowEmails: true });
|
||||
// 6 + email
|
||||
assert.equal(results.length, 7);
|
||||
|
||||
results = await userSearch({ term: "sam+test@sam.com", allowEmails: true });
|
||||
assert.equal(results.length, 7);
|
||||
|
||||
results = await userSearch({ term: "sam@sam.com" });
|
||||
assert.equal(results.length, 0);
|
||||
|
||||
results = await userSearch({
|
||||
term: "no-results@example.com",
|
||||
allowEmails: true,
|
||||
});
|
||||
assert.equal(results.length, 1);
|
||||
});
|
||||
assert.equal(results.length, 1);
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { skip } from "qunit";
|
||||
import { test } from "qunit";
|
||||
import { skip, test } from "qunit";
|
||||
import {
|
||||
escapeExpression,
|
||||
emailValid,
|
||||
@@ -20,263 +19,266 @@ import {
|
||||
import Handlebars from "handlebars";
|
||||
import { discourseModule } from "discourse/tests/helpers/qunit-helpers";
|
||||
|
||||
discourseModule("lib:utilities");
|
||||
discourseModule("Unit | Utilities", function () {
|
||||
test("escapeExpression", function (assert) {
|
||||
assert.equal(escapeExpression(">"), ">", "escapes unsafe characters");
|
||||
|
||||
test("escapeExpression", function (assert) {
|
||||
assert.equal(escapeExpression(">"), ">", "escapes unsafe characters");
|
||||
|
||||
assert.equal(
|
||||
escapeExpression(new Handlebars.SafeString(">")),
|
||||
">",
|
||||
"does not double-escape safe strings"
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
escapeExpression(undefined),
|
||||
"",
|
||||
"returns a falsy string when given a falsy value"
|
||||
);
|
||||
});
|
||||
|
||||
test("emailValid", function (assert) {
|
||||
assert.ok(
|
||||
emailValid("Bob@example.com"),
|
||||
"allows upper case in the first part of emails"
|
||||
);
|
||||
assert.ok(
|
||||
emailValid("bob@EXAMPLE.com"),
|
||||
"allows upper case in the email domain"
|
||||
);
|
||||
});
|
||||
|
||||
test("extractDomainFromUrl", function (assert) {
|
||||
assert.equal(
|
||||
extractDomainFromUrl("http://meta.discourse.org:443/random"),
|
||||
"meta.discourse.org",
|
||||
"extract domain name from url"
|
||||
);
|
||||
assert.equal(
|
||||
extractDomainFromUrl("meta.discourse.org:443/random"),
|
||||
"meta.discourse.org",
|
||||
"extract domain regardless of scheme presence"
|
||||
);
|
||||
assert.equal(
|
||||
extractDomainFromUrl("http://192.168.0.1:443/random"),
|
||||
"192.168.0.1",
|
||||
"works for IP address"
|
||||
);
|
||||
assert.equal(
|
||||
extractDomainFromUrl("http://localhost:443/random"),
|
||||
"localhost",
|
||||
"works for localhost"
|
||||
);
|
||||
});
|
||||
|
||||
test("avatarUrl", function (assert) {
|
||||
var rawSize = getRawSize;
|
||||
assert.blank(avatarUrl("", "tiny"), "no template returns blank");
|
||||
assert.equal(
|
||||
avatarUrl("/fake/template/{size}.png", "tiny"),
|
||||
"/fake/template/" + rawSize(20) + ".png",
|
||||
"simple avatar url"
|
||||
);
|
||||
assert.equal(
|
||||
avatarUrl("/fake/template/{size}.png", "large"),
|
||||
"/fake/template/" + rawSize(45) + ".png",
|
||||
"different size"
|
||||
);
|
||||
});
|
||||
|
||||
var setDevicePixelRatio = function (value) {
|
||||
if (Object.defineProperty && !window.hasOwnProperty("devicePixelRatio")) {
|
||||
Object.defineProperty(window, "devicePixelRatio", { value: 2 });
|
||||
} else {
|
||||
window.devicePixelRatio = value;
|
||||
}
|
||||
};
|
||||
|
||||
test("avatarImg", function (assert) {
|
||||
var oldRatio = window.devicePixelRatio;
|
||||
setDevicePixelRatio(2);
|
||||
|
||||
var avatarTemplate = "/path/to/avatar/{size}.png";
|
||||
assert.equal(
|
||||
avatarImg({ avatarTemplate: avatarTemplate, size: "tiny" }),
|
||||
"<img alt='' width='20' height='20' src='/path/to/avatar/40.png' class='avatar'>",
|
||||
"it returns the avatar html"
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
avatarImg({
|
||||
avatarTemplate: avatarTemplate,
|
||||
size: "tiny",
|
||||
title: "evilest trout",
|
||||
}),
|
||||
"<img alt='' width='20' height='20' src='/path/to/avatar/40.png' class='avatar' title='evilest trout' aria-label='evilest trout'>",
|
||||
"it adds a title if supplied"
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
avatarImg({
|
||||
avatarTemplate: avatarTemplate,
|
||||
size: "tiny",
|
||||
extraClasses: "evil fish",
|
||||
}),
|
||||
"<img alt='' width='20' height='20' src='/path/to/avatar/40.png' class='avatar evil fish'>",
|
||||
"it adds extra classes if supplied"
|
||||
);
|
||||
|
||||
assert.blank(
|
||||
avatarImg({ avatarTemplate: "", size: "tiny" }),
|
||||
"it doesn't render avatars for invalid avatar template"
|
||||
);
|
||||
|
||||
setDevicePixelRatio(oldRatio);
|
||||
});
|
||||
|
||||
test("defaultHomepage via meta tag", function (assert) {
|
||||
let meta = document.createElement("meta");
|
||||
meta.name = "discourse_current_homepage";
|
||||
meta.content = "hot";
|
||||
document.body.appendChild(meta);
|
||||
initializeDefaultHomepage(this.siteSettings);
|
||||
assert.equal(
|
||||
defaultHomepage(),
|
||||
"hot",
|
||||
"default homepage is pulled from <meta name=discourse_current_homepage>"
|
||||
);
|
||||
document.body.removeChild(meta);
|
||||
});
|
||||
|
||||
test("defaultHomepage via site settings", function (assert) {
|
||||
this.siteSettings.top_menu = "top|latest|hot";
|
||||
initializeDefaultHomepage(this.siteSettings);
|
||||
assert.equal(
|
||||
defaultHomepage(),
|
||||
"top",
|
||||
"default homepage is the first item in the top_menu site setting"
|
||||
);
|
||||
});
|
||||
|
||||
test("setDefaultHomepage", function (assert) {
|
||||
initializeDefaultHomepage(this.siteSettings);
|
||||
assert.equal(defaultHomepage(), "latest");
|
||||
setDefaultHomepage("top");
|
||||
assert.equal(defaultHomepage(), "top");
|
||||
});
|
||||
|
||||
test("caretRowCol", function (assert) {
|
||||
var textarea = document.createElement("textarea");
|
||||
const content = document.createTextNode("01234\n56789\n012345");
|
||||
textarea.appendChild(content);
|
||||
document.body.appendChild(textarea);
|
||||
|
||||
const assertResult = (setCaretPos, expectedRowNum, expectedColNum) => {
|
||||
setCaretPosition(textarea, setCaretPos);
|
||||
|
||||
const result = caretRowCol(textarea);
|
||||
assert.equal(
|
||||
result.rowNum,
|
||||
expectedRowNum,
|
||||
"returns the right row of the caret"
|
||||
escapeExpression(new Handlebars.SafeString(">")),
|
||||
">",
|
||||
"does not double-escape safe strings"
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
escapeExpression(undefined),
|
||||
"",
|
||||
"returns a falsy string when given a falsy value"
|
||||
);
|
||||
});
|
||||
|
||||
test("emailValid", function (assert) {
|
||||
assert.ok(
|
||||
emailValid("Bob@example.com"),
|
||||
"allows upper case in the first part of emails"
|
||||
);
|
||||
assert.ok(
|
||||
emailValid("bob@EXAMPLE.com"),
|
||||
"allows upper case in the email domain"
|
||||
);
|
||||
});
|
||||
|
||||
test("extractDomainFromUrl", function (assert) {
|
||||
assert.equal(
|
||||
extractDomainFromUrl("http://meta.discourse.org:443/random"),
|
||||
"meta.discourse.org",
|
||||
"extract domain name from url"
|
||||
);
|
||||
assert.equal(
|
||||
result.colNum,
|
||||
expectedColNum,
|
||||
"returns the right col of the caret"
|
||||
extractDomainFromUrl("meta.discourse.org:443/random"),
|
||||
"meta.discourse.org",
|
||||
"extract domain regardless of scheme presence"
|
||||
);
|
||||
assert.equal(
|
||||
extractDomainFromUrl("http://192.168.0.1:443/random"),
|
||||
"192.168.0.1",
|
||||
"works for IP address"
|
||||
);
|
||||
assert.equal(
|
||||
extractDomainFromUrl("http://localhost:443/random"),
|
||||
"localhost",
|
||||
"works for localhost"
|
||||
);
|
||||
});
|
||||
|
||||
test("avatarUrl", function (assert) {
|
||||
var rawSize = getRawSize;
|
||||
assert.blank(avatarUrl("", "tiny"), "no template returns blank");
|
||||
assert.equal(
|
||||
avatarUrl("/fake/template/{size}.png", "tiny"),
|
||||
"/fake/template/" + rawSize(20) + ".png",
|
||||
"simple avatar url"
|
||||
);
|
||||
assert.equal(
|
||||
avatarUrl("/fake/template/{size}.png", "large"),
|
||||
"/fake/template/" + rawSize(45) + ".png",
|
||||
"different size"
|
||||
);
|
||||
});
|
||||
|
||||
var setDevicePixelRatio = function (value) {
|
||||
if (Object.defineProperty && !window.hasOwnProperty("devicePixelRatio")) {
|
||||
Object.defineProperty(window, "devicePixelRatio", { value: 2 });
|
||||
} else {
|
||||
window.devicePixelRatio = value;
|
||||
}
|
||||
};
|
||||
|
||||
assertResult(0, 1, 0);
|
||||
assertResult(5, 1, 5);
|
||||
assertResult(6, 2, 0);
|
||||
assertResult(11, 2, 5);
|
||||
assertResult(14, 3, 2);
|
||||
test("avatarImg", function (assert) {
|
||||
var oldRatio = window.devicePixelRatio;
|
||||
setDevicePixelRatio(2);
|
||||
|
||||
document.body.removeChild(textarea);
|
||||
});
|
||||
var avatarTemplate = "/path/to/avatar/{size}.png";
|
||||
assert.equal(
|
||||
avatarImg({ avatarTemplate: avatarTemplate, size: "tiny" }),
|
||||
"<img alt='' width='20' height='20' src='/path/to/avatar/40.png' class='avatar'>",
|
||||
"it returns the avatar html"
|
||||
);
|
||||
|
||||
test("toAsciiPrintable", function (assert) {
|
||||
const accentedString = "Créme_Brûlée!";
|
||||
const unicodeString = "談話";
|
||||
assert.equal(
|
||||
avatarImg({
|
||||
avatarTemplate: avatarTemplate,
|
||||
size: "tiny",
|
||||
title: "evilest trout",
|
||||
}),
|
||||
"<img alt='' width='20' height='20' src='/path/to/avatar/40.png' class='avatar' title='evilest trout' aria-label='evilest trout'>",
|
||||
"it adds a title if supplied"
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
toAsciiPrintable(accentedString, "discourse"),
|
||||
"Creme_Brulee!",
|
||||
"it replaces accented characters with the appropriate ASCII equivalent"
|
||||
);
|
||||
assert.equal(
|
||||
avatarImg({
|
||||
avatarTemplate: avatarTemplate,
|
||||
size: "tiny",
|
||||
extraClasses: "evil fish",
|
||||
}),
|
||||
"<img alt='' width='20' height='20' src='/path/to/avatar/40.png' class='avatar evil fish'>",
|
||||
"it adds extra classes if supplied"
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
toAsciiPrintable(unicodeString, "discourse"),
|
||||
"discourse",
|
||||
"it uses the fallback string when unable to convert"
|
||||
);
|
||||
assert.blank(
|
||||
avatarImg({ avatarTemplate: "", size: "tiny" }),
|
||||
"it doesn't render avatars for invalid avatar template"
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
typeof toAsciiPrintable(unicodeString),
|
||||
"undefined",
|
||||
"it returns undefined when unable to convert and no fallback is provided"
|
||||
);
|
||||
});
|
||||
setDevicePixelRatio(oldRatio);
|
||||
});
|
||||
|
||||
test("slugify", function (assert) {
|
||||
const asciiString = "--- 0__( Some-cool Discourse Site! )__0 --- ";
|
||||
const accentedString = "Créme_Brûlée!";
|
||||
const unicodeString = "談話";
|
||||
test("defaultHomepage via meta tag", function (assert) {
|
||||
let meta = document.createElement("meta");
|
||||
meta.name = "discourse_current_homepage";
|
||||
meta.content = "hot";
|
||||
document.body.appendChild(meta);
|
||||
initializeDefaultHomepage(this.siteSettings);
|
||||
assert.equal(
|
||||
defaultHomepage(),
|
||||
"hot",
|
||||
"default homepage is pulled from <meta name=discourse_current_homepage>"
|
||||
);
|
||||
document.body.removeChild(meta);
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
slugify(asciiString),
|
||||
"0-some-cool-discourse-site-0",
|
||||
"it properly slugifies an ASCII string"
|
||||
);
|
||||
test("defaultHomepage via site settings", function (assert) {
|
||||
this.siteSettings.top_menu = "top|latest|hot";
|
||||
initializeDefaultHomepage(this.siteSettings);
|
||||
assert.equal(
|
||||
defaultHomepage(),
|
||||
"top",
|
||||
"default homepage is the first item in the top_menu site setting"
|
||||
);
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
slugify(accentedString),
|
||||
"crme-brle",
|
||||
"it removes accented characters"
|
||||
);
|
||||
test("setDefaultHomepage", function (assert) {
|
||||
initializeDefaultHomepage(this.siteSettings);
|
||||
assert.equal(defaultHomepage(), "latest");
|
||||
setDefaultHomepage("top");
|
||||
assert.equal(defaultHomepage(), "top");
|
||||
});
|
||||
|
||||
assert.equal(slugify(unicodeString), "", "it removes unicode characters");
|
||||
});
|
||||
test("caretRowCol", function (assert) {
|
||||
var textarea = document.createElement("textarea");
|
||||
const content = document.createTextNode("01234\n56789\n012345");
|
||||
textarea.appendChild(content);
|
||||
document.body.appendChild(textarea);
|
||||
|
||||
test("fillMissingDates", function (assert) {
|
||||
const startDate = "2017-11-12"; // YYYY-MM-DD
|
||||
const endDate = "2017-12-12"; // YYYY-MM-DD
|
||||
const data =
|
||||
'[{"x":"2017-11-12","y":3},{"x":"2017-11-27","y":2},{"x":"2017-12-06","y":9},{"x":"2017-12-11","y":2}]';
|
||||
const assertResult = (setCaretPos, expectedRowNum, expectedColNum) => {
|
||||
setCaretPosition(textarea, setCaretPos);
|
||||
|
||||
assert.equal(
|
||||
fillMissingDates(JSON.parse(data), startDate, endDate).length,
|
||||
31,
|
||||
"it returns a JSON array with 31 dates"
|
||||
);
|
||||
});
|
||||
const result = caretRowCol(textarea);
|
||||
assert.equal(
|
||||
result.rowNum,
|
||||
expectedRowNum,
|
||||
"returns the right row of the caret"
|
||||
);
|
||||
assert.equal(
|
||||
result.colNum,
|
||||
expectedColNum,
|
||||
"returns the right col of the caret"
|
||||
);
|
||||
};
|
||||
|
||||
test("inCodeBlock", function (assert) {
|
||||
const text =
|
||||
"000\n\n```\n111\n```\n\n000\n\n`111 111`\n\n000\n\n[code]\n111\n[/code]\n\n 111\n\t111\n\n000`000";
|
||||
for (let i = 0; i < text.length; ++i) {
|
||||
if (text[i] === "0") {
|
||||
assert.notOk(inCodeBlock(text, i), `position ${i} is not in code block`);
|
||||
} else if (text[i] === "1") {
|
||||
assert.ok(inCodeBlock(text, i), `position ${i} is in code block`);
|
||||
assertResult(0, 1, 0);
|
||||
assertResult(5, 1, 5);
|
||||
assertResult(6, 2, 0);
|
||||
assertResult(11, 2, 5);
|
||||
assertResult(14, 3, 2);
|
||||
|
||||
document.body.removeChild(textarea);
|
||||
});
|
||||
|
||||
test("toAsciiPrintable", function (assert) {
|
||||
const accentedString = "Créme_Brûlée!";
|
||||
const unicodeString = "談話";
|
||||
|
||||
assert.equal(
|
||||
toAsciiPrintable(accentedString, "discourse"),
|
||||
"Creme_Brulee!",
|
||||
"it replaces accented characters with the appropriate ASCII equivalent"
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
toAsciiPrintable(unicodeString, "discourse"),
|
||||
"discourse",
|
||||
"it uses the fallback string when unable to convert"
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
typeof toAsciiPrintable(unicodeString),
|
||||
"undefined",
|
||||
"it returns undefined when unable to convert and no fallback is provided"
|
||||
);
|
||||
});
|
||||
|
||||
test("slugify", function (assert) {
|
||||
const asciiString = "--- 0__( Some-cool Discourse Site! )__0 --- ";
|
||||
const accentedString = "Créme_Brûlée!";
|
||||
const unicodeString = "談話";
|
||||
|
||||
assert.equal(
|
||||
slugify(asciiString),
|
||||
"0-some-cool-discourse-site-0",
|
||||
"it properly slugifies an ASCII string"
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
slugify(accentedString),
|
||||
"crme-brle",
|
||||
"it removes accented characters"
|
||||
);
|
||||
|
||||
assert.equal(slugify(unicodeString), "", "it removes unicode characters");
|
||||
});
|
||||
|
||||
test("fillMissingDates", function (assert) {
|
||||
const startDate = "2017-11-12"; // YYYY-MM-DD
|
||||
const endDate = "2017-12-12"; // YYYY-MM-DD
|
||||
const data =
|
||||
'[{"x":"2017-11-12","y":3},{"x":"2017-11-27","y":2},{"x":"2017-12-06","y":9},{"x":"2017-12-11","y":2}]';
|
||||
|
||||
assert.equal(
|
||||
fillMissingDates(JSON.parse(data), startDate, endDate).length,
|
||||
31,
|
||||
"it returns a JSON array with 31 dates"
|
||||
);
|
||||
});
|
||||
|
||||
test("inCodeBlock", function (assert) {
|
||||
const text =
|
||||
"000\n\n```\n111\n```\n\n000\n\n`111 111`\n\n000\n\n[code]\n111\n[/code]\n\n 111\n\t111\n\n000`000";
|
||||
for (let i = 0; i < text.length; ++i) {
|
||||
if (text[i] === "0") {
|
||||
assert.notOk(
|
||||
inCodeBlock(text, i),
|
||||
`position ${i} is not in code block`
|
||||
);
|
||||
} else if (text[i] === "1") {
|
||||
assert.ok(inCodeBlock(text, i), `position ${i} is in code block`);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
skip("inCodeBlock - runs fast", function (assert) {
|
||||
const phrase = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
|
||||
const text = `${phrase}\n\n\`\`\`\n${phrase}\n\`\`\`\n\n${phrase}\n\n\`${phrase}\n${phrase}\n\n${phrase}\n\n[code]\n${phrase}\n[/code]\n\n${phrase}\n\n ${phrase}\n\n\`${phrase}\`\n\n${phrase}`;
|
||||
|
||||
let time = Number.MAX_VALUE;
|
||||
for (let i = 0; i < 10; ++i) {
|
||||
const start = performance.now();
|
||||
inCodeBlock(text, text.length);
|
||||
const end = performance.now();
|
||||
time = Math.min(time, end - start);
|
||||
}
|
||||
|
||||
// This runs in 'keyUp' event handler so it should run as fast as
|
||||
// possible. It should take less than 1ms for the test text.
|
||||
assert.ok(time < 10);
|
||||
});
|
||||
|
||||
skip("inCodeBlock - runs fast", function (assert) {
|
||||
const phrase = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
|
||||
const text = `${phrase}\n\n\`\`\`\n${phrase}\n\`\`\`\n\n${phrase}\n\n\`${phrase}\n${phrase}\n\n${phrase}\n\n[code]\n${phrase}\n[/code]\n\n${phrase}\n\n ${phrase}\n\n\`${phrase}\`\n\n${phrase}`;
|
||||
|
||||
let time = Number.MAX_VALUE;
|
||||
for (let i = 0; i < 10; ++i) {
|
||||
const start = performance.now();
|
||||
inCodeBlock(text, text.length);
|
||||
const end = performance.now();
|
||||
time = Math.min(time, end - start);
|
||||
}
|
||||
|
||||
// This runs in 'keyUp' event handler so it should run as fast as
|
||||
// possible. It should take less than 1ms for the test text.
|
||||
assert.ok(time < 10);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,8 +3,8 @@ import Controller from "@ember/controller";
|
||||
import GrantBadgeControllerMixin from "discourse/mixins/grant-badge-controller";
|
||||
import Badge from "discourse/models/badge";
|
||||
|
||||
module("mixin:grant-badge-controller", {
|
||||
before: function () {
|
||||
module("Unit | Mixin | grant-badge-controller", function (hooks) {
|
||||
hooks.beforeEach(function () {
|
||||
this.GrantBadgeController = Controller.extend(GrantBadgeControllerMixin);
|
||||
|
||||
this.badgeFirst = Badge.create({
|
||||
@@ -37,9 +37,7 @@ module("mixin:grant-badge-controller", {
|
||||
enabled: true,
|
||||
manually_grantable: false,
|
||||
});
|
||||
},
|
||||
|
||||
beforeEach: function () {
|
||||
this.subject = this.GrantBadgeController.create({
|
||||
userBadges: [],
|
||||
allBadges: [
|
||||
@@ -50,34 +48,34 @@ module("mixin:grant-badge-controller", {
|
||||
this.badgeAutomatic,
|
||||
],
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
test("grantableBadges", function (assert) {
|
||||
const sortedNames = [
|
||||
this.badgeFirst.name,
|
||||
this.badgeMiddle.name,
|
||||
this.badgeLast.name,
|
||||
];
|
||||
const badgeNames = this.subject
|
||||
.get("grantableBadges")
|
||||
.map((badge) => badge.name);
|
||||
|
||||
assert.not(
|
||||
badgeNames.includes(this.badgeDisabled),
|
||||
"excludes disabled badges"
|
||||
);
|
||||
assert.not(
|
||||
badgeNames.includes(this.badgeAutomatic),
|
||||
"excludes automatic badges"
|
||||
);
|
||||
assert.deepEqual(badgeNames, sortedNames, "sorts badges by name");
|
||||
});
|
||||
|
||||
test("selectedBadgeGrantable", function (assert) {
|
||||
this.subject.set("selectedBadgeId", this.badgeDisabled.id);
|
||||
assert.not(this.subject.get("selectedBadgeGrantable"));
|
||||
|
||||
this.subject.set("selectedBadgeId", this.badgeFirst.id);
|
||||
assert.ok(this.subject.get("selectedBadgeGrantable"));
|
||||
});
|
||||
|
||||
test("grantableBadges", function (assert) {
|
||||
const sortedNames = [
|
||||
this.badgeFirst.name,
|
||||
this.badgeMiddle.name,
|
||||
this.badgeLast.name,
|
||||
];
|
||||
const badgeNames = this.subject
|
||||
.get("grantableBadges")
|
||||
.map((badge) => badge.name);
|
||||
|
||||
assert.not(
|
||||
badgeNames.includes(this.badgeDisabled),
|
||||
"excludes disabled badges"
|
||||
);
|
||||
assert.not(
|
||||
badgeNames.includes(this.badgeAutomatic),
|
||||
"excludes automatic badges"
|
||||
);
|
||||
assert.deepEqual(badgeNames, sortedNames, "sorts badges by name");
|
||||
});
|
||||
|
||||
test("selectedBadgeGrantable", function (assert) {
|
||||
this.subject.set("selectedBadgeId", this.badgeDisabled.id);
|
||||
assert.not(this.subject.get("selectedBadgeGrantable"));
|
||||
|
||||
this.subject.set("selectedBadgeId", this.badgeFirst.id);
|
||||
assert.ok(this.subject.get("selectedBadgeGrantable"));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,50 +2,50 @@ import { test, module } from "qunit";
|
||||
import EmberObject from "@ember/object";
|
||||
import Setting from "admin/mixins/setting-object";
|
||||
|
||||
module("mixin:setting-object");
|
||||
module("Unit | Mixin | setting-object", function () {
|
||||
test("flat array", function (assert) {
|
||||
const FooSetting = EmberObject.extend(Setting);
|
||||
|
||||
test("flat array", function (assert) {
|
||||
const FooSetting = EmberObject.extend(Setting);
|
||||
const fooSettingInstance = FooSetting.create({
|
||||
valid_values: ["foo", "bar"],
|
||||
});
|
||||
|
||||
const fooSettingInstance = FooSetting.create({
|
||||
valid_values: ["foo", "bar"],
|
||||
assert.equal(fooSettingInstance.computedValueProperty, null);
|
||||
assert.equal(fooSettingInstance.computedNameProperty, null);
|
||||
});
|
||||
|
||||
assert.equal(fooSettingInstance.computedValueProperty, null);
|
||||
assert.equal(fooSettingInstance.computedNameProperty, null);
|
||||
});
|
||||
test("object", function (assert) {
|
||||
const FooSetting = EmberObject.extend(Setting);
|
||||
|
||||
test("object", function (assert) {
|
||||
const FooSetting = EmberObject.extend(Setting);
|
||||
const fooSettingInstance = FooSetting.create({
|
||||
valid_values: [{ value: "foo", name: "bar" }],
|
||||
});
|
||||
|
||||
const fooSettingInstance = FooSetting.create({
|
||||
valid_values: [{ value: "foo", name: "bar" }],
|
||||
assert.equal(fooSettingInstance.computedValueProperty, "value");
|
||||
assert.equal(fooSettingInstance.computedNameProperty, "name");
|
||||
});
|
||||
|
||||
assert.equal(fooSettingInstance.computedValueProperty, "value");
|
||||
assert.equal(fooSettingInstance.computedNameProperty, "name");
|
||||
});
|
||||
test("no values", function (assert) {
|
||||
const FooSetting = EmberObject.extend(Setting);
|
||||
|
||||
test("no values", function (assert) {
|
||||
const FooSetting = EmberObject.extend(Setting);
|
||||
const fooSettingInstance = FooSetting.create({
|
||||
valid_values: [],
|
||||
});
|
||||
|
||||
const fooSettingInstance = FooSetting.create({
|
||||
valid_values: [],
|
||||
assert.equal(fooSettingInstance.computedValueProperty, null);
|
||||
assert.equal(fooSettingInstance.computedNameProperty, null);
|
||||
});
|
||||
|
||||
assert.equal(fooSettingInstance.computedValueProperty, null);
|
||||
assert.equal(fooSettingInstance.computedNameProperty, null);
|
||||
});
|
||||
test("value/name properties defined", function (assert) {
|
||||
const FooSetting = EmberObject.extend(Setting);
|
||||
|
||||
test("value/name properties defined", function (assert) {
|
||||
const FooSetting = EmberObject.extend(Setting);
|
||||
const fooSettingInstance = FooSetting.create({
|
||||
valueProperty: "foo",
|
||||
nameProperty: "bar",
|
||||
valid_values: [],
|
||||
});
|
||||
|
||||
const fooSettingInstance = FooSetting.create({
|
||||
valueProperty: "foo",
|
||||
nameProperty: "bar",
|
||||
valid_values: [],
|
||||
assert.equal(fooSettingInstance.computedValueProperty, "foo");
|
||||
assert.equal(fooSettingInstance.computedNameProperty, "bar");
|
||||
});
|
||||
|
||||
assert.equal(fooSettingInstance.computedValueProperty, "foo");
|
||||
assert.equal(fooSettingInstance.computedNameProperty, "bar");
|
||||
});
|
||||
|
||||
@@ -2,96 +2,100 @@ import { test, module } from "qunit";
|
||||
import EmberObject from "@ember/object";
|
||||
import Singleton from "discourse/mixins/singleton";
|
||||
|
||||
module("mixin:singleton");
|
||||
module("Unit | Mixin | singleton", function () {
|
||||
test("current", function (assert) {
|
||||
var DummyModel = EmberObject.extend({});
|
||||
DummyModel.reopenClass(Singleton);
|
||||
|
||||
test("current", function (assert) {
|
||||
var DummyModel = EmberObject.extend({});
|
||||
DummyModel.reopenClass(Singleton);
|
||||
|
||||
var current = DummyModel.current();
|
||||
assert.present(current, "current returns the current instance");
|
||||
assert.equal(
|
||||
current,
|
||||
DummyModel.current(),
|
||||
"calling it again returns the same instance"
|
||||
);
|
||||
assert.notEqual(
|
||||
current,
|
||||
DummyModel.create({}),
|
||||
"we can create other instances that are not the same as current"
|
||||
);
|
||||
});
|
||||
|
||||
test("currentProp reading", function (assert) {
|
||||
var DummyModel = EmberObject.extend({});
|
||||
DummyModel.reopenClass(Singleton);
|
||||
var current = DummyModel.current();
|
||||
|
||||
assert.blank(
|
||||
DummyModel.currentProp("evil"),
|
||||
"by default attributes are blank"
|
||||
);
|
||||
current.set("evil", "trout");
|
||||
assert.equal(
|
||||
DummyModel.currentProp("evil"),
|
||||
"trout",
|
||||
"after changing the instance, the value is set"
|
||||
);
|
||||
});
|
||||
|
||||
test("currentProp writing", function (assert) {
|
||||
var DummyModel = EmberObject.extend({});
|
||||
DummyModel.reopenClass(Singleton);
|
||||
|
||||
assert.blank(
|
||||
DummyModel.currentProp("adventure"),
|
||||
"by default attributes are blank"
|
||||
);
|
||||
var result = DummyModel.currentProp("adventure", "time");
|
||||
assert.equal(result, "time", "it returns the new value");
|
||||
assert.equal(
|
||||
DummyModel.currentProp("adventure"),
|
||||
"time",
|
||||
"after calling currentProp the value is set"
|
||||
);
|
||||
|
||||
DummyModel.currentProp("count", 0);
|
||||
assert.equal(DummyModel.currentProp("count"), 0, "we can set the value to 0");
|
||||
|
||||
DummyModel.currentProp("adventure", null);
|
||||
assert.equal(
|
||||
DummyModel.currentProp("adventure"),
|
||||
null,
|
||||
"we can set the value to null"
|
||||
);
|
||||
});
|
||||
|
||||
test("createCurrent", function (assert) {
|
||||
var Shoe = EmberObject.extend({});
|
||||
Shoe.reopenClass(Singleton, {
|
||||
createCurrent: function () {
|
||||
return Shoe.create({ toes: 5 });
|
||||
},
|
||||
var current = DummyModel.current();
|
||||
assert.present(current, "current returns the current instance");
|
||||
assert.equal(
|
||||
current,
|
||||
DummyModel.current(),
|
||||
"calling it again returns the same instance"
|
||||
);
|
||||
assert.notEqual(
|
||||
current,
|
||||
DummyModel.create({}),
|
||||
"we can create other instances that are not the same as current"
|
||||
);
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
Shoe.currentProp("toes"),
|
||||
5,
|
||||
"it created the class using `createCurrent`"
|
||||
);
|
||||
});
|
||||
test("currentProp reading", function (assert) {
|
||||
var DummyModel = EmberObject.extend({});
|
||||
DummyModel.reopenClass(Singleton);
|
||||
var current = DummyModel.current();
|
||||
|
||||
test("createCurrent that returns null", function (assert) {
|
||||
var Missing = EmberObject.extend({});
|
||||
Missing.reopenClass(Singleton, {
|
||||
createCurrent: function () {
|
||||
return null;
|
||||
},
|
||||
assert.blank(
|
||||
DummyModel.currentProp("evil"),
|
||||
"by default attributes are blank"
|
||||
);
|
||||
current.set("evil", "trout");
|
||||
assert.equal(
|
||||
DummyModel.currentProp("evil"),
|
||||
"trout",
|
||||
"after changing the instance, the value is set"
|
||||
);
|
||||
});
|
||||
|
||||
assert.blank(Missing.current(), "it doesn't return an instance");
|
||||
assert.blank(
|
||||
Missing.currentProp("madeup"),
|
||||
"it won't raise an error asking for a property. Will just return null."
|
||||
);
|
||||
test("currentProp writing", function (assert) {
|
||||
var DummyModel = EmberObject.extend({});
|
||||
DummyModel.reopenClass(Singleton);
|
||||
|
||||
assert.blank(
|
||||
DummyModel.currentProp("adventure"),
|
||||
"by default attributes are blank"
|
||||
);
|
||||
var result = DummyModel.currentProp("adventure", "time");
|
||||
assert.equal(result, "time", "it returns the new value");
|
||||
assert.equal(
|
||||
DummyModel.currentProp("adventure"),
|
||||
"time",
|
||||
"after calling currentProp the value is set"
|
||||
);
|
||||
|
||||
DummyModel.currentProp("count", 0);
|
||||
assert.equal(
|
||||
DummyModel.currentProp("count"),
|
||||
0,
|
||||
"we can set the value to 0"
|
||||
);
|
||||
|
||||
DummyModel.currentProp("adventure", null);
|
||||
assert.equal(
|
||||
DummyModel.currentProp("adventure"),
|
||||
null,
|
||||
"we can set the value to null"
|
||||
);
|
||||
});
|
||||
|
||||
test("createCurrent", function (assert) {
|
||||
var Shoe = EmberObject.extend({});
|
||||
Shoe.reopenClass(Singleton, {
|
||||
createCurrent: function () {
|
||||
return Shoe.create({ toes: 5 });
|
||||
},
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
Shoe.currentProp("toes"),
|
||||
5,
|
||||
"it created the class using `createCurrent`"
|
||||
);
|
||||
});
|
||||
|
||||
test("createCurrent that returns null", function (assert) {
|
||||
var Missing = EmberObject.extend({});
|
||||
Missing.reopenClass(Singleton, {
|
||||
createCurrent: function () {
|
||||
return null;
|
||||
},
|
||||
});
|
||||
|
||||
assert.blank(Missing.current(), "it doesn't return an instance");
|
||||
assert.blank(
|
||||
Missing.currentProp("madeup"),
|
||||
"it won't raise an error asking for a property. Will just return null."
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,78 +1,78 @@
|
||||
import { test, module } from "qunit";
|
||||
import Badge from "discourse/models/badge";
|
||||
|
||||
module("model:badge");
|
||||
|
||||
test("newBadge", function (assert) {
|
||||
const badge1 = Badge.create({ name: "New Badge" }),
|
||||
badge2 = Badge.create({ id: 1, name: "Old Badge" });
|
||||
assert.ok(badge1.get("newBadge"), "badges without ids are new");
|
||||
assert.ok(!badge2.get("newBadge"), "badges with ids are not new");
|
||||
});
|
||||
|
||||
test("createFromJson array", function (assert) {
|
||||
const badgesJson = {
|
||||
badge_types: [{ id: 6, name: "Silver 1" }],
|
||||
badges: [
|
||||
{ id: 1126, name: "Badge 1", description: null, badge_type_id: 6 },
|
||||
],
|
||||
};
|
||||
|
||||
const badges = Badge.createFromJson(badgesJson);
|
||||
|
||||
assert.ok(Array.isArray(badges), "returns an array");
|
||||
assert.equal(badges[0].get("name"), "Badge 1", "badge details are set");
|
||||
assert.equal(
|
||||
badges[0].get("badge_type.name"),
|
||||
"Silver 1",
|
||||
"badge_type reference is set"
|
||||
);
|
||||
});
|
||||
|
||||
test("createFromJson single", function (assert) {
|
||||
const badgeJson = {
|
||||
badge_types: [{ id: 6, name: "Silver 1" }],
|
||||
badge: { id: 1126, name: "Badge 1", description: null, badge_type_id: 6 },
|
||||
};
|
||||
|
||||
const badge = Badge.createFromJson(badgeJson);
|
||||
|
||||
assert.ok(!Array.isArray(badge), "does not returns an array");
|
||||
});
|
||||
|
||||
test("updateFromJson", function (assert) {
|
||||
const badgeJson = {
|
||||
badge_types: [{ id: 6, name: "Silver 1" }],
|
||||
badge: { id: 1126, name: "Badge 1", description: null, badge_type_id: 6 },
|
||||
};
|
||||
const badge = Badge.create({ name: "Badge 1" });
|
||||
badge.updateFromJson(badgeJson);
|
||||
assert.equal(badge.get("id"), 1126, "id is set");
|
||||
assert.equal(
|
||||
badge.get("badge_type.name"),
|
||||
"Silver 1",
|
||||
"badge_type reference is set"
|
||||
);
|
||||
});
|
||||
|
||||
test("save", function (assert) {
|
||||
assert.expect(0);
|
||||
const badge = Badge.create({
|
||||
name: "New Badge",
|
||||
description: "This is a new badge.",
|
||||
badge_type_id: 1,
|
||||
module("Unit | Model | badge", function () {
|
||||
test("newBadge", function (assert) {
|
||||
const badge1 = Badge.create({ name: "New Badge" }),
|
||||
badge2 = Badge.create({ id: 1, name: "Old Badge" });
|
||||
assert.ok(badge1.get("newBadge"), "badges without ids are new");
|
||||
assert.ok(!badge2.get("newBadge"), "badges with ids are not new");
|
||||
});
|
||||
return badge.save(["name", "description", "badge_type_id"]);
|
||||
});
|
||||
|
||||
test("destroy", function (assert) {
|
||||
assert.expect(0);
|
||||
const badge = Badge.create({
|
||||
name: "New Badge",
|
||||
description: "This is a new badge.",
|
||||
badge_type_id: 1,
|
||||
test("createFromJson array", function (assert) {
|
||||
const badgesJson = {
|
||||
badge_types: [{ id: 6, name: "Silver 1" }],
|
||||
badges: [
|
||||
{ id: 1126, name: "Badge 1", description: null, badge_type_id: 6 },
|
||||
],
|
||||
};
|
||||
|
||||
const badges = Badge.createFromJson(badgesJson);
|
||||
|
||||
assert.ok(Array.isArray(badges), "returns an array");
|
||||
assert.equal(badges[0].get("name"), "Badge 1", "badge details are set");
|
||||
assert.equal(
|
||||
badges[0].get("badge_type.name"),
|
||||
"Silver 1",
|
||||
"badge_type reference is set"
|
||||
);
|
||||
});
|
||||
|
||||
test("createFromJson single", function (assert) {
|
||||
const badgeJson = {
|
||||
badge_types: [{ id: 6, name: "Silver 1" }],
|
||||
badge: { id: 1126, name: "Badge 1", description: null, badge_type_id: 6 },
|
||||
};
|
||||
|
||||
const badge = Badge.createFromJson(badgeJson);
|
||||
|
||||
assert.ok(!Array.isArray(badge), "does not returns an array");
|
||||
});
|
||||
|
||||
test("updateFromJson", function (assert) {
|
||||
const badgeJson = {
|
||||
badge_types: [{ id: 6, name: "Silver 1" }],
|
||||
badge: { id: 1126, name: "Badge 1", description: null, badge_type_id: 6 },
|
||||
};
|
||||
const badge = Badge.create({ name: "Badge 1" });
|
||||
badge.updateFromJson(badgeJson);
|
||||
assert.equal(badge.get("id"), 1126, "id is set");
|
||||
assert.equal(
|
||||
badge.get("badge_type.name"),
|
||||
"Silver 1",
|
||||
"badge_type reference is set"
|
||||
);
|
||||
});
|
||||
|
||||
test("save", function (assert) {
|
||||
assert.expect(0);
|
||||
const badge = Badge.create({
|
||||
name: "New Badge",
|
||||
description: "This is a new badge.",
|
||||
badge_type_id: 1,
|
||||
});
|
||||
return badge.save(["name", "description", "badge_type_id"]);
|
||||
});
|
||||
|
||||
test("destroy", function (assert) {
|
||||
assert.expect(0);
|
||||
const badge = Badge.create({
|
||||
name: "New Badge",
|
||||
description: "This is a new badge.",
|
||||
badge_type_id: 1,
|
||||
});
|
||||
badge.destroy();
|
||||
badge.set("id", 3);
|
||||
return badge.destroy();
|
||||
});
|
||||
badge.destroy();
|
||||
badge.set("id", 3);
|
||||
return badge.destroy();
|
||||
});
|
||||
|
||||
@@ -3,332 +3,349 @@ import { test, module } from "qunit";
|
||||
import createStore from "discourse/tests/helpers/create-store";
|
||||
import Category from "discourse/models/category";
|
||||
|
||||
module("model:category");
|
||||
module("Unit | Model | category", function () {
|
||||
test("slugFor", function (assert) {
|
||||
const store = createStore();
|
||||
|
||||
test("slugFor", function (assert) {
|
||||
const store = createStore();
|
||||
const slugFor = function (cat, val, text) {
|
||||
assert.equal(Category.slugFor(cat), val, text);
|
||||
};
|
||||
|
||||
const slugFor = function (cat, val, text) {
|
||||
assert.equal(Category.slugFor(cat), val, text);
|
||||
};
|
||||
slugFor(
|
||||
store.createRecord("category", { slug: "hello" }),
|
||||
"hello",
|
||||
"It calculates the proper slug for hello"
|
||||
);
|
||||
slugFor(
|
||||
store.createRecord("category", { id: 123, slug: "" }),
|
||||
"123-category",
|
||||
"It returns id-category for empty strings"
|
||||
);
|
||||
slugFor(
|
||||
store.createRecord("category", { id: 456 }),
|
||||
"456-category",
|
||||
"It returns id-category for undefined slugs"
|
||||
);
|
||||
slugFor(
|
||||
store.createRecord("category", { slug: "熱帶風暴畫眉" }),
|
||||
"熱帶風暴畫眉",
|
||||
"It can be non english characters"
|
||||
);
|
||||
|
||||
slugFor(
|
||||
store.createRecord("category", { slug: "hello" }),
|
||||
"hello",
|
||||
"It calculates the proper slug for hello"
|
||||
);
|
||||
slugFor(
|
||||
store.createRecord("category", { id: 123, slug: "" }),
|
||||
"123-category",
|
||||
"It returns id-category for empty strings"
|
||||
);
|
||||
slugFor(
|
||||
store.createRecord("category", { id: 456 }),
|
||||
"456-category",
|
||||
"It returns id-category for undefined slugs"
|
||||
);
|
||||
slugFor(
|
||||
store.createRecord("category", { slug: "熱帶風暴畫眉" }),
|
||||
"熱帶風暴畫眉",
|
||||
"It can be non english characters"
|
||||
);
|
||||
|
||||
const parentCategory = store.createRecord("category", {
|
||||
id: 345,
|
||||
slug: "darth",
|
||||
});
|
||||
slugFor(
|
||||
store.createRecord("category", {
|
||||
slug: "luke",
|
||||
parentCategory: parentCategory,
|
||||
}),
|
||||
"darth/luke",
|
||||
"it uses the parent slug before the child"
|
||||
);
|
||||
|
||||
slugFor(
|
||||
store.createRecord("category", { id: 555, parentCategory: parentCategory }),
|
||||
"darth/555-category",
|
||||
"it uses the parent slug before the child and then uses id"
|
||||
);
|
||||
|
||||
parentCategory.set("slug", null);
|
||||
slugFor(
|
||||
store.createRecord("category", { id: 555, parentCategory: parentCategory }),
|
||||
"345-category/555-category",
|
||||
"it uses the parent before the child and uses ids for both"
|
||||
);
|
||||
});
|
||||
|
||||
test("findBySlug", function (assert) {
|
||||
assert.expect(6);
|
||||
|
||||
const store = createStore();
|
||||
const darth = store.createRecord("category", { id: 1, slug: "darth" }),
|
||||
luke = store.createRecord("category", {
|
||||
id: 2,
|
||||
slug: "luke",
|
||||
parentCategory: darth,
|
||||
}),
|
||||
hurricane = store.createRecord("category", { id: 3, slug: "熱帶風暴畫眉" }),
|
||||
newsFeed = store.createRecord("category", {
|
||||
id: 4,
|
||||
slug: "뉴스피드",
|
||||
parentCategory: hurricane,
|
||||
}),
|
||||
time = store.createRecord("category", {
|
||||
id: 5,
|
||||
slug: "时间",
|
||||
parentCategory: darth,
|
||||
}),
|
||||
bah = store.createRecord("category", {
|
||||
id: 6,
|
||||
slug: "bah",
|
||||
parentCategory: hurricane,
|
||||
}),
|
||||
categoryList = [darth, luke, hurricane, newsFeed, time, bah];
|
||||
|
||||
sinon.stub(Category, "list").returns(categoryList);
|
||||
|
||||
assert.deepEqual(
|
||||
Category.findBySlug("darth"),
|
||||
darth,
|
||||
"we can find a category"
|
||||
);
|
||||
assert.deepEqual(
|
||||
Category.findBySlug("luke", "darth"),
|
||||
luke,
|
||||
"we can find the other category with parent category"
|
||||
);
|
||||
assert.deepEqual(
|
||||
Category.findBySlug("熱帶風暴畫眉"),
|
||||
hurricane,
|
||||
"we can find a category with CJK slug"
|
||||
);
|
||||
assert.deepEqual(
|
||||
Category.findBySlug("뉴스피드", "熱帶風暴畫眉"),
|
||||
newsFeed,
|
||||
"we can find a category with CJK slug whose parent slug is also CJK"
|
||||
);
|
||||
assert.deepEqual(
|
||||
Category.findBySlug("时间", "darth"),
|
||||
time,
|
||||
"we can find a category with CJK slug whose parent slug is english"
|
||||
);
|
||||
assert.deepEqual(
|
||||
Category.findBySlug("bah", "熱帶風暴畫眉"),
|
||||
bah,
|
||||
"we can find a category with english slug whose parent slug is CJK"
|
||||
);
|
||||
|
||||
sinon.restore();
|
||||
});
|
||||
|
||||
test("findSingleBySlug", function (assert) {
|
||||
assert.expect(6);
|
||||
|
||||
const store = createStore();
|
||||
const darth = store.createRecord("category", { id: 1, slug: "darth" }),
|
||||
luke = store.createRecord("category", {
|
||||
id: 2,
|
||||
slug: "luke",
|
||||
parentCategory: darth,
|
||||
}),
|
||||
hurricane = store.createRecord("category", { id: 3, slug: "熱帶風暴畫眉" }),
|
||||
newsFeed = store.createRecord("category", {
|
||||
id: 4,
|
||||
slug: "뉴스피드",
|
||||
parentCategory: hurricane,
|
||||
}),
|
||||
time = store.createRecord("category", {
|
||||
id: 5,
|
||||
slug: "时间",
|
||||
parentCategory: darth,
|
||||
}),
|
||||
bah = store.createRecord("category", {
|
||||
id: 6,
|
||||
slug: "bah",
|
||||
parentCategory: hurricane,
|
||||
}),
|
||||
categoryList = [darth, luke, hurricane, newsFeed, time, bah];
|
||||
|
||||
sinon.stub(Category, "list").returns(categoryList);
|
||||
|
||||
assert.deepEqual(
|
||||
Category.findSingleBySlug("darth"),
|
||||
darth,
|
||||
"we can find a category"
|
||||
);
|
||||
assert.deepEqual(
|
||||
Category.findSingleBySlug("darth/luke"),
|
||||
luke,
|
||||
"we can find the other category with parent category"
|
||||
);
|
||||
assert.deepEqual(
|
||||
Category.findSingleBySlug("熱帶風暴畫眉"),
|
||||
hurricane,
|
||||
"we can find a category with CJK slug"
|
||||
);
|
||||
assert.deepEqual(
|
||||
Category.findSingleBySlug("熱帶風暴畫眉/뉴스피드"),
|
||||
newsFeed,
|
||||
"we can find a category with CJK slug whose parent slug is also CJK"
|
||||
);
|
||||
assert.deepEqual(
|
||||
Category.findSingleBySlug("darth/时间"),
|
||||
time,
|
||||
"we can find a category with CJK slug whose parent slug is english"
|
||||
);
|
||||
assert.deepEqual(
|
||||
Category.findSingleBySlug("熱帶風暴畫眉/bah"),
|
||||
bah,
|
||||
"we can find a category with english slug whose parent slug is CJK"
|
||||
);
|
||||
});
|
||||
|
||||
test("findBySlugPathWithID", function (assert) {
|
||||
const store = createStore();
|
||||
|
||||
const foo = store.createRecord("category", { id: 1, slug: "foo" });
|
||||
const bar = store.createRecord("category", {
|
||||
id: 2,
|
||||
slug: "bar",
|
||||
parentCategory: foo,
|
||||
});
|
||||
const baz = store.createRecord("category", {
|
||||
id: 3,
|
||||
slug: "baz",
|
||||
parentCategory: foo,
|
||||
});
|
||||
|
||||
const categoryList = [foo, bar, baz];
|
||||
sinon.stub(Category, "list").returns(categoryList);
|
||||
|
||||
assert.deepEqual(Category.findBySlugPathWithID("foo"), foo);
|
||||
assert.deepEqual(Category.findBySlugPathWithID("foo/bar"), bar);
|
||||
assert.deepEqual(Category.findBySlugPathWithID("foo/bar/"), bar);
|
||||
assert.deepEqual(Category.findBySlugPathWithID("foo/baz/3"), baz);
|
||||
});
|
||||
|
||||
test("search with category name", function (assert) {
|
||||
const store = createStore(),
|
||||
category1 = store.createRecord("category", {
|
||||
id: 1,
|
||||
name: "middle term",
|
||||
slug: "different-slug",
|
||||
}),
|
||||
category2 = store.createRecord("category", {
|
||||
id: 2,
|
||||
name: "middle term",
|
||||
slug: "another-different-slug",
|
||||
const parentCategory = store.createRecord("category", {
|
||||
id: 345,
|
||||
slug: "darth",
|
||||
});
|
||||
slugFor(
|
||||
store.createRecord("category", {
|
||||
slug: "luke",
|
||||
parentCategory: parentCategory,
|
||||
}),
|
||||
"darth/luke",
|
||||
"it uses the parent slug before the child"
|
||||
);
|
||||
|
||||
sinon.stub(Category, "listByActivity").returns([category1, category2]);
|
||||
slugFor(
|
||||
store.createRecord("category", {
|
||||
id: 555,
|
||||
parentCategory: parentCategory,
|
||||
}),
|
||||
"darth/555-category",
|
||||
"it uses the parent slug before the child and then uses id"
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
Category.search("term", { limit: 0 }),
|
||||
[],
|
||||
"returns an empty array when limit is 0"
|
||||
);
|
||||
assert.deepEqual(
|
||||
Category.search(""),
|
||||
[category1, category2],
|
||||
"orders by activity if no term is matched"
|
||||
);
|
||||
assert.deepEqual(
|
||||
Category.search("term"),
|
||||
[category1, category2],
|
||||
"orders by activity"
|
||||
);
|
||||
parentCategory.set("slug", null);
|
||||
slugFor(
|
||||
store.createRecord("category", {
|
||||
id: 555,
|
||||
parentCategory: parentCategory,
|
||||
}),
|
||||
"345-category/555-category",
|
||||
"it uses the parent before the child and uses ids for both"
|
||||
);
|
||||
});
|
||||
|
||||
category2.set("name", "TeRm start");
|
||||
assert.deepEqual(
|
||||
Category.search("tErM"),
|
||||
[category2, category1],
|
||||
"ignores case of category name and search term"
|
||||
);
|
||||
test("findBySlug", function (assert) {
|
||||
assert.expect(6);
|
||||
|
||||
category2.set("name", "term start");
|
||||
assert.deepEqual(
|
||||
Category.search("term"),
|
||||
[category2, category1],
|
||||
"orders matching begin with and then contains"
|
||||
);
|
||||
const store = createStore();
|
||||
const darth = store.createRecord("category", { id: 1, slug: "darth" }),
|
||||
luke = store.createRecord("category", {
|
||||
id: 2,
|
||||
slug: "luke",
|
||||
parentCategory: darth,
|
||||
}),
|
||||
hurricane = store.createRecord("category", {
|
||||
id: 3,
|
||||
slug: "熱帶風暴畫眉",
|
||||
}),
|
||||
newsFeed = store.createRecord("category", {
|
||||
id: 4,
|
||||
slug: "뉴스피드",
|
||||
parentCategory: hurricane,
|
||||
}),
|
||||
time = store.createRecord("category", {
|
||||
id: 5,
|
||||
slug: "时间",
|
||||
parentCategory: darth,
|
||||
}),
|
||||
bah = store.createRecord("category", {
|
||||
id: 6,
|
||||
slug: "bah",
|
||||
parentCategory: hurricane,
|
||||
}),
|
||||
categoryList = [darth, luke, hurricane, newsFeed, time, bah];
|
||||
|
||||
sinon.restore();
|
||||
sinon.stub(Category, "list").returns(categoryList);
|
||||
|
||||
const child_category1 = store.createRecord("category", {
|
||||
assert.deepEqual(
|
||||
Category.findBySlug("darth"),
|
||||
darth,
|
||||
"we can find a category"
|
||||
);
|
||||
assert.deepEqual(
|
||||
Category.findBySlug("luke", "darth"),
|
||||
luke,
|
||||
"we can find the other category with parent category"
|
||||
);
|
||||
assert.deepEqual(
|
||||
Category.findBySlug("熱帶風暴畫眉"),
|
||||
hurricane,
|
||||
"we can find a category with CJK slug"
|
||||
);
|
||||
assert.deepEqual(
|
||||
Category.findBySlug("뉴스피드", "熱帶風暴畫眉"),
|
||||
newsFeed,
|
||||
"we can find a category with CJK slug whose parent slug is also CJK"
|
||||
);
|
||||
assert.deepEqual(
|
||||
Category.findBySlug("时间", "darth"),
|
||||
time,
|
||||
"we can find a category with CJK slug whose parent slug is english"
|
||||
);
|
||||
assert.deepEqual(
|
||||
Category.findBySlug("bah", "熱帶風暴畫眉"),
|
||||
bah,
|
||||
"we can find a category with english slug whose parent slug is CJK"
|
||||
);
|
||||
|
||||
sinon.restore();
|
||||
});
|
||||
|
||||
test("findSingleBySlug", function (assert) {
|
||||
assert.expect(6);
|
||||
|
||||
const store = createStore();
|
||||
const darth = store.createRecord("category", { id: 1, slug: "darth" }),
|
||||
luke = store.createRecord("category", {
|
||||
id: 2,
|
||||
slug: "luke",
|
||||
parentCategory: darth,
|
||||
}),
|
||||
hurricane = store.createRecord("category", {
|
||||
id: 3,
|
||||
slug: "熱帶風暴畫眉",
|
||||
}),
|
||||
newsFeed = store.createRecord("category", {
|
||||
id: 4,
|
||||
slug: "뉴스피드",
|
||||
parentCategory: hurricane,
|
||||
}),
|
||||
time = store.createRecord("category", {
|
||||
id: 5,
|
||||
slug: "时间",
|
||||
parentCategory: darth,
|
||||
}),
|
||||
bah = store.createRecord("category", {
|
||||
id: 6,
|
||||
slug: "bah",
|
||||
parentCategory: hurricane,
|
||||
}),
|
||||
categoryList = [darth, luke, hurricane, newsFeed, time, bah];
|
||||
|
||||
sinon.stub(Category, "list").returns(categoryList);
|
||||
|
||||
assert.deepEqual(
|
||||
Category.findSingleBySlug("darth"),
|
||||
darth,
|
||||
"we can find a category"
|
||||
);
|
||||
assert.deepEqual(
|
||||
Category.findSingleBySlug("darth/luke"),
|
||||
luke,
|
||||
"we can find the other category with parent category"
|
||||
);
|
||||
assert.deepEqual(
|
||||
Category.findSingleBySlug("熱帶風暴畫眉"),
|
||||
hurricane,
|
||||
"we can find a category with CJK slug"
|
||||
);
|
||||
assert.deepEqual(
|
||||
Category.findSingleBySlug("熱帶風暴畫眉/뉴스피드"),
|
||||
newsFeed,
|
||||
"we can find a category with CJK slug whose parent slug is also CJK"
|
||||
);
|
||||
assert.deepEqual(
|
||||
Category.findSingleBySlug("darth/时间"),
|
||||
time,
|
||||
"we can find a category with CJK slug whose parent slug is english"
|
||||
);
|
||||
assert.deepEqual(
|
||||
Category.findSingleBySlug("熱帶風暴畫眉/bah"),
|
||||
bah,
|
||||
"we can find a category with english slug whose parent slug is CJK"
|
||||
);
|
||||
});
|
||||
|
||||
test("findBySlugPathWithID", function (assert) {
|
||||
const store = createStore();
|
||||
|
||||
const foo = store.createRecord("category", { id: 1, slug: "foo" });
|
||||
const bar = store.createRecord("category", {
|
||||
id: 2,
|
||||
slug: "bar",
|
||||
parentCategory: foo,
|
||||
});
|
||||
const baz = store.createRecord("category", {
|
||||
id: 3,
|
||||
name: "term start",
|
||||
parent_category_id: category1.get("id"),
|
||||
}),
|
||||
read_restricted_category = store.createRecord("category", {
|
||||
id: 4,
|
||||
name: "some term",
|
||||
read_restricted: true,
|
||||
slug: "baz",
|
||||
parentCategory: foo,
|
||||
});
|
||||
|
||||
sinon
|
||||
.stub(Category, "listByActivity")
|
||||
.returns([read_restricted_category, category1, child_category1, category2]);
|
||||
const categoryList = [foo, bar, baz];
|
||||
sinon.stub(Category, "list").returns(categoryList);
|
||||
|
||||
assert.deepEqual(
|
||||
Category.search(""),
|
||||
[category1, category2, read_restricted_category],
|
||||
"prioritize non read_restricted and does not include child categories when term is blank"
|
||||
);
|
||||
assert.deepEqual(Category.findBySlugPathWithID("foo"), foo);
|
||||
assert.deepEqual(Category.findBySlugPathWithID("foo/bar"), bar);
|
||||
assert.deepEqual(Category.findBySlugPathWithID("foo/bar/"), bar);
|
||||
assert.deepEqual(Category.findBySlugPathWithID("foo/baz/3"), baz);
|
||||
});
|
||||
|
||||
assert.deepEqual(
|
||||
Category.search("", { limit: 3 }),
|
||||
[category1, category2, read_restricted_category],
|
||||
"prioritize non read_restricted and does not include child categories categories when term is blank with limit"
|
||||
);
|
||||
test("search with category name", function (assert) {
|
||||
const store = createStore(),
|
||||
category1 = store.createRecord("category", {
|
||||
id: 1,
|
||||
name: "middle term",
|
||||
slug: "different-slug",
|
||||
}),
|
||||
category2 = store.createRecord("category", {
|
||||
id: 2,
|
||||
name: "middle term",
|
||||
slug: "another-different-slug",
|
||||
});
|
||||
|
||||
assert.deepEqual(
|
||||
Category.search("term"),
|
||||
[child_category1, category2, category1, read_restricted_category],
|
||||
"prioritize non read_restricted"
|
||||
);
|
||||
sinon.stub(Category, "listByActivity").returns([category1, category2]);
|
||||
|
||||
assert.deepEqual(
|
||||
Category.search("term", { limit: 3 }),
|
||||
[child_category1, category2, read_restricted_category],
|
||||
"prioritize non read_restricted with limit"
|
||||
);
|
||||
assert.deepEqual(
|
||||
Category.search("term", { limit: 0 }),
|
||||
[],
|
||||
"returns an empty array when limit is 0"
|
||||
);
|
||||
assert.deepEqual(
|
||||
Category.search(""),
|
||||
[category1, category2],
|
||||
"orders by activity if no term is matched"
|
||||
);
|
||||
assert.deepEqual(
|
||||
Category.search("term"),
|
||||
[category1, category2],
|
||||
"orders by activity"
|
||||
);
|
||||
|
||||
sinon.restore();
|
||||
});
|
||||
|
||||
test("search with category slug", function (assert) {
|
||||
const store = createStore(),
|
||||
category1 = store.createRecord("category", {
|
||||
id: 1,
|
||||
name: "middle term",
|
||||
slug: "different-slug",
|
||||
}),
|
||||
category2 = store.createRecord("category", {
|
||||
id: 2,
|
||||
name: "middle term",
|
||||
slug: "another-different-slug",
|
||||
});
|
||||
|
||||
sinon.stub(Category, "listByActivity").returns([category1, category2]);
|
||||
|
||||
assert.deepEqual(
|
||||
Category.search("different-slug"),
|
||||
[category1, category2],
|
||||
"returns the right categories"
|
||||
);
|
||||
assert.deepEqual(
|
||||
Category.search("another-different"),
|
||||
[category2],
|
||||
"returns the right categories"
|
||||
);
|
||||
|
||||
category2.set("slug", "ANOTher-DIFfereNT");
|
||||
assert.deepEqual(
|
||||
Category.search("anOtHer-dIfFeREnt"),
|
||||
[category2],
|
||||
"ignores case of category slug and search term"
|
||||
);
|
||||
category2.set("name", "TeRm start");
|
||||
assert.deepEqual(
|
||||
Category.search("tErM"),
|
||||
[category2, category1],
|
||||
"ignores case of category name and search term"
|
||||
);
|
||||
|
||||
category2.set("name", "term start");
|
||||
assert.deepEqual(
|
||||
Category.search("term"),
|
||||
[category2, category1],
|
||||
"orders matching begin with and then contains"
|
||||
);
|
||||
|
||||
sinon.restore();
|
||||
|
||||
const child_category1 = store.createRecord("category", {
|
||||
id: 3,
|
||||
name: "term start",
|
||||
parent_category_id: category1.get("id"),
|
||||
}),
|
||||
read_restricted_category = store.createRecord("category", {
|
||||
id: 4,
|
||||
name: "some term",
|
||||
read_restricted: true,
|
||||
});
|
||||
|
||||
sinon
|
||||
.stub(Category, "listByActivity")
|
||||
.returns([
|
||||
read_restricted_category,
|
||||
category1,
|
||||
child_category1,
|
||||
category2,
|
||||
]);
|
||||
|
||||
assert.deepEqual(
|
||||
Category.search(""),
|
||||
[category1, category2, read_restricted_category],
|
||||
"prioritize non read_restricted and does not include child categories when term is blank"
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
Category.search("", { limit: 3 }),
|
||||
[category1, category2, read_restricted_category],
|
||||
"prioritize non read_restricted and does not include child categories categories when term is blank with limit"
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
Category.search("term"),
|
||||
[child_category1, category2, category1, read_restricted_category],
|
||||
"prioritize non read_restricted"
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
Category.search("term", { limit: 3 }),
|
||||
[child_category1, category2, read_restricted_category],
|
||||
"prioritize non read_restricted with limit"
|
||||
);
|
||||
|
||||
sinon.restore();
|
||||
});
|
||||
|
||||
test("search with category slug", function (assert) {
|
||||
const store = createStore(),
|
||||
category1 = store.createRecord("category", {
|
||||
id: 1,
|
||||
name: "middle term",
|
||||
slug: "different-slug",
|
||||
}),
|
||||
category2 = store.createRecord("category", {
|
||||
id: 2,
|
||||
name: "middle term",
|
||||
slug: "another-different-slug",
|
||||
});
|
||||
|
||||
sinon.stub(Category, "listByActivity").returns([category1, category2]);
|
||||
|
||||
assert.deepEqual(
|
||||
Category.search("different-slug"),
|
||||
[category1, category2],
|
||||
"returns the right categories"
|
||||
);
|
||||
assert.deepEqual(
|
||||
Category.search("another-different"),
|
||||
[category2],
|
||||
"returns the right categories"
|
||||
);
|
||||
|
||||
category2.set("slug", "ANOTher-DIFfereNT");
|
||||
assert.deepEqual(
|
||||
Category.search("anOtHer-dIfFeREnt"),
|
||||
[category2],
|
||||
"ignores case of category slug and search term"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,8 +14,6 @@ import {
|
||||
import Post from "discourse/models/post";
|
||||
import createStore from "discourse/tests/helpers/create-store";
|
||||
|
||||
discourseModule("model:composer");
|
||||
|
||||
function createComposer(opts) {
|
||||
opts = opts || {};
|
||||
opts.user = opts.user || currentUser();
|
||||
@@ -29,382 +27,384 @@ function openComposer(opts) {
|
||||
return composer;
|
||||
}
|
||||
|
||||
test("replyLength", function (assert) {
|
||||
const replyLength = function (val, expectedLength) {
|
||||
const composer = createComposer({ reply: val });
|
||||
assert.equal(composer.get("replyLength"), expectedLength);
|
||||
};
|
||||
discourseModule("Unit | Model | composer", function () {
|
||||
test("replyLength", function (assert) {
|
||||
const replyLength = function (val, expectedLength) {
|
||||
const composer = createComposer({ reply: val });
|
||||
assert.equal(composer.get("replyLength"), expectedLength);
|
||||
};
|
||||
|
||||
replyLength("basic reply", 11, "basic reply length");
|
||||
replyLength(" \nbasic reply\t", 11, "trims whitespaces");
|
||||
replyLength("ba sic\n\nreply", 12, "count only significant whitespaces");
|
||||
replyLength(
|
||||
"1[quote=]not counted[/quote]2[quote=]at all[/quote]3",
|
||||
3,
|
||||
"removes quotes"
|
||||
);
|
||||
replyLength(
|
||||
"1[quote=]not[quote=]counted[/quote]yay[/quote]2",
|
||||
2,
|
||||
"handles nested quotes correctly"
|
||||
);
|
||||
});
|
||||
|
||||
test("missingReplyCharacters", function (assert) {
|
||||
this.siteSettings.min_first_post_length = 40;
|
||||
const missingReplyCharacters = function (
|
||||
val,
|
||||
isPM,
|
||||
isFirstPost,
|
||||
expected,
|
||||
message
|
||||
) {
|
||||
let action = REPLY;
|
||||
if (isPM) {
|
||||
action = PRIVATE_MESSAGE;
|
||||
}
|
||||
if (isFirstPost) {
|
||||
action = CREATE_TOPIC;
|
||||
}
|
||||
const composer = createComposer({ reply: val, action });
|
||||
assert.equal(composer.get("missingReplyCharacters"), expected, message);
|
||||
};
|
||||
|
||||
missingReplyCharacters(
|
||||
"hi",
|
||||
false,
|
||||
false,
|
||||
this.siteSettings.min_post_length - 2,
|
||||
"too short public post"
|
||||
);
|
||||
missingReplyCharacters(
|
||||
"hi",
|
||||
false,
|
||||
true,
|
||||
this.siteSettings.min_first_post_length - 2,
|
||||
"too short first post"
|
||||
);
|
||||
missingReplyCharacters(
|
||||
"hi",
|
||||
true,
|
||||
false,
|
||||
this.siteSettings.min_personal_message_post_length - 2,
|
||||
"too short private message"
|
||||
);
|
||||
|
||||
const link = "http://imgur.com/gallery/grxX8";
|
||||
this.siteSettings.topic_featured_link_enabled = true;
|
||||
this.siteSettings.topic_featured_link_allowed_category_ids = 12345;
|
||||
const composer = createComposer({
|
||||
title: link,
|
||||
categoryId: 12345,
|
||||
featuredLink: link,
|
||||
action: CREATE_TOPIC,
|
||||
reply: link,
|
||||
replyLength("basic reply", 11, "basic reply length");
|
||||
replyLength(" \nbasic reply\t", 11, "trims whitespaces");
|
||||
replyLength("ba sic\n\nreply", 12, "count only significant whitespaces");
|
||||
replyLength(
|
||||
"1[quote=]not counted[/quote]2[quote=]at all[/quote]3",
|
||||
3,
|
||||
"removes quotes"
|
||||
);
|
||||
replyLength(
|
||||
"1[quote=]not[quote=]counted[/quote]yay[/quote]2",
|
||||
2,
|
||||
"handles nested quotes correctly"
|
||||
);
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
composer.get("missingReplyCharacters"),
|
||||
0,
|
||||
"don't require any post content"
|
||||
);
|
||||
});
|
||||
test("missingReplyCharacters", function (assert) {
|
||||
this.siteSettings.min_first_post_length = 40;
|
||||
const missingReplyCharacters = function (
|
||||
val,
|
||||
isPM,
|
||||
isFirstPost,
|
||||
expected,
|
||||
message
|
||||
) {
|
||||
let action = REPLY;
|
||||
if (isPM) {
|
||||
action = PRIVATE_MESSAGE;
|
||||
}
|
||||
if (isFirstPost) {
|
||||
action = CREATE_TOPIC;
|
||||
}
|
||||
const composer = createComposer({ reply: val, action });
|
||||
assert.equal(composer.get("missingReplyCharacters"), expected, message);
|
||||
};
|
||||
|
||||
test("missingTitleCharacters", function (assert) {
|
||||
const missingTitleCharacters = function (val, isPM, expected, message) {
|
||||
missingReplyCharacters(
|
||||
"hi",
|
||||
false,
|
||||
false,
|
||||
this.siteSettings.min_post_length - 2,
|
||||
"too short public post"
|
||||
);
|
||||
missingReplyCharacters(
|
||||
"hi",
|
||||
false,
|
||||
true,
|
||||
this.siteSettings.min_first_post_length - 2,
|
||||
"too short first post"
|
||||
);
|
||||
missingReplyCharacters(
|
||||
"hi",
|
||||
true,
|
||||
false,
|
||||
this.siteSettings.min_personal_message_post_length - 2,
|
||||
"too short private message"
|
||||
);
|
||||
|
||||
const link = "http://imgur.com/gallery/grxX8";
|
||||
this.siteSettings.topic_featured_link_enabled = true;
|
||||
this.siteSettings.topic_featured_link_allowed_category_ids = 12345;
|
||||
const composer = createComposer({
|
||||
title: val,
|
||||
action: isPM ? PRIVATE_MESSAGE : REPLY,
|
||||
title: link,
|
||||
categoryId: 12345,
|
||||
featuredLink: link,
|
||||
action: CREATE_TOPIC,
|
||||
reply: link,
|
||||
});
|
||||
assert.equal(composer.get("missingTitleCharacters"), expected, message);
|
||||
};
|
||||
|
||||
missingTitleCharacters(
|
||||
"hi",
|
||||
false,
|
||||
this.siteSettings.min_topic_title_length - 2,
|
||||
"too short post title"
|
||||
);
|
||||
missingTitleCharacters(
|
||||
"z",
|
||||
true,
|
||||
this.siteSettings.min_personal_message_title_length - 1,
|
||||
"too short pm title"
|
||||
);
|
||||
});
|
||||
|
||||
test("replyDirty", function (assert) {
|
||||
const composer = createComposer();
|
||||
assert.ok(!composer.get("replyDirty"), "by default it's false");
|
||||
|
||||
composer.setProperties({
|
||||
originalText: "hello",
|
||||
reply: "hello",
|
||||
assert.equal(
|
||||
composer.get("missingReplyCharacters"),
|
||||
0,
|
||||
"don't require any post content"
|
||||
);
|
||||
});
|
||||
|
||||
assert.ok(
|
||||
!composer.get("replyDirty"),
|
||||
"it's false when the originalText is the same as the reply"
|
||||
);
|
||||
composer.set("reply", "hello world");
|
||||
assert.ok(composer.get("replyDirty"), "it's true when the reply changes");
|
||||
});
|
||||
test("missingTitleCharacters", function (assert) {
|
||||
const missingTitleCharacters = function (val, isPM, expected, message) {
|
||||
const composer = createComposer({
|
||||
title: val,
|
||||
action: isPM ? PRIVATE_MESSAGE : REPLY,
|
||||
});
|
||||
assert.equal(composer.get("missingTitleCharacters"), expected, message);
|
||||
};
|
||||
|
||||
test("appendText", function (assert) {
|
||||
const composer = createComposer();
|
||||
|
||||
assert.blank(composer.get("reply"), "the reply is blank by default");
|
||||
|
||||
composer.appendText("hello");
|
||||
assert.equal(composer.get("reply"), "hello", "it appends text to nothing");
|
||||
composer.appendText(" world");
|
||||
assert.equal(
|
||||
composer.get("reply"),
|
||||
"hello world",
|
||||
"it appends text to existing text"
|
||||
);
|
||||
|
||||
composer.clearState();
|
||||
composer.appendText("a\n\n\n\nb");
|
||||
composer.appendText("c", 3, { block: true });
|
||||
|
||||
assert.equal(composer.get("reply"), "a\n\nc\n\nb");
|
||||
|
||||
composer.clearState();
|
||||
composer.appendText("ab");
|
||||
composer.appendText("c", 1, { block: true });
|
||||
|
||||
assert.equal(composer.get("reply"), "a\n\nc\n\nb");
|
||||
|
||||
composer.clearState();
|
||||
composer.appendText("\nab");
|
||||
composer.appendText("c", 0, { block: true });
|
||||
|
||||
assert.equal(composer.get("reply"), "c\n\nab");
|
||||
});
|
||||
|
||||
test("prependText", function (assert) {
|
||||
const composer = createComposer();
|
||||
|
||||
assert.blank(composer.get("reply"), "the reply is blank by default");
|
||||
|
||||
composer.prependText("hello");
|
||||
assert.equal(composer.get("reply"), "hello", "it prepends text to nothing");
|
||||
|
||||
composer.prependText("world ");
|
||||
assert.equal(
|
||||
composer.get("reply"),
|
||||
"world hello",
|
||||
"it prepends text to existing text"
|
||||
);
|
||||
|
||||
composer.prependText("before new line", { new_line: true });
|
||||
assert.equal(
|
||||
composer.get("reply"),
|
||||
"before new line\n\nworld hello",
|
||||
"it prepends text with new line to existing text"
|
||||
);
|
||||
});
|
||||
|
||||
test("Title length for regular topics", function (assert) {
|
||||
this.siteSettings.min_topic_title_length = 5;
|
||||
this.siteSettings.max_topic_title_length = 10;
|
||||
const composer = createComposer();
|
||||
|
||||
composer.set("title", "asdf");
|
||||
assert.ok(!composer.get("titleLengthValid"), "short titles are not valid");
|
||||
|
||||
composer.set("title", "this is a long title");
|
||||
assert.ok(!composer.get("titleLengthValid"), "long titles are not valid");
|
||||
|
||||
composer.set("title", "just right");
|
||||
assert.ok(composer.get("titleLengthValid"), "in the range is okay");
|
||||
});
|
||||
|
||||
test("Title length for private messages", function (assert) {
|
||||
this.siteSettings.min_personal_message_title_length = 5;
|
||||
this.siteSettings.max_topic_title_length = 10;
|
||||
const composer = createComposer({ action: PRIVATE_MESSAGE });
|
||||
|
||||
composer.set("title", "asdf");
|
||||
assert.ok(!composer.get("titleLengthValid"), "short titles are not valid");
|
||||
|
||||
composer.set("title", "this is a long title");
|
||||
assert.ok(!composer.get("titleLengthValid"), "long titles are not valid");
|
||||
|
||||
composer.set("title", "just right");
|
||||
assert.ok(composer.get("titleLengthValid"), "in the range is okay");
|
||||
});
|
||||
|
||||
test("Post length for private messages with non human users", function (assert) {
|
||||
const composer = createComposer({
|
||||
topic: EmberObject.create({ pm_with_non_human_user: true }),
|
||||
missingTitleCharacters(
|
||||
"hi",
|
||||
false,
|
||||
this.siteSettings.min_topic_title_length - 2,
|
||||
"too short post title"
|
||||
);
|
||||
missingTitleCharacters(
|
||||
"z",
|
||||
true,
|
||||
this.siteSettings.min_personal_message_title_length - 1,
|
||||
"too short pm title"
|
||||
);
|
||||
});
|
||||
|
||||
assert.equal(composer.get("minimumPostLength"), 1);
|
||||
});
|
||||
test("replyDirty", function (assert) {
|
||||
const composer = createComposer();
|
||||
assert.ok(!composer.get("replyDirty"), "by default it's false");
|
||||
|
||||
test("editingFirstPost", function (assert) {
|
||||
const composer = createComposer();
|
||||
assert.ok(!composer.get("editingFirstPost"), "it's false by default");
|
||||
composer.setProperties({
|
||||
originalText: "hello",
|
||||
reply: "hello",
|
||||
});
|
||||
|
||||
const post = Post.create({ id: 123, post_number: 2 });
|
||||
composer.setProperties({ post: post, action: EDIT });
|
||||
assert.ok(
|
||||
!composer.get("editingFirstPost"),
|
||||
"it's false when not editing the first post"
|
||||
);
|
||||
|
||||
post.set("post_number", 1);
|
||||
assert.ok(
|
||||
composer.get("editingFirstPost"),
|
||||
"it's true when editing the first post"
|
||||
);
|
||||
});
|
||||
|
||||
test("clearState", function (assert) {
|
||||
const composer = createComposer({
|
||||
originalText: "asdf",
|
||||
reply: "asdf2",
|
||||
post: Post.create({ id: 1 }),
|
||||
title: "wat",
|
||||
assert.ok(
|
||||
!composer.get("replyDirty"),
|
||||
"it's false when the originalText is the same as the reply"
|
||||
);
|
||||
composer.set("reply", "hello world");
|
||||
assert.ok(composer.get("replyDirty"), "it's true when the reply changes");
|
||||
});
|
||||
|
||||
composer.clearState();
|
||||
test("appendText", function (assert) {
|
||||
const composer = createComposer();
|
||||
|
||||
assert.blank(composer.get("originalText"));
|
||||
assert.blank(composer.get("reply"));
|
||||
assert.blank(composer.get("post"));
|
||||
assert.blank(composer.get("title"));
|
||||
});
|
||||
assert.blank(composer.get("reply"), "the reply is blank by default");
|
||||
|
||||
test("initial category when uncategorized is allowed", function (assert) {
|
||||
this.siteSettings.allow_uncategorized_topics = true;
|
||||
const composer = openComposer({
|
||||
action: CREATE_TOPIC,
|
||||
draftKey: "asfd",
|
||||
draftSequence: 1,
|
||||
composer.appendText("hello");
|
||||
assert.equal(composer.get("reply"), "hello", "it appends text to nothing");
|
||||
composer.appendText(" world");
|
||||
assert.equal(
|
||||
composer.get("reply"),
|
||||
"hello world",
|
||||
"it appends text to existing text"
|
||||
);
|
||||
|
||||
composer.clearState();
|
||||
composer.appendText("a\n\n\n\nb");
|
||||
composer.appendText("c", 3, { block: true });
|
||||
|
||||
assert.equal(composer.get("reply"), "a\n\nc\n\nb");
|
||||
|
||||
composer.clearState();
|
||||
composer.appendText("ab");
|
||||
composer.appendText("c", 1, { block: true });
|
||||
|
||||
assert.equal(composer.get("reply"), "a\n\nc\n\nb");
|
||||
|
||||
composer.clearState();
|
||||
composer.appendText("\nab");
|
||||
composer.appendText("c", 0, { block: true });
|
||||
|
||||
assert.equal(composer.get("reply"), "c\n\nab");
|
||||
});
|
||||
assert.ok(!composer.get("categoryId"), "Uncategorized by default");
|
||||
});
|
||||
|
||||
test("initial category when uncategorized is not allowed", function (assert) {
|
||||
this.siteSettings.allow_uncategorized_topics = false;
|
||||
const composer = openComposer({
|
||||
action: CREATE_TOPIC,
|
||||
draftKey: "asfd",
|
||||
draftSequence: 1,
|
||||
test("prependText", function (assert) {
|
||||
const composer = createComposer();
|
||||
|
||||
assert.blank(composer.get("reply"), "the reply is blank by default");
|
||||
|
||||
composer.prependText("hello");
|
||||
assert.equal(composer.get("reply"), "hello", "it prepends text to nothing");
|
||||
|
||||
composer.prependText("world ");
|
||||
assert.equal(
|
||||
composer.get("reply"),
|
||||
"world hello",
|
||||
"it prepends text to existing text"
|
||||
);
|
||||
|
||||
composer.prependText("before new line", { new_line: true });
|
||||
assert.equal(
|
||||
composer.get("reply"),
|
||||
"before new line\n\nworld hello",
|
||||
"it prepends text with new line to existing text"
|
||||
);
|
||||
});
|
||||
assert.ok(
|
||||
!composer.get("categoryId"),
|
||||
"Uncategorized by default. Must choose a category."
|
||||
);
|
||||
});
|
||||
|
||||
test("open with a quote", function (assert) {
|
||||
const quote =
|
||||
'[quote="neil, post:5, topic:413"]\nSimmer down you two.\n[/quote]';
|
||||
const newComposer = function () {
|
||||
return openComposer({
|
||||
action: REPLY,
|
||||
test("Title length for regular topics", function (assert) {
|
||||
this.siteSettings.min_topic_title_length = 5;
|
||||
this.siteSettings.max_topic_title_length = 10;
|
||||
const composer = createComposer();
|
||||
|
||||
composer.set("title", "asdf");
|
||||
assert.ok(!composer.get("titleLengthValid"), "short titles are not valid");
|
||||
|
||||
composer.set("title", "this is a long title");
|
||||
assert.ok(!composer.get("titleLengthValid"), "long titles are not valid");
|
||||
|
||||
composer.set("title", "just right");
|
||||
assert.ok(composer.get("titleLengthValid"), "in the range is okay");
|
||||
});
|
||||
|
||||
test("Title length for private messages", function (assert) {
|
||||
this.siteSettings.min_personal_message_title_length = 5;
|
||||
this.siteSettings.max_topic_title_length = 10;
|
||||
const composer = createComposer({ action: PRIVATE_MESSAGE });
|
||||
|
||||
composer.set("title", "asdf");
|
||||
assert.ok(!composer.get("titleLengthValid"), "short titles are not valid");
|
||||
|
||||
composer.set("title", "this is a long title");
|
||||
assert.ok(!composer.get("titleLengthValid"), "long titles are not valid");
|
||||
|
||||
composer.set("title", "just right");
|
||||
assert.ok(composer.get("titleLengthValid"), "in the range is okay");
|
||||
});
|
||||
|
||||
test("Post length for private messages with non human users", function (assert) {
|
||||
const composer = createComposer({
|
||||
topic: EmberObject.create({ pm_with_non_human_user: true }),
|
||||
});
|
||||
|
||||
assert.equal(composer.get("minimumPostLength"), 1);
|
||||
});
|
||||
|
||||
test("editingFirstPost", function (assert) {
|
||||
const composer = createComposer();
|
||||
assert.ok(!composer.get("editingFirstPost"), "it's false by default");
|
||||
|
||||
const post = Post.create({ id: 123, post_number: 2 });
|
||||
composer.setProperties({ post: post, action: EDIT });
|
||||
assert.ok(
|
||||
!composer.get("editingFirstPost"),
|
||||
"it's false when not editing the first post"
|
||||
);
|
||||
|
||||
post.set("post_number", 1);
|
||||
assert.ok(
|
||||
composer.get("editingFirstPost"),
|
||||
"it's true when editing the first post"
|
||||
);
|
||||
});
|
||||
|
||||
test("clearState", function (assert) {
|
||||
const composer = createComposer({
|
||||
originalText: "asdf",
|
||||
reply: "asdf2",
|
||||
post: Post.create({ id: 1 }),
|
||||
title: "wat",
|
||||
});
|
||||
|
||||
composer.clearState();
|
||||
|
||||
assert.blank(composer.get("originalText"));
|
||||
assert.blank(composer.get("reply"));
|
||||
assert.blank(composer.get("post"));
|
||||
assert.blank(composer.get("title"));
|
||||
});
|
||||
|
||||
test("initial category when uncategorized is allowed", function (assert) {
|
||||
this.siteSettings.allow_uncategorized_topics = true;
|
||||
const composer = openComposer({
|
||||
action: CREATE_TOPIC,
|
||||
draftKey: "asfd",
|
||||
draftSequence: 1,
|
||||
quote: quote,
|
||||
});
|
||||
};
|
||||
|
||||
assert.equal(
|
||||
newComposer().get("originalText"),
|
||||
quote,
|
||||
"originalText is the quote"
|
||||
);
|
||||
assert.equal(
|
||||
newComposer().get("replyDirty"),
|
||||
false,
|
||||
"replyDirty is initally false with a quote"
|
||||
);
|
||||
});
|
||||
|
||||
test("Title length for static page topics as admin", function (assert) {
|
||||
this.siteSettings.min_topic_title_length = 5;
|
||||
this.siteSettings.max_topic_title_length = 10;
|
||||
const composer = createComposer();
|
||||
|
||||
const post = Post.create({
|
||||
id: 123,
|
||||
post_number: 2,
|
||||
static_doc: true,
|
||||
assert.ok(!composer.get("categoryId"), "Uncategorized by default");
|
||||
});
|
||||
composer.setProperties({ post: post, action: EDIT });
|
||||
|
||||
composer.set("title", "asdf");
|
||||
assert.ok(composer.get("titleLengthValid"), "admins can use short titles");
|
||||
|
||||
composer.set("title", "this is a long title");
|
||||
assert.ok(composer.get("titleLengthValid"), "admins can use long titles");
|
||||
|
||||
composer.set("title", "just right");
|
||||
assert.ok(composer.get("titleLengthValid"), "in the range is okay");
|
||||
|
||||
composer.set("title", "");
|
||||
assert.ok(
|
||||
!composer.get("titleLengthValid"),
|
||||
"admins must set title to at least 1 character"
|
||||
);
|
||||
});
|
||||
|
||||
test("title placeholder depends on what you're doing", function (assert) {
|
||||
let composer = createComposer({ action: CREATE_TOPIC });
|
||||
assert.equal(
|
||||
composer.get("titlePlaceholder"),
|
||||
"composer.title_placeholder",
|
||||
"placeholder for normal topic"
|
||||
);
|
||||
|
||||
composer = createComposer({ action: PRIVATE_MESSAGE });
|
||||
assert.equal(
|
||||
composer.get("titlePlaceholder"),
|
||||
"composer.title_placeholder",
|
||||
"placeholder for private message"
|
||||
);
|
||||
|
||||
this.siteSettings.topic_featured_link_enabled = true;
|
||||
|
||||
composer = createComposer({ action: CREATE_TOPIC });
|
||||
assert.equal(
|
||||
composer.get("titlePlaceholder"),
|
||||
"composer.title_or_link_placeholder",
|
||||
"placeholder invites you to paste a link"
|
||||
);
|
||||
|
||||
composer = createComposer({ action: PRIVATE_MESSAGE });
|
||||
assert.equal(
|
||||
composer.get("titlePlaceholder"),
|
||||
"composer.title_placeholder",
|
||||
"placeholder for private message with topic links enabled"
|
||||
);
|
||||
});
|
||||
|
||||
test("allows featured link before choosing a category", function (assert) {
|
||||
this.siteSettings.topic_featured_link_enabled = true;
|
||||
this.siteSettings.allow_uncategorized_topics = false;
|
||||
let composer = createComposer({ action: CREATE_TOPIC });
|
||||
assert.equal(
|
||||
composer.get("titlePlaceholder"),
|
||||
"composer.title_or_link_placeholder",
|
||||
"placeholder invites you to paste a link"
|
||||
);
|
||||
assert.ok(composer.get("canEditTopicFeaturedLink"), "can paste link");
|
||||
});
|
||||
|
||||
test("targetRecipientsArray contains types", function (assert) {
|
||||
let composer = createComposer({
|
||||
targetRecipients: "test,codinghorror,staff,foo@bar.com",
|
||||
test("initial category when uncategorized is not allowed", function (assert) {
|
||||
this.siteSettings.allow_uncategorized_topics = false;
|
||||
const composer = openComposer({
|
||||
action: CREATE_TOPIC,
|
||||
draftKey: "asfd",
|
||||
draftSequence: 1,
|
||||
});
|
||||
assert.ok(
|
||||
!composer.get("categoryId"),
|
||||
"Uncategorized by default. Must choose a category."
|
||||
);
|
||||
});
|
||||
|
||||
test("open with a quote", function (assert) {
|
||||
const quote =
|
||||
'[quote="neil, post:5, topic:413"]\nSimmer down you two.\n[/quote]';
|
||||
const newComposer = function () {
|
||||
return openComposer({
|
||||
action: REPLY,
|
||||
draftKey: "asfd",
|
||||
draftSequence: 1,
|
||||
quote: quote,
|
||||
});
|
||||
};
|
||||
|
||||
assert.equal(
|
||||
newComposer().get("originalText"),
|
||||
quote,
|
||||
"originalText is the quote"
|
||||
);
|
||||
assert.equal(
|
||||
newComposer().get("replyDirty"),
|
||||
false,
|
||||
"replyDirty is initally false with a quote"
|
||||
);
|
||||
});
|
||||
|
||||
test("Title length for static page topics as admin", function (assert) {
|
||||
this.siteSettings.min_topic_title_length = 5;
|
||||
this.siteSettings.max_topic_title_length = 10;
|
||||
const composer = createComposer();
|
||||
|
||||
const post = Post.create({
|
||||
id: 123,
|
||||
post_number: 2,
|
||||
static_doc: true,
|
||||
});
|
||||
composer.setProperties({ post: post, action: EDIT });
|
||||
|
||||
composer.set("title", "asdf");
|
||||
assert.ok(composer.get("titleLengthValid"), "admins can use short titles");
|
||||
|
||||
composer.set("title", "this is a long title");
|
||||
assert.ok(composer.get("titleLengthValid"), "admins can use long titles");
|
||||
|
||||
composer.set("title", "just right");
|
||||
assert.ok(composer.get("titleLengthValid"), "in the range is okay");
|
||||
|
||||
composer.set("title", "");
|
||||
assert.ok(
|
||||
!composer.get("titleLengthValid"),
|
||||
"admins must set title to at least 1 character"
|
||||
);
|
||||
});
|
||||
|
||||
test("title placeholder depends on what you're doing", function (assert) {
|
||||
let composer = createComposer({ action: CREATE_TOPIC });
|
||||
assert.equal(
|
||||
composer.get("titlePlaceholder"),
|
||||
"composer.title_placeholder",
|
||||
"placeholder for normal topic"
|
||||
);
|
||||
|
||||
composer = createComposer({ action: PRIVATE_MESSAGE });
|
||||
assert.equal(
|
||||
composer.get("titlePlaceholder"),
|
||||
"composer.title_placeholder",
|
||||
"placeholder for private message"
|
||||
);
|
||||
|
||||
this.siteSettings.topic_featured_link_enabled = true;
|
||||
|
||||
composer = createComposer({ action: CREATE_TOPIC });
|
||||
assert.equal(
|
||||
composer.get("titlePlaceholder"),
|
||||
"composer.title_or_link_placeholder",
|
||||
"placeholder invites you to paste a link"
|
||||
);
|
||||
|
||||
composer = createComposer({ action: PRIVATE_MESSAGE });
|
||||
assert.equal(
|
||||
composer.get("titlePlaceholder"),
|
||||
"composer.title_placeholder",
|
||||
"placeholder for private message with topic links enabled"
|
||||
);
|
||||
});
|
||||
|
||||
test("allows featured link before choosing a category", function (assert) {
|
||||
this.siteSettings.topic_featured_link_enabled = true;
|
||||
this.siteSettings.allow_uncategorized_topics = false;
|
||||
let composer = createComposer({ action: CREATE_TOPIC });
|
||||
assert.equal(
|
||||
composer.get("titlePlaceholder"),
|
||||
"composer.title_or_link_placeholder",
|
||||
"placeholder invites you to paste a link"
|
||||
);
|
||||
assert.ok(composer.get("canEditTopicFeaturedLink"), "can paste link");
|
||||
});
|
||||
|
||||
test("targetRecipientsArray contains types", function (assert) {
|
||||
let composer = createComposer({
|
||||
targetRecipients: "test,codinghorror,staff,foo@bar.com",
|
||||
});
|
||||
assert.ok(composer.targetRecipientsArray, [
|
||||
{ type: "group", name: "test" },
|
||||
{ type: "user", name: "codinghorror" },
|
||||
{ type: "group", name: "staff" },
|
||||
{ type: "email", name: "foo@bar.com" },
|
||||
]);
|
||||
});
|
||||
assert.ok(composer.targetRecipientsArray, [
|
||||
{ type: "group", name: "test" },
|
||||
{ type: "user", name: "codinghorror" },
|
||||
{ type: "group", name: "staff" },
|
||||
{ type: "email", name: "foo@bar.com" },
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -2,34 +2,34 @@ import { test, module } from "qunit";
|
||||
import EmailLog from "admin/models/email-log";
|
||||
import { setPrefix } from "discourse-common/lib/get-url";
|
||||
|
||||
module("model:email-log");
|
||||
module("Unit | Model | email-log", function () {
|
||||
test("create", function (assert) {
|
||||
assert.ok(EmailLog.create(), "it can be created without arguments");
|
||||
});
|
||||
|
||||
test("create", function (assert) {
|
||||
assert.ok(EmailLog.create(), "it can be created without arguments");
|
||||
});
|
||||
|
||||
test("subfolder support", function (assert) {
|
||||
setPrefix("/forum");
|
||||
const attrs = {
|
||||
id: 60,
|
||||
to_address: "wikiman@asdf.com",
|
||||
email_type: "user_linked",
|
||||
user_id: 9,
|
||||
created_at: "2018-08-08T17:21:52.022Z",
|
||||
post_url: "/t/some-pro-tips-for-you/41/5",
|
||||
post_description: "Some Pro Tips For You",
|
||||
bounced: false,
|
||||
user: {
|
||||
id: 9,
|
||||
username: "wikiman",
|
||||
avatar_template:
|
||||
"/forum/letter_avatar_proxy/v2/letter/w/dfb087/{size}.png",
|
||||
},
|
||||
};
|
||||
const emailLog = EmailLog.create(attrs);
|
||||
assert.equal(
|
||||
emailLog.get("post_url"),
|
||||
"/forum/t/some-pro-tips-for-you/41/5",
|
||||
"includes the subfolder in the post url"
|
||||
);
|
||||
test("subfolder support", function (assert) {
|
||||
setPrefix("/forum");
|
||||
const attrs = {
|
||||
id: 60,
|
||||
to_address: "wikiman@asdf.com",
|
||||
email_type: "user_linked",
|
||||
user_id: 9,
|
||||
created_at: "2018-08-08T17:21:52.022Z",
|
||||
post_url: "/t/some-pro-tips-for-you/41/5",
|
||||
post_description: "Some Pro Tips For You",
|
||||
bounced: false,
|
||||
user: {
|
||||
id: 9,
|
||||
username: "wikiman",
|
||||
avatar_template:
|
||||
"/forum/letter_avatar_proxy/v2/letter/w/dfb087/{size}.png",
|
||||
},
|
||||
};
|
||||
const emailLog = EmailLog.create(attrs);
|
||||
assert.equal(
|
||||
emailLog.get("post_url"),
|
||||
"/forum/t/some-pro-tips-for-you/41/5",
|
||||
"includes the subfolder in the post url"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
import { test, module } from "qunit";
|
||||
import Group from "discourse/models/group";
|
||||
|
||||
module("model:group");
|
||||
module("Unit | Model | group", function () {
|
||||
test("displayName", function (assert) {
|
||||
const group = Group.create({ name: "test", display_name: "donkey" });
|
||||
|
||||
test("displayName", function (assert) {
|
||||
const group = Group.create({ name: "test", display_name: "donkey" });
|
||||
assert.equal(
|
||||
group.get("displayName"),
|
||||
"donkey",
|
||||
"it should return the display name"
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
group.get("displayName"),
|
||||
"donkey",
|
||||
"it should return the display name"
|
||||
);
|
||||
group.set("display_name", null);
|
||||
|
||||
group.set("display_name", null);
|
||||
|
||||
assert.equal(
|
||||
group.get("displayName"),
|
||||
"test",
|
||||
"it should return the group's name"
|
||||
);
|
||||
assert.equal(
|
||||
group.get("displayName"),
|
||||
"test",
|
||||
"it should return the group's name"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { test, module } from "qunit";
|
||||
import Invite from "discourse/models/invite";
|
||||
|
||||
module("model:invite");
|
||||
|
||||
test("create", function (assert) {
|
||||
assert.ok(Invite.create(), "it can be created without arguments");
|
||||
module("Unit | Model | invite", function () {
|
||||
test("create", function (assert) {
|
||||
assert.ok(Invite.create(), "it can be created without arguments");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,8 +5,8 @@ import NavItem from "discourse/models/nav-item";
|
||||
import Category from "discourse/models/category";
|
||||
import Site from "discourse/models/site";
|
||||
|
||||
module("NavItem", {
|
||||
beforeEach() {
|
||||
module("Unit | Model | nav-item", function (hooks) {
|
||||
hooks.beforeEach(function () {
|
||||
run(function () {
|
||||
const asianCategory = Category.create({
|
||||
name: "确实是这样",
|
||||
@@ -14,32 +14,32 @@ module("NavItem", {
|
||||
});
|
||||
Site.currentProp("categories").addObject(asianCategory);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
test("href", function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
function href(text, expected, label) {
|
||||
assert.equal(NavItem.fromText(text, {}).get("href"), expected, label);
|
||||
}
|
||||
|
||||
href("latest", "/latest", "latest");
|
||||
href("categories", "/categories", "categories");
|
||||
});
|
||||
|
||||
test("count", function (assert) {
|
||||
const navItem = createStore().createRecord("nav-item", { name: "new" });
|
||||
|
||||
assert.equal(navItem.get("count"), 0, "it has no count by default");
|
||||
|
||||
const tracker = navItem.get("topicTrackingState");
|
||||
tracker.states["t1"] = { topic_id: 1, last_read_post_number: null };
|
||||
tracker.incrementMessageCount();
|
||||
|
||||
assert.equal(
|
||||
navItem.get("count"),
|
||||
1,
|
||||
"it updates when a new message arrives"
|
||||
);
|
||||
});
|
||||
|
||||
test("href", function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
function href(text, expected, label) {
|
||||
assert.equal(NavItem.fromText(text, {}).get("href"), expected, label);
|
||||
}
|
||||
|
||||
href("latest", "/latest", "latest");
|
||||
href("categories", "/categories", "categories");
|
||||
});
|
||||
|
||||
test("count", function (assert) {
|
||||
const navItem = createStore().createRecord("nav-item", { name: "new" });
|
||||
|
||||
assert.equal(navItem.get("count"), 0, "it has no count by default");
|
||||
|
||||
const tracker = navItem.get("topicTrackingState");
|
||||
tracker.states["t1"] = { topic_id: 1, last_read_post_number: null };
|
||||
tracker.incrementMessageCount();
|
||||
|
||||
assert.equal(
|
||||
navItem.get("count"),
|
||||
1,
|
||||
"it updates when a new message arrives"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,9 +3,7 @@ import Post from "discourse/models/post";
|
||||
import User from "discourse/models/user";
|
||||
import { deepMerge } from "discourse-common/lib/object";
|
||||
|
||||
module("model: Post");
|
||||
|
||||
var buildPost = function (args) {
|
||||
function buildPost(args) {
|
||||
return Post.create(
|
||||
deepMerge(
|
||||
{
|
||||
@@ -16,87 +14,89 @@ var buildPost = function (args) {
|
||||
args || {}
|
||||
)
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
test("defaults", function (assert) {
|
||||
var post = Post.create({ id: 1 });
|
||||
assert.blank(post.get("deleted_at"), "it has no deleted_at by default");
|
||||
assert.blank(post.get("deleted_by"), "there is no deleted_by by default");
|
||||
});
|
||||
|
||||
test("new_user", function (assert) {
|
||||
var post = Post.create({ trust_level: 0 });
|
||||
assert.ok(post.get("new_user"), "post is from a new user");
|
||||
|
||||
post.set("trust_level", 1);
|
||||
assert.ok(!post.get("new_user"), "post is no longer from a new user");
|
||||
});
|
||||
|
||||
test("firstPost", function (assert) {
|
||||
var post = Post.create({ post_number: 1 });
|
||||
assert.ok(post.get("firstPost"), "it's the first post");
|
||||
|
||||
post.set("post_number", 10);
|
||||
assert.ok(!post.get("firstPost"), "post is no longer the first post");
|
||||
});
|
||||
|
||||
test("updateFromPost", function (assert) {
|
||||
var post = Post.create({
|
||||
post_number: 1,
|
||||
raw: "hello world",
|
||||
module("Unit | Model | post", function () {
|
||||
test("defaults", function (assert) {
|
||||
var post = Post.create({ id: 1 });
|
||||
assert.blank(post.get("deleted_at"), "it has no deleted_at by default");
|
||||
assert.blank(post.get("deleted_by"), "there is no deleted_by by default");
|
||||
});
|
||||
|
||||
post.updateFromPost(
|
||||
Post.create({
|
||||
raw: "different raw",
|
||||
wat: function () {
|
||||
return 123;
|
||||
},
|
||||
})
|
||||
);
|
||||
test("new_user", function (assert) {
|
||||
var post = Post.create({ trust_level: 0 });
|
||||
assert.ok(post.get("new_user"), "post is from a new user");
|
||||
|
||||
assert.equal(post.get("raw"), "different raw", "raw field updated");
|
||||
});
|
||||
|
||||
test("destroy by staff", async function (assert) {
|
||||
let user = User.create({ username: "staff", moderator: true });
|
||||
let post = buildPost({ user: user });
|
||||
|
||||
await post.destroy(user);
|
||||
|
||||
assert.present(post.get("deleted_at"), "it has a `deleted_at` field.");
|
||||
assert.equal(
|
||||
post.get("deleted_by"),
|
||||
user,
|
||||
"it has the user in the `deleted_by` field"
|
||||
);
|
||||
|
||||
await post.recover();
|
||||
|
||||
assert.blank(
|
||||
post.get("deleted_at"),
|
||||
"it clears `deleted_at` when recovering"
|
||||
);
|
||||
assert.blank(
|
||||
post.get("deleted_by"),
|
||||
"it clears `deleted_by` when recovering"
|
||||
);
|
||||
});
|
||||
|
||||
test("destroy by non-staff", async function (assert) {
|
||||
const originalCooked = "this is the original cooked value";
|
||||
const user = User.create({ username: "evil trout" });
|
||||
const post = buildPost({ user: user, cooked: originalCooked });
|
||||
|
||||
await post.destroy(user);
|
||||
|
||||
assert.ok(
|
||||
!post.get("can_delete"),
|
||||
"the post can't be deleted again in this session"
|
||||
);
|
||||
assert.ok(
|
||||
post.get("cooked") !== originalCooked,
|
||||
"the cooked content changed"
|
||||
);
|
||||
assert.equal(post.get("version"), 2, "the version number increased");
|
||||
post.set("trust_level", 1);
|
||||
assert.ok(!post.get("new_user"), "post is no longer from a new user");
|
||||
});
|
||||
|
||||
test("firstPost", function (assert) {
|
||||
var post = Post.create({ post_number: 1 });
|
||||
assert.ok(post.get("firstPost"), "it's the first post");
|
||||
|
||||
post.set("post_number", 10);
|
||||
assert.ok(!post.get("firstPost"), "post is no longer the first post");
|
||||
});
|
||||
|
||||
test("updateFromPost", function (assert) {
|
||||
var post = Post.create({
|
||||
post_number: 1,
|
||||
raw: "hello world",
|
||||
});
|
||||
|
||||
post.updateFromPost(
|
||||
Post.create({
|
||||
raw: "different raw",
|
||||
wat: function () {
|
||||
return 123;
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
assert.equal(post.get("raw"), "different raw", "raw field updated");
|
||||
});
|
||||
|
||||
test("destroy by staff", async function (assert) {
|
||||
let user = User.create({ username: "staff", moderator: true });
|
||||
let post = buildPost({ user: user });
|
||||
|
||||
await post.destroy(user);
|
||||
|
||||
assert.present(post.get("deleted_at"), "it has a `deleted_at` field.");
|
||||
assert.equal(
|
||||
post.get("deleted_by"),
|
||||
user,
|
||||
"it has the user in the `deleted_by` field"
|
||||
);
|
||||
|
||||
await post.recover();
|
||||
|
||||
assert.blank(
|
||||
post.get("deleted_at"),
|
||||
"it clears `deleted_at` when recovering"
|
||||
);
|
||||
assert.blank(
|
||||
post.get("deleted_by"),
|
||||
"it clears `deleted_by` when recovering"
|
||||
);
|
||||
});
|
||||
|
||||
test("destroy by non-staff", async function (assert) {
|
||||
const originalCooked = "this is the original cooked value";
|
||||
const user = User.create({ username: "evil trout" });
|
||||
const post = buildPost({ user: user, cooked: originalCooked });
|
||||
|
||||
await post.destroy(user);
|
||||
|
||||
assert.ok(
|
||||
!post.get("can_delete"),
|
||||
"the post can't be deleted again in this session"
|
||||
);
|
||||
assert.ok(
|
||||
post.get("cooked") !== originalCooked,
|
||||
"the cooked content changed"
|
||||
);
|
||||
assert.equal(post.get("version"), 2, "the version number increased");
|
||||
});
|
||||
});
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,144 +1,144 @@
|
||||
import sinon from "sinon";
|
||||
import { test, module } from "qunit";
|
||||
module("rest-model");
|
||||
|
||||
import createStore from "discourse/tests/helpers/create-store";
|
||||
import RestModel from "discourse/models/rest";
|
||||
import RestAdapter from "discourse/adapters/rest";
|
||||
|
||||
test("munging", function (assert) {
|
||||
const store = createStore();
|
||||
const Grape = RestModel.extend();
|
||||
Grape.reopenClass({
|
||||
munge: function (json) {
|
||||
json.inverse = 1 - json.percent;
|
||||
return json;
|
||||
},
|
||||
module("Unit | Model | rest-model", function () {
|
||||
test("munging", function (assert) {
|
||||
const store = createStore();
|
||||
const Grape = RestModel.extend();
|
||||
Grape.reopenClass({
|
||||
munge: function (json) {
|
||||
json.inverse = 1 - json.percent;
|
||||
return json;
|
||||
},
|
||||
});
|
||||
|
||||
var g = Grape.create({ store, percent: 0.4 });
|
||||
assert.equal(g.get("inverse"), 0.6, "it runs `munge` on `create`");
|
||||
});
|
||||
|
||||
var g = Grape.create({ store, percent: 0.4 });
|
||||
assert.equal(g.get("inverse"), 0.6, "it runs `munge` on `create`");
|
||||
});
|
||||
test("update", async function (assert) {
|
||||
const store = createStore();
|
||||
const widget = await store.find("widget", 123);
|
||||
assert.equal(widget.get("name"), "Trout Lure");
|
||||
assert.ok(!widget.get("isSaving"), "it is not saving");
|
||||
|
||||
test("update", async function (assert) {
|
||||
const store = createStore();
|
||||
const widget = await store.find("widget", 123);
|
||||
assert.equal(widget.get("name"), "Trout Lure");
|
||||
assert.ok(!widget.get("isSaving"), "it is not saving");
|
||||
const spyBeforeUpdate = sinon.spy(widget, "beforeUpdate");
|
||||
const spyAfterUpdate = sinon.spy(widget, "afterUpdate");
|
||||
const promise = widget.update({ name: "new name" });
|
||||
assert.ok(widget.get("isSaving"), "it is saving");
|
||||
assert.ok(spyBeforeUpdate.calledOn(widget));
|
||||
|
||||
const spyBeforeUpdate = sinon.spy(widget, "beforeUpdate");
|
||||
const spyAfterUpdate = sinon.spy(widget, "afterUpdate");
|
||||
const promise = widget.update({ name: "new name" });
|
||||
assert.ok(widget.get("isSaving"), "it is saving");
|
||||
assert.ok(spyBeforeUpdate.calledOn(widget));
|
||||
const result = await promise;
|
||||
assert.ok(spyAfterUpdate.calledOn(widget));
|
||||
assert.ok(!widget.get("isSaving"), "it is no longer saving");
|
||||
assert.equal(widget.get("name"), "new name");
|
||||
|
||||
const result = await promise;
|
||||
assert.ok(spyAfterUpdate.calledOn(widget));
|
||||
assert.ok(!widget.get("isSaving"), "it is no longer saving");
|
||||
assert.equal(widget.get("name"), "new name");
|
||||
|
||||
assert.ok(result.target, "it has a reference to the record");
|
||||
assert.equal(result.target.name, widget.get("name"));
|
||||
});
|
||||
|
||||
test("updating simultaneously", async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const store = createStore();
|
||||
const widget = await store.find("widget", 123);
|
||||
|
||||
const firstPromise = widget.update({ name: "new name" });
|
||||
const secondPromise = widget.update({ name: "new name" });
|
||||
|
||||
firstPromise.then(function () {
|
||||
assert.ok(true, "the first promise succeeeds");
|
||||
assert.ok(result.target, "it has a reference to the record");
|
||||
assert.equal(result.target.name, widget.get("name"));
|
||||
});
|
||||
|
||||
secondPromise.catch(function () {
|
||||
assert.ok(true, "the second promise fails");
|
||||
});
|
||||
});
|
||||
test("updating simultaneously", async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
test("save new", async function (assert) {
|
||||
const store = createStore();
|
||||
const widget = store.createRecord("widget");
|
||||
const store = createStore();
|
||||
const widget = await store.find("widget", 123);
|
||||
|
||||
assert.ok(widget.get("isNew"), "it is a new record");
|
||||
assert.ok(!widget.get("isCreated"), "it is not created");
|
||||
assert.ok(!widget.get("isSaving"), "it is not saving");
|
||||
const firstPromise = widget.update({ name: "new name" });
|
||||
const secondPromise = widget.update({ name: "new name" });
|
||||
|
||||
const spyBeforeCreate = sinon.spy(widget, "beforeCreate");
|
||||
const spyAfterCreate = sinon.spy(widget, "afterCreate");
|
||||
const promise = widget.save({ name: "Evil Widget" });
|
||||
assert.ok(widget.get("isSaving"), "it is not saving");
|
||||
assert.ok(spyBeforeCreate.calledOn(widget));
|
||||
firstPromise.then(function () {
|
||||
assert.ok(true, "the first promise succeeeds");
|
||||
});
|
||||
|
||||
const result = await promise;
|
||||
assert.ok(spyAfterCreate.calledOn(widget));
|
||||
assert.ok(!widget.get("isSaving"), "it is no longer saving");
|
||||
assert.ok(widget.get("id"), "it has an id");
|
||||
assert.ok(widget.get("name"), "Evil Widget");
|
||||
assert.ok(widget.get("isCreated"), "it is created");
|
||||
assert.ok(!widget.get("isNew"), "it is no longer new");
|
||||
|
||||
assert.ok(result.target, "it has a reference to the record");
|
||||
assert.equal(result.target.name, widget.get("name"));
|
||||
});
|
||||
|
||||
test("creating simultaneously", function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const store = createStore();
|
||||
const widget = store.createRecord("widget");
|
||||
|
||||
const firstPromise = widget.save({ name: "Evil Widget" });
|
||||
const secondPromise = widget.save({ name: "Evil Widget" });
|
||||
firstPromise.then(function () {
|
||||
assert.ok(true, "the first promise succeeeds");
|
||||
secondPromise.catch(function () {
|
||||
assert.ok(true, "the second promise fails");
|
||||
});
|
||||
});
|
||||
|
||||
secondPromise.catch(function () {
|
||||
assert.ok(true, "the second promise fails");
|
||||
});
|
||||
});
|
||||
test("save new", async function (assert) {
|
||||
const store = createStore();
|
||||
const widget = store.createRecord("widget");
|
||||
|
||||
test("destroyRecord", async function (assert) {
|
||||
const store = createStore();
|
||||
const widget = await store.find("widget", 123);
|
||||
assert.ok(widget.get("isNew"), "it is a new record");
|
||||
assert.ok(!widget.get("isCreated"), "it is not created");
|
||||
assert.ok(!widget.get("isSaving"), "it is not saving");
|
||||
|
||||
assert.ok(await widget.destroyRecord());
|
||||
});
|
||||
const spyBeforeCreate = sinon.spy(widget, "beforeCreate");
|
||||
const spyAfterCreate = sinon.spy(widget, "afterCreate");
|
||||
const promise = widget.save({ name: "Evil Widget" });
|
||||
assert.ok(widget.get("isSaving"), "it is not saving");
|
||||
assert.ok(spyBeforeCreate.calledOn(widget));
|
||||
|
||||
test("custom api name", async function (assert) {
|
||||
const store = createStore((type) => {
|
||||
if (type === "adapter:my-widget") {
|
||||
return RestAdapter.extend({
|
||||
// An adapter like this is used when the server-side key/url
|
||||
// do not match the name of the es6 class
|
||||
apiNameFor() {
|
||||
return "widget";
|
||||
},
|
||||
}).create();
|
||||
}
|
||||
const result = await promise;
|
||||
assert.ok(spyAfterCreate.calledOn(widget));
|
||||
assert.ok(!widget.get("isSaving"), "it is no longer saving");
|
||||
assert.ok(widget.get("id"), "it has an id");
|
||||
assert.ok(widget.get("name"), "Evil Widget");
|
||||
assert.ok(widget.get("isCreated"), "it is created");
|
||||
assert.ok(!widget.get("isNew"), "it is no longer new");
|
||||
|
||||
assert.ok(result.target, "it has a reference to the record");
|
||||
assert.equal(result.target.name, widget.get("name"));
|
||||
});
|
||||
|
||||
// The pretenders only respond to requests for `widget`
|
||||
// If these basic tests pass, the name override worked correctly
|
||||
test("creating simultaneously", function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
//Create
|
||||
const widget = store.createRecord("my-widget");
|
||||
await widget.save({ name: "Evil Widget" });
|
||||
assert.equal(widget.id, 100, "it saved a new record successully");
|
||||
assert.equal(widget.get("name"), "Evil Widget");
|
||||
const store = createStore();
|
||||
const widget = store.createRecord("widget");
|
||||
|
||||
// Update
|
||||
await widget.update({ name: "new name" });
|
||||
assert.equal(widget.get("name"), "new name");
|
||||
const firstPromise = widget.save({ name: "Evil Widget" });
|
||||
const secondPromise = widget.save({ name: "Evil Widget" });
|
||||
firstPromise.then(function () {
|
||||
assert.ok(true, "the first promise succeeeds");
|
||||
});
|
||||
|
||||
// Destroy
|
||||
await widget.destroyRecord();
|
||||
secondPromise.catch(function () {
|
||||
assert.ok(true, "the second promise fails");
|
||||
});
|
||||
});
|
||||
|
||||
// Lookup
|
||||
const foundWidget = await store.find("my-widget", 123);
|
||||
assert.equal(foundWidget.name, "Trout Lure");
|
||||
test("destroyRecord", async function (assert) {
|
||||
const store = createStore();
|
||||
const widget = await store.find("widget", 123);
|
||||
|
||||
assert.ok(await widget.destroyRecord());
|
||||
});
|
||||
|
||||
test("custom api name", async function (assert) {
|
||||
const store = createStore((type) => {
|
||||
if (type === "adapter:my-widget") {
|
||||
return RestAdapter.extend({
|
||||
// An adapter like this is used when the server-side key/url
|
||||
// do not match the name of the es6 class
|
||||
apiNameFor() {
|
||||
return "widget";
|
||||
},
|
||||
}).create();
|
||||
}
|
||||
});
|
||||
|
||||
// The pretenders only respond to requests for `widget`
|
||||
// If these basic tests pass, the name override worked correctly
|
||||
|
||||
//Create
|
||||
const widget = store.createRecord("my-widget");
|
||||
await widget.save({ name: "Evil Widget" });
|
||||
assert.equal(widget.id, 100, "it saved a new record successully");
|
||||
assert.equal(widget.get("name"), "Evil Widget");
|
||||
|
||||
// Update
|
||||
await widget.update({ name: "new name" });
|
||||
assert.equal(widget.get("name"), "new name");
|
||||
|
||||
// Destroy
|
||||
await widget.destroyRecord();
|
||||
|
||||
// Lookup
|
||||
const foundWidget = await store.find("my-widget", 123);
|
||||
assert.equal(foundWidget.name, "Trout Lure");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,50 +1,50 @@
|
||||
import { test, module } from "qunit";
|
||||
module("result-set");
|
||||
|
||||
import ResultSet from "discourse/models/result-set";
|
||||
import createStore from "discourse/tests/helpers/create-store";
|
||||
|
||||
test("defaults", function (assert) {
|
||||
const resultSet = ResultSet.create({ content: [] });
|
||||
assert.equal(resultSet.get("length"), 0);
|
||||
assert.equal(resultSet.get("totalRows"), 0);
|
||||
assert.ok(!resultSet.get("loadMoreUrl"));
|
||||
assert.ok(!resultSet.get("loading"));
|
||||
assert.ok(!resultSet.get("loadingMore"));
|
||||
assert.ok(!resultSet.get("refreshing"));
|
||||
});
|
||||
|
||||
test("pagination support", async function (assert) {
|
||||
const store = createStore();
|
||||
const resultSet = await store.findAll("widget");
|
||||
assert.equal(resultSet.get("length"), 2);
|
||||
assert.equal(resultSet.get("totalRows"), 4);
|
||||
assert.ok(resultSet.get("loadMoreUrl"), "has a url to load more");
|
||||
assert.ok(!resultSet.get("loadingMore"), "it is not loading more");
|
||||
assert.ok(resultSet.get("canLoadMore"));
|
||||
|
||||
const promise = resultSet.loadMore();
|
||||
assert.ok(resultSet.get("loadingMore"), "it is loading more");
|
||||
|
||||
await promise;
|
||||
assert.ok(!resultSet.get("loadingMore"), "it finished loading more");
|
||||
assert.equal(resultSet.get("length"), 4);
|
||||
assert.ok(!resultSet.get("loadMoreUrl"));
|
||||
assert.ok(!resultSet.get("canLoadMore"));
|
||||
});
|
||||
|
||||
test("refresh support", async function (assert) {
|
||||
const store = createStore();
|
||||
const resultSet = await store.findAll("widget");
|
||||
assert.equal(
|
||||
resultSet.get("refreshUrl"),
|
||||
"/widgets?refresh=true",
|
||||
"it has the refresh url"
|
||||
);
|
||||
|
||||
const promise = resultSet.refresh();
|
||||
assert.ok(resultSet.get("refreshing"), "it is refreshing");
|
||||
|
||||
await promise;
|
||||
assert.ok(!resultSet.get("refreshing"), "it is finished refreshing");
|
||||
module("Unit | Model | result-set", function () {
|
||||
test("defaults", function (assert) {
|
||||
const resultSet = ResultSet.create({ content: [] });
|
||||
assert.equal(resultSet.get("length"), 0);
|
||||
assert.equal(resultSet.get("totalRows"), 0);
|
||||
assert.ok(!resultSet.get("loadMoreUrl"));
|
||||
assert.ok(!resultSet.get("loading"));
|
||||
assert.ok(!resultSet.get("loadingMore"));
|
||||
assert.ok(!resultSet.get("refreshing"));
|
||||
});
|
||||
|
||||
test("pagination support", async function (assert) {
|
||||
const store = createStore();
|
||||
const resultSet = await store.findAll("widget");
|
||||
assert.equal(resultSet.get("length"), 2);
|
||||
assert.equal(resultSet.get("totalRows"), 4);
|
||||
assert.ok(resultSet.get("loadMoreUrl"), "has a url to load more");
|
||||
assert.ok(!resultSet.get("loadingMore"), "it is not loading more");
|
||||
assert.ok(resultSet.get("canLoadMore"));
|
||||
|
||||
const promise = resultSet.loadMore();
|
||||
assert.ok(resultSet.get("loadingMore"), "it is loading more");
|
||||
|
||||
await promise;
|
||||
assert.ok(!resultSet.get("loadingMore"), "it finished loading more");
|
||||
assert.equal(resultSet.get("length"), 4);
|
||||
assert.ok(!resultSet.get("loadMoreUrl"));
|
||||
assert.ok(!resultSet.get("canLoadMore"));
|
||||
});
|
||||
|
||||
test("refresh support", async function (assert) {
|
||||
const store = createStore();
|
||||
const resultSet = await store.findAll("widget");
|
||||
assert.equal(
|
||||
resultSet.get("refreshUrl"),
|
||||
"/widgets?refresh=true",
|
||||
"it has the refresh url"
|
||||
);
|
||||
|
||||
const promise = resultSet.refresh();
|
||||
assert.ok(resultSet.get("refreshing"), "it is refreshing");
|
||||
|
||||
await promise;
|
||||
assert.ok(!resultSet.get("refreshing"), "it is finished refreshing");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { test, module } from "qunit";
|
||||
import Session from "discourse/models/session";
|
||||
|
||||
module("model:session");
|
||||
|
||||
test("highestSeenByTopic", function (assert) {
|
||||
const session = Session.current();
|
||||
assert.deepEqual(
|
||||
session.get("highestSeenByTopic"),
|
||||
{},
|
||||
"by default it returns an empty object"
|
||||
);
|
||||
module("Unit | Model | session", function () {
|
||||
test("highestSeenByTopic", function (assert) {
|
||||
const session = Session.current();
|
||||
assert.deepEqual(
|
||||
session.get("highestSeenByTopic"),
|
||||
{},
|
||||
"by default it returns an empty object"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,72 +2,72 @@ import { test, module } from "qunit";
|
||||
import createStore from "discourse/tests/helpers/create-store";
|
||||
import Site from "discourse/models/site";
|
||||
|
||||
module("model:site");
|
||||
|
||||
test("create", function (assert) {
|
||||
assert.ok(Site.create(), "it can create with no parameters");
|
||||
});
|
||||
|
||||
test("instance", function (assert) {
|
||||
const site = Site.current();
|
||||
|
||||
assert.present(site, "We have a current site singleton");
|
||||
assert.present(
|
||||
site.get("categories"),
|
||||
"The instance has a list of categories"
|
||||
);
|
||||
assert.present(
|
||||
site.get("flagTypes"),
|
||||
"The instance has a list of flag types"
|
||||
);
|
||||
assert.present(
|
||||
site.get("trustLevels"),
|
||||
"The instance has a list of trust levels"
|
||||
);
|
||||
});
|
||||
|
||||
test("create categories", function (assert) {
|
||||
const store = createStore();
|
||||
const site = store.createRecord("site", {
|
||||
categories: [
|
||||
{ id: 1234, name: "Test" },
|
||||
{ id: 3456, name: "Test Subcategory", parent_category_id: 1234 },
|
||||
{ id: 3458, name: "Invalid Subcategory", parent_category_id: 6666 },
|
||||
],
|
||||
module("Unit | Model | site", function () {
|
||||
test("create", function (assert) {
|
||||
assert.ok(Site.create(), "it can create with no parameters");
|
||||
});
|
||||
|
||||
const categories = site.get("categories");
|
||||
site.get("sortedCategories");
|
||||
test("instance", function (assert) {
|
||||
const site = Site.current();
|
||||
|
||||
assert.present(categories, "The categories are present");
|
||||
assert.equal(categories.length, 3, "it loaded all three categories");
|
||||
assert.present(site, "We have a current site singleton");
|
||||
assert.present(
|
||||
site.get("categories"),
|
||||
"The instance has a list of categories"
|
||||
);
|
||||
assert.present(
|
||||
site.get("flagTypes"),
|
||||
"The instance has a list of flag types"
|
||||
);
|
||||
assert.present(
|
||||
site.get("trustLevels"),
|
||||
"The instance has a list of trust levels"
|
||||
);
|
||||
});
|
||||
|
||||
const parent = categories.findBy("id", 1234);
|
||||
assert.present(parent, "it loaded the parent category");
|
||||
assert.blank(parent.get("parentCategory"), "it has no parent category");
|
||||
test("create categories", function (assert) {
|
||||
const store = createStore();
|
||||
const site = store.createRecord("site", {
|
||||
categories: [
|
||||
{ id: 1234, name: "Test" },
|
||||
{ id: 3456, name: "Test Subcategory", parent_category_id: 1234 },
|
||||
{ id: 3458, name: "Invalid Subcategory", parent_category_id: 6666 },
|
||||
],
|
||||
});
|
||||
|
||||
assert.equal(parent.get("subcategories").length, 1);
|
||||
const categories = site.get("categories");
|
||||
site.get("sortedCategories");
|
||||
|
||||
const subcategory = categories.findBy("id", 3456);
|
||||
assert.present(subcategory, "it loaded the subcategory");
|
||||
assert.equal(
|
||||
subcategory.get("parentCategory"),
|
||||
parent,
|
||||
"it has associated the child with the parent"
|
||||
);
|
||||
assert.present(categories, "The categories are present");
|
||||
assert.equal(categories.length, 3, "it loaded all three categories");
|
||||
|
||||
// remove invalid category and child
|
||||
categories.removeObject(categories[2]);
|
||||
categories.removeObject(categories[1]);
|
||||
const parent = categories.findBy("id", 1234);
|
||||
assert.present(parent, "it loaded the parent category");
|
||||
assert.blank(parent.get("parentCategory"), "it has no parent category");
|
||||
|
||||
assert.equal(
|
||||
categories.length,
|
||||
site.get("categoriesByCount").length,
|
||||
"categories by count should change on removal"
|
||||
);
|
||||
assert.equal(
|
||||
categories.length,
|
||||
site.get("sortedCategories").length,
|
||||
"sorted categories should change on removal"
|
||||
);
|
||||
assert.equal(parent.get("subcategories").length, 1);
|
||||
|
||||
const subcategory = categories.findBy("id", 3456);
|
||||
assert.present(subcategory, "it loaded the subcategory");
|
||||
assert.equal(
|
||||
subcategory.get("parentCategory"),
|
||||
parent,
|
||||
"it has associated the child with the parent"
|
||||
);
|
||||
|
||||
// remove invalid category and child
|
||||
categories.removeObject(categories[2]);
|
||||
categories.removeObject(categories[1]);
|
||||
|
||||
assert.equal(
|
||||
categories.length,
|
||||
site.get("categoriesByCount").length,
|
||||
"categories by count should change on removal"
|
||||
);
|
||||
assert.equal(
|
||||
categories.length,
|
||||
site.get("sortedCategories").length,
|
||||
"sorted categories should change on removal"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { test, module } from "qunit";
|
||||
import StaffActionLog from "admin/models/staff-action-log";
|
||||
|
||||
module("StaffActionLog");
|
||||
|
||||
test("create", function (assert) {
|
||||
assert.ok(StaffActionLog.create(), "it can be created without arguments");
|
||||
module("Unit | Model | staff-action-log", function () {
|
||||
test("create", function (assert) {
|
||||
assert.ok(StaffActionLog.create(), "it can be created without arguments");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,188 +0,0 @@
|
||||
import { test, module } from "qunit";
|
||||
module("service:store");
|
||||
|
||||
import createStore from "discourse/tests/helpers/create-store";
|
||||
|
||||
test("createRecord", function (assert) {
|
||||
const store = createStore();
|
||||
const widget = store.createRecord("widget", { id: 111, name: "hello" });
|
||||
|
||||
assert.ok(!widget.get("isNew"), "it is not a new record");
|
||||
assert.equal(widget.get("name"), "hello");
|
||||
assert.equal(widget.get("id"), 111);
|
||||
});
|
||||
|
||||
test("createRecord without an `id`", function (assert) {
|
||||
const store = createStore();
|
||||
const widget = store.createRecord("widget", { name: "hello" });
|
||||
|
||||
assert.ok(widget.get("isNew"), "it is a new record");
|
||||
assert.ok(!widget.get("id"), "there is no id");
|
||||
});
|
||||
|
||||
test("createRecord doesn't modify the input `id` field", function (assert) {
|
||||
const store = createStore();
|
||||
const widget = store.createRecord("widget", { id: 1, name: "hello" });
|
||||
|
||||
const obj = { id: 1, name: "something" };
|
||||
|
||||
const other = store.createRecord("widget", obj);
|
||||
assert.equal(widget, other, "returns the same record");
|
||||
assert.equal(widget.name, "something", "it updates the properties");
|
||||
assert.equal(obj.id, 1, "it does not remove the id from the input");
|
||||
});
|
||||
|
||||
test("createRecord without attributes", function (assert) {
|
||||
const store = createStore();
|
||||
const widget = store.createRecord("widget");
|
||||
|
||||
assert.ok(!widget.get("id"), "there is no id");
|
||||
assert.ok(widget.get("isNew"), "it is a new record");
|
||||
});
|
||||
|
||||
test("createRecord with a record as attributes returns that record from the map", function (assert) {
|
||||
const store = createStore();
|
||||
const widget = store.createRecord("widget", { id: 33 });
|
||||
const secondWidget = store.createRecord("widget", { id: 33 });
|
||||
|
||||
assert.equal(widget, secondWidget, "they should be the same");
|
||||
});
|
||||
|
||||
test("find", async function (assert) {
|
||||
const store = createStore();
|
||||
|
||||
const widget = await store.find("widget", 123);
|
||||
assert.equal(widget.get("name"), "Trout Lure");
|
||||
assert.equal(widget.get("id"), 123);
|
||||
assert.ok(!widget.get("isNew"), "found records are not new");
|
||||
assert.equal(widget.get("extras.hello"), "world", "extra attributes are set");
|
||||
|
||||
// A second find by id returns the same object
|
||||
const widget2 = await store.find("widget", 123);
|
||||
assert.equal(widget, widget2);
|
||||
assert.equal(widget.get("extras.hello"), "world", "extra attributes are set");
|
||||
});
|
||||
|
||||
test("find with object id", async function (assert) {
|
||||
const store = createStore();
|
||||
const widget = await store.find("widget", { id: 123 });
|
||||
assert.equal(widget.get("firstObject.name"), "Trout Lure");
|
||||
});
|
||||
|
||||
test("find with query param", async function (assert) {
|
||||
const store = createStore();
|
||||
const widget = await store.find("widget", { name: "Trout Lure" });
|
||||
assert.equal(widget.get("firstObject.id"), 123);
|
||||
});
|
||||
|
||||
test("findStale with no stale results", async function (assert) {
|
||||
const store = createStore();
|
||||
const stale = store.findStale("widget", { name: "Trout Lure" });
|
||||
|
||||
assert.ok(!stale.hasResults, "there are no stale results");
|
||||
assert.ok(!stale.results, "results are present");
|
||||
const widget = await stale.refresh();
|
||||
assert.equal(
|
||||
widget.get("firstObject.id"),
|
||||
123,
|
||||
"a `refresh()` method provides results for stale"
|
||||
);
|
||||
});
|
||||
|
||||
test("update", async function (assert) {
|
||||
const store = createStore();
|
||||
const result = await store.update("widget", 123, { name: "hello" });
|
||||
assert.ok(result);
|
||||
});
|
||||
|
||||
test("update with a multi world name", async function (assert) {
|
||||
const store = createStore();
|
||||
const result = await store.update("cool-thing", 123, { name: "hello" });
|
||||
assert.ok(result);
|
||||
assert.equal(result.payload.name, "hello");
|
||||
});
|
||||
|
||||
test("findAll", async function (assert) {
|
||||
const store = createStore();
|
||||
const result = await store.findAll("widget");
|
||||
assert.equal(result.get("length"), 2);
|
||||
|
||||
const widget = result.findBy("id", 124);
|
||||
assert.ok(!widget.get("isNew"), "found records are not new");
|
||||
assert.equal(widget.get("name"), "Evil Repellant");
|
||||
});
|
||||
|
||||
test("destroyRecord", async function (assert) {
|
||||
const store = createStore();
|
||||
const widget = await store.find("widget", 123);
|
||||
|
||||
assert.ok(await store.destroyRecord("widget", widget));
|
||||
});
|
||||
|
||||
test("destroyRecord when new", async function (assert) {
|
||||
const store = createStore();
|
||||
const widget = store.createRecord("widget", { name: "hello" });
|
||||
|
||||
assert.ok(await store.destroyRecord("widget", widget));
|
||||
});
|
||||
|
||||
test("find embedded", async function (assert) {
|
||||
const store = createStore();
|
||||
const fruit = await store.find("fruit", 1);
|
||||
assert.ok(fruit.get("farmer"), "it has the embedded object");
|
||||
|
||||
const fruitCols = fruit.get("colors");
|
||||
assert.equal(fruitCols.length, 2);
|
||||
assert.equal(fruitCols[0].get("id"), 1);
|
||||
assert.equal(fruitCols[1].get("id"), 2);
|
||||
|
||||
assert.ok(fruit.get("category"), "categories are found automatically");
|
||||
});
|
||||
|
||||
test("embedded records can be cleared", async function (assert) {
|
||||
const store = createStore();
|
||||
let fruit = await store.find("fruit", 4);
|
||||
fruit.set("farmer", { dummy: "object" });
|
||||
|
||||
fruit = await store.find("fruit", 4);
|
||||
assert.ok(!fruit.get("farmer"));
|
||||
});
|
||||
|
||||
test("meta types", async function (assert) {
|
||||
const store = createStore();
|
||||
const barn = await store.find("barn", 1);
|
||||
assert.equal(
|
||||
barn.get("owner.name"),
|
||||
"Old MacDonald",
|
||||
"it has the embedded farmer"
|
||||
);
|
||||
});
|
||||
|
||||
test("findAll embedded", async function (assert) {
|
||||
const store = createStore();
|
||||
const fruits = await store.findAll("fruit");
|
||||
assert.equal(fruits.objectAt(0).get("farmer.name"), "Old MacDonald");
|
||||
assert.equal(
|
||||
fruits.objectAt(0).get("farmer"),
|
||||
fruits.objectAt(1).get("farmer"),
|
||||
"points at the same object"
|
||||
);
|
||||
assert.equal(
|
||||
fruits.get("extras.hello"),
|
||||
"world",
|
||||
"it can supply extra information"
|
||||
);
|
||||
|
||||
const fruitCols = fruits.objectAt(0).get("colors");
|
||||
assert.equal(fruitCols.length, 2);
|
||||
assert.equal(fruitCols[0].get("id"), 1);
|
||||
assert.equal(fruitCols[1].get("id"), 2);
|
||||
|
||||
assert.equal(fruits.objectAt(2).get("farmer.name"), "Luke Skywalker");
|
||||
});
|
||||
|
||||
test("custom primaryKey", async function (assert) {
|
||||
const store = createStore();
|
||||
const cats = await store.findAll("cat");
|
||||
assert.equal(cats.objectAt(0).name, "souna");
|
||||
});
|
||||
@@ -1,32 +1,31 @@
|
||||
import { test, module } from "qunit";
|
||||
import User from "discourse/models/user";
|
||||
|
||||
module("model:topic-details");
|
||||
|
||||
import Topic from "discourse/models/topic";
|
||||
|
||||
var buildDetails = function (id) {
|
||||
var topic = Topic.create({ id: id });
|
||||
function buildDetails(id) {
|
||||
const topic = Topic.create({ id: id });
|
||||
return topic.get("details");
|
||||
};
|
||||
}
|
||||
|
||||
test("defaults", function (assert) {
|
||||
var details = buildDetails(1234);
|
||||
assert.present(details, "the details are present by default");
|
||||
assert.ok(!details.get("loaded"), "details are not loaded by default");
|
||||
});
|
||||
|
||||
test("updateFromJson", function (assert) {
|
||||
var details = buildDetails(1234);
|
||||
|
||||
details.updateFromJson({
|
||||
allowed_users: [{ username: "eviltrout" }],
|
||||
module("Unit | Model | topic-details", function () {
|
||||
test("defaults", function (assert) {
|
||||
var details = buildDetails(1234);
|
||||
assert.present(details, "the details are present by default");
|
||||
assert.ok(!details.get("loaded"), "details are not loaded by default");
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
details.get("allowed_users.length"),
|
||||
1,
|
||||
"it loaded the allowed users"
|
||||
);
|
||||
assert.containsInstance(details.get("allowed_users"), User);
|
||||
test("updateFromJson", function (assert) {
|
||||
var details = buildDetails(1234);
|
||||
|
||||
details.updateFromJson({
|
||||
allowed_users: [{ username: "eviltrout" }],
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
details.get("allowed_users.length"),
|
||||
1,
|
||||
"it loaded the allowed users"
|
||||
);
|
||||
assert.containsInstance(details.get("allowed_users"), User);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,161 +6,161 @@ import Topic from "discourse/models/topic";
|
||||
import User from "discourse/models/user";
|
||||
import { discourseModule } from "discourse/tests/helpers/qunit-helpers";
|
||||
|
||||
discourseModule("model:topic");
|
||||
discourseModule("Unit | Model | topic", function () {
|
||||
test("defaults", function (assert) {
|
||||
const topic = Topic.create({ id: 1234 });
|
||||
|
||||
test("defaults", function (assert) {
|
||||
const topic = Topic.create({ id: 1234 });
|
||||
|
||||
assert.blank(topic.get("deleted_at"), "deleted_at defaults to blank");
|
||||
assert.blank(topic.get("deleted_by"), "deleted_by defaults to blank");
|
||||
});
|
||||
|
||||
test("visited", function (assert) {
|
||||
const topic = Topic.create({
|
||||
highest_post_number: 2,
|
||||
last_read_post_number: 1,
|
||||
assert.blank(topic.get("deleted_at"), "deleted_at defaults to blank");
|
||||
assert.blank(topic.get("deleted_by"), "deleted_by defaults to blank");
|
||||
});
|
||||
|
||||
assert.not(
|
||||
topic.get("visited"),
|
||||
"not visited unless we've read all the posts"
|
||||
);
|
||||
test("visited", function (assert) {
|
||||
const topic = Topic.create({
|
||||
highest_post_number: 2,
|
||||
last_read_post_number: 1,
|
||||
});
|
||||
|
||||
topic.set("last_read_post_number", 2);
|
||||
assert.ok(topic.get("visited"), "is visited once we've read all the posts");
|
||||
assert.not(
|
||||
topic.get("visited"),
|
||||
"not visited unless we've read all the posts"
|
||||
);
|
||||
|
||||
topic.set("last_read_post_number", 3);
|
||||
assert.ok(
|
||||
topic.get("visited"),
|
||||
"is visited if we've read all the posts and some are deleted at the end"
|
||||
);
|
||||
});
|
||||
topic.set("last_read_post_number", 2);
|
||||
assert.ok(topic.get("visited"), "is visited once we've read all the posts");
|
||||
|
||||
test("lastUnreadUrl", function (assert) {
|
||||
const category = EmberObject.create({
|
||||
navigate_to_first_post_after_read: true,
|
||||
topic.set("last_read_post_number", 3);
|
||||
assert.ok(
|
||||
topic.get("visited"),
|
||||
"is visited if we've read all the posts and some are deleted at the end"
|
||||
);
|
||||
});
|
||||
|
||||
const topic = Topic.create({
|
||||
id: 101,
|
||||
highest_post_number: 10,
|
||||
last_read_post_number: 10,
|
||||
slug: "hello",
|
||||
test("lastUnreadUrl", function (assert) {
|
||||
const category = EmberObject.create({
|
||||
navigate_to_first_post_after_read: true,
|
||||
});
|
||||
|
||||
const topic = Topic.create({
|
||||
id: 101,
|
||||
highest_post_number: 10,
|
||||
last_read_post_number: 10,
|
||||
slug: "hello",
|
||||
});
|
||||
|
||||
topic.set("category", category);
|
||||
|
||||
assert.equal(topic.get("lastUnreadUrl"), "/t/hello/101/1");
|
||||
});
|
||||
|
||||
topic.set("category", category);
|
||||
test("has details", function (assert) {
|
||||
const topic = Topic.create({ id: 1234 });
|
||||
const topicDetails = topic.get("details");
|
||||
|
||||
assert.equal(topic.get("lastUnreadUrl"), "/t/hello/101/1");
|
||||
});
|
||||
|
||||
test("has details", function (assert) {
|
||||
const topic = Topic.create({ id: 1234 });
|
||||
const topicDetails = topic.get("details");
|
||||
|
||||
assert.present(topicDetails, "a topic has topicDetails after we create it");
|
||||
assert.equal(
|
||||
topicDetails.get("topic"),
|
||||
topic,
|
||||
"the topicDetails has a reference back to the topic"
|
||||
);
|
||||
});
|
||||
|
||||
test("has a postStream", function (assert) {
|
||||
const topic = Topic.create({ id: 1234 });
|
||||
const postStream = topic.get("postStream");
|
||||
|
||||
assert.present(postStream, "a topic has a postStream after we create it");
|
||||
assert.equal(
|
||||
postStream.get("topic"),
|
||||
topic,
|
||||
"the postStream has a reference back to the topic"
|
||||
);
|
||||
});
|
||||
|
||||
test("has suggestedTopics", function (assert) {
|
||||
const topic = Topic.create({ suggested_topics: [{ id: 1 }, { id: 2 }] });
|
||||
const suggestedTopics = topic.get("suggestedTopics");
|
||||
|
||||
assert.equal(suggestedTopics.length, 2, "it loaded the suggested_topics");
|
||||
assert.containsInstance(suggestedTopics, Topic);
|
||||
});
|
||||
|
||||
test("category relationship", function (assert) {
|
||||
// It finds the category by id
|
||||
const category = Category.list()[0];
|
||||
const topic = Topic.create({ id: 1111, category_id: category.get("id") });
|
||||
|
||||
assert.equal(topic.get("category"), category);
|
||||
});
|
||||
|
||||
test("updateFromJson", function (assert) {
|
||||
const topic = Topic.create({ id: 1234 });
|
||||
const category = Category.list()[0];
|
||||
|
||||
topic.updateFromJson({
|
||||
post_stream: [1, 2, 3],
|
||||
details: { hello: "world" },
|
||||
cool: "property",
|
||||
category_id: category.get("id"),
|
||||
assert.present(topicDetails, "a topic has topicDetails after we create it");
|
||||
assert.equal(
|
||||
topicDetails.get("topic"),
|
||||
topic,
|
||||
"the topicDetails has a reference back to the topic"
|
||||
);
|
||||
});
|
||||
|
||||
assert.blank(topic.get("post_stream"), "it does not update post_stream");
|
||||
assert.equal(topic.get("details.hello"), "world", "it updates the details");
|
||||
assert.equal(topic.get("cool"), "property", "it updates other properties");
|
||||
assert.equal(topic.get("category"), category);
|
||||
});
|
||||
test("has a postStream", function (assert) {
|
||||
const topic = Topic.create({ id: 1234 });
|
||||
const postStream = topic.get("postStream");
|
||||
|
||||
test("recover", function (assert) {
|
||||
const user = User.create({ username: "eviltrout" });
|
||||
const topic = Topic.create({
|
||||
id: 1234,
|
||||
deleted_at: new Date(),
|
||||
deleted_by: user,
|
||||
assert.present(postStream, "a topic has a postStream after we create it");
|
||||
assert.equal(
|
||||
postStream.get("topic"),
|
||||
topic,
|
||||
"the postStream has a reference back to the topic"
|
||||
);
|
||||
});
|
||||
|
||||
topic.recover();
|
||||
assert.blank(topic.get("deleted_at"), "it clears deleted_at");
|
||||
assert.blank(topic.get("deleted_by"), "it clears deleted_by");
|
||||
});
|
||||
test("has suggestedTopics", function (assert) {
|
||||
const topic = Topic.create({ suggested_topics: [{ id: 1 }, { id: 2 }] });
|
||||
const suggestedTopics = topic.get("suggestedTopics");
|
||||
|
||||
test("fancyTitle", function (assert) {
|
||||
const topic = Topic.create({
|
||||
fancy_title: ":smile: with all :) the emojis :pear::peach:",
|
||||
assert.equal(suggestedTopics.length, 2, "it loaded the suggested_topics");
|
||||
assert.containsInstance(suggestedTopics, Topic);
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
topic.get("fancyTitle"),
|
||||
`<img width=\"20\" height=\"20\" src='/images/emoji/emoji_one/smile.png?v=${v}' title='smile' alt='smile' class='emoji'> with all <img width=\"20\" height=\"20\" src='/images/emoji/emoji_one/slight_smile.png?v=${v}' title='slight_smile' alt='slight_smile' class='emoji'> the emojis <img width=\"20\" height=\"20\" src='/images/emoji/emoji_one/pear.png?v=${v}' title='pear' alt='pear' class='emoji'><img width=\"20\" height=\"20\" src='/images/emoji/emoji_one/peach.png?v=${v}' title='peach' alt='peach' class='emoji'>`,
|
||||
"supports emojis"
|
||||
);
|
||||
});
|
||||
test("category relationship", function (assert) {
|
||||
// It finds the category by id
|
||||
const category = Category.list()[0];
|
||||
const topic = Topic.create({ id: 1111, category_id: category.get("id") });
|
||||
|
||||
test("fancyTitle direction", function (assert) {
|
||||
const rtlTopic = Topic.create({ fancy_title: "هذا اختبار" });
|
||||
const ltrTopic = Topic.create({ fancy_title: "This is a test" });
|
||||
|
||||
this.siteSettings.support_mixed_text_direction = true;
|
||||
assert.equal(
|
||||
rtlTopic.get("fancyTitle"),
|
||||
`<span dir="rtl">هذا اختبار</span>`,
|
||||
"sets the dir-span to rtl"
|
||||
);
|
||||
assert.equal(
|
||||
ltrTopic.get("fancyTitle"),
|
||||
`<span dir="ltr">This is a test</span>`,
|
||||
"sets the dir-span to ltr"
|
||||
);
|
||||
});
|
||||
|
||||
test("excerpt", function (assert) {
|
||||
const topic = Topic.create({
|
||||
excerpt: "This is a test topic :smile:",
|
||||
pinned: true,
|
||||
assert.equal(topic.get("category"), category);
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
topic.get("escapedExcerpt"),
|
||||
`This is a test topic <img width=\"20\" height=\"20\" src='/images/emoji/emoji_one/smile.png?v=${v}' title='smile' alt='smile' class='emoji'>`,
|
||||
"supports emojis"
|
||||
);
|
||||
test("updateFromJson", function (assert) {
|
||||
const topic = Topic.create({ id: 1234 });
|
||||
const category = Category.list()[0];
|
||||
|
||||
topic.updateFromJson({
|
||||
post_stream: [1, 2, 3],
|
||||
details: { hello: "world" },
|
||||
cool: "property",
|
||||
category_id: category.get("id"),
|
||||
});
|
||||
|
||||
assert.blank(topic.get("post_stream"), "it does not update post_stream");
|
||||
assert.equal(topic.get("details.hello"), "world", "it updates the details");
|
||||
assert.equal(topic.get("cool"), "property", "it updates other properties");
|
||||
assert.equal(topic.get("category"), category);
|
||||
});
|
||||
|
||||
test("recover", function (assert) {
|
||||
const user = User.create({ username: "eviltrout" });
|
||||
const topic = Topic.create({
|
||||
id: 1234,
|
||||
deleted_at: new Date(),
|
||||
deleted_by: user,
|
||||
});
|
||||
|
||||
topic.recover();
|
||||
assert.blank(topic.get("deleted_at"), "it clears deleted_at");
|
||||
assert.blank(topic.get("deleted_by"), "it clears deleted_by");
|
||||
});
|
||||
|
||||
test("fancyTitle", function (assert) {
|
||||
const topic = Topic.create({
|
||||
fancy_title: ":smile: with all :) the emojis :pear::peach:",
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
topic.get("fancyTitle"),
|
||||
`<img width=\"20\" height=\"20\" src='/images/emoji/emoji_one/smile.png?v=${v}' title='smile' alt='smile' class='emoji'> with all <img width=\"20\" height=\"20\" src='/images/emoji/emoji_one/slight_smile.png?v=${v}' title='slight_smile' alt='slight_smile' class='emoji'> the emojis <img width=\"20\" height=\"20\" src='/images/emoji/emoji_one/pear.png?v=${v}' title='pear' alt='pear' class='emoji'><img width=\"20\" height=\"20\" src='/images/emoji/emoji_one/peach.png?v=${v}' title='peach' alt='peach' class='emoji'>`,
|
||||
"supports emojis"
|
||||
);
|
||||
});
|
||||
|
||||
test("fancyTitle direction", function (assert) {
|
||||
const rtlTopic = Topic.create({ fancy_title: "هذا اختبار" });
|
||||
const ltrTopic = Topic.create({ fancy_title: "This is a test" });
|
||||
|
||||
this.siteSettings.support_mixed_text_direction = true;
|
||||
assert.equal(
|
||||
rtlTopic.get("fancyTitle"),
|
||||
`<span dir="rtl">هذا اختبار</span>`,
|
||||
"sets the dir-span to rtl"
|
||||
);
|
||||
assert.equal(
|
||||
ltrTopic.get("fancyTitle"),
|
||||
`<span dir="ltr">This is a test</span>`,
|
||||
"sets the dir-span to ltr"
|
||||
);
|
||||
});
|
||||
|
||||
test("excerpt", function (assert) {
|
||||
const topic = Topic.create({
|
||||
excerpt: "This is a test topic :smile:",
|
||||
pinned: true,
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
topic.get("escapedExcerpt"),
|
||||
`This is a test topic <img width=\"20\" height=\"20\" src='/images/emoji/emoji_one/smile.png?v=${v}' title='smile' alt='smile' class='emoji'>`,
|
||||
"supports emojis"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,343 +6,343 @@ import { NotificationLevels } from "discourse/lib/notification-levels";
|
||||
import User from "discourse/models/user";
|
||||
import sinon from "sinon";
|
||||
|
||||
module("model:topic-tracking-state", {
|
||||
beforeEach() {
|
||||
module("Unit | Model | topic-tracking-state", function (hooks) {
|
||||
hooks.beforeEach(function () {
|
||||
this.clock = sinon.useFakeTimers(new Date(2012, 11, 31, 12, 0).getTime());
|
||||
},
|
||||
});
|
||||
|
||||
afterEach() {
|
||||
hooks.afterEach(function () {
|
||||
this.clock.restore();
|
||||
},
|
||||
});
|
||||
|
||||
test("tag counts", function (assert) {
|
||||
const state = TopicTrackingState.create();
|
||||
|
||||
state.loadStates([
|
||||
{
|
||||
topic_id: 1,
|
||||
last_read_post_number: null,
|
||||
tags: ["foo", "new"],
|
||||
},
|
||||
{
|
||||
topic_id: 2,
|
||||
last_read_post_number: null,
|
||||
tags: ["new"],
|
||||
},
|
||||
{
|
||||
topic_id: 3,
|
||||
last_read_post_number: null,
|
||||
tags: ["random"],
|
||||
},
|
||||
{
|
||||
topic_id: 4,
|
||||
last_read_post_number: 1,
|
||||
highest_post_number: 7,
|
||||
tags: ["unread"],
|
||||
notification_level: NotificationLevels.TRACKING,
|
||||
},
|
||||
{
|
||||
topic_id: 5,
|
||||
last_read_post_number: 1,
|
||||
highest_post_number: 7,
|
||||
tags: ["bar", "unread"],
|
||||
notification_level: NotificationLevels.TRACKING,
|
||||
},
|
||||
{
|
||||
topic_id: 6,
|
||||
last_read_post_number: 1,
|
||||
highest_post_number: 7,
|
||||
tags: null,
|
||||
notification_level: NotificationLevels.TRACKING,
|
||||
},
|
||||
]);
|
||||
|
||||
const states = state.countTags(["new", "unread"]);
|
||||
|
||||
assert.equal(states["new"].newCount, 2, "new counts");
|
||||
assert.equal(states["new"].unreadCount, 0, "new counts");
|
||||
assert.equal(states["unread"].unreadCount, 2, "unread counts");
|
||||
assert.equal(states["unread"].newCount, 0, "unread counts");
|
||||
});
|
||||
|
||||
test("forEachTracked", function (assert) {
|
||||
const state = TopicTrackingState.create();
|
||||
|
||||
state.loadStates([
|
||||
{
|
||||
topic_id: 1,
|
||||
last_read_post_number: null,
|
||||
tags: ["foo", "new"],
|
||||
},
|
||||
{
|
||||
topic_id: 2,
|
||||
last_read_post_number: null,
|
||||
tags: ["new"],
|
||||
},
|
||||
{
|
||||
topic_id: 3,
|
||||
last_read_post_number: null,
|
||||
tags: ["random"],
|
||||
},
|
||||
{
|
||||
topic_id: 4,
|
||||
last_read_post_number: 1,
|
||||
highest_post_number: 7,
|
||||
category_id: 7,
|
||||
tags: ["unread"],
|
||||
notification_level: NotificationLevels.TRACKING,
|
||||
},
|
||||
{
|
||||
topic_id: 5,
|
||||
last_read_post_number: 1,
|
||||
highest_post_number: 7,
|
||||
tags: ["bar", "unread"],
|
||||
category_id: 7,
|
||||
notification_level: NotificationLevels.TRACKING,
|
||||
},
|
||||
{
|
||||
topic_id: 6,
|
||||
last_read_post_number: 1,
|
||||
highest_post_number: 7,
|
||||
tags: null,
|
||||
notification_level: NotificationLevels.TRACKING,
|
||||
},
|
||||
]);
|
||||
|
||||
let randomUnread = 0,
|
||||
randomNew = 0,
|
||||
sevenUnread = 0,
|
||||
sevenNew = 0;
|
||||
|
||||
state.forEachTracked((topic, isNew, isUnread) => {
|
||||
if (topic.category_id === 7) {
|
||||
if (isNew) {
|
||||
sevenNew += 1;
|
||||
}
|
||||
if (isUnread) {
|
||||
sevenUnread += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (topic.tags && topic.tags.indexOf("random") > -1) {
|
||||
if (isNew) {
|
||||
randomNew += 1;
|
||||
}
|
||||
if (isUnread) {
|
||||
randomUnread += 1;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
assert.equal(randomNew, 1, "random new");
|
||||
assert.equal(randomUnread, 0, "random unread");
|
||||
assert.equal(sevenNew, 0, "seven unread");
|
||||
assert.equal(sevenUnread, 2, "seven unread");
|
||||
});
|
||||
test("tag counts", function (assert) {
|
||||
const state = TopicTrackingState.create();
|
||||
|
||||
test("sync", function (assert) {
|
||||
const state = TopicTrackingState.create();
|
||||
state.states["t111"] = { last_read_post_number: null };
|
||||
|
||||
state.updateSeen(111, 7);
|
||||
const list = {
|
||||
topics: [
|
||||
state.loadStates([
|
||||
{
|
||||
highest_post_number: null,
|
||||
id: 111,
|
||||
unread: 10,
|
||||
new_posts: 10,
|
||||
topic_id: 1,
|
||||
last_read_post_number: null,
|
||||
tags: ["foo", "new"],
|
||||
},
|
||||
],
|
||||
};
|
||||
{
|
||||
topic_id: 2,
|
||||
last_read_post_number: null,
|
||||
tags: ["new"],
|
||||
},
|
||||
{
|
||||
topic_id: 3,
|
||||
last_read_post_number: null,
|
||||
tags: ["random"],
|
||||
},
|
||||
{
|
||||
topic_id: 4,
|
||||
last_read_post_number: 1,
|
||||
highest_post_number: 7,
|
||||
tags: ["unread"],
|
||||
notification_level: NotificationLevels.TRACKING,
|
||||
},
|
||||
{
|
||||
topic_id: 5,
|
||||
last_read_post_number: 1,
|
||||
highest_post_number: 7,
|
||||
tags: ["bar", "unread"],
|
||||
notification_level: NotificationLevels.TRACKING,
|
||||
},
|
||||
{
|
||||
topic_id: 6,
|
||||
last_read_post_number: 1,
|
||||
highest_post_number: 7,
|
||||
tags: null,
|
||||
notification_level: NotificationLevels.TRACKING,
|
||||
},
|
||||
]);
|
||||
|
||||
state.sync(list, "new");
|
||||
assert.equal(
|
||||
list.topics.length,
|
||||
0,
|
||||
"expect new topic to be removed as it was seen"
|
||||
);
|
||||
});
|
||||
const states = state.countTags(["new", "unread"]);
|
||||
|
||||
test("subscribe to category", function (assert) {
|
||||
const store = createStore();
|
||||
const darth = store.createRecord("category", { id: 1, slug: "darth" }),
|
||||
luke = store.createRecord("category", {
|
||||
assert.equal(states["new"].newCount, 2, "new counts");
|
||||
assert.equal(states["new"].unreadCount, 0, "new counts");
|
||||
assert.equal(states["unread"].unreadCount, 2, "unread counts");
|
||||
assert.equal(states["unread"].newCount, 0, "unread counts");
|
||||
});
|
||||
|
||||
test("forEachTracked", function (assert) {
|
||||
const state = TopicTrackingState.create();
|
||||
|
||||
state.loadStates([
|
||||
{
|
||||
topic_id: 1,
|
||||
last_read_post_number: null,
|
||||
tags: ["foo", "new"],
|
||||
},
|
||||
{
|
||||
topic_id: 2,
|
||||
last_read_post_number: null,
|
||||
tags: ["new"],
|
||||
},
|
||||
{
|
||||
topic_id: 3,
|
||||
last_read_post_number: null,
|
||||
tags: ["random"],
|
||||
},
|
||||
{
|
||||
topic_id: 4,
|
||||
last_read_post_number: 1,
|
||||
highest_post_number: 7,
|
||||
category_id: 7,
|
||||
tags: ["unread"],
|
||||
notification_level: NotificationLevels.TRACKING,
|
||||
},
|
||||
{
|
||||
topic_id: 5,
|
||||
last_read_post_number: 1,
|
||||
highest_post_number: 7,
|
||||
tags: ["bar", "unread"],
|
||||
category_id: 7,
|
||||
notification_level: NotificationLevels.TRACKING,
|
||||
},
|
||||
{
|
||||
topic_id: 6,
|
||||
last_read_post_number: 1,
|
||||
highest_post_number: 7,
|
||||
tags: null,
|
||||
notification_level: NotificationLevels.TRACKING,
|
||||
},
|
||||
]);
|
||||
|
||||
let randomUnread = 0,
|
||||
randomNew = 0,
|
||||
sevenUnread = 0,
|
||||
sevenNew = 0;
|
||||
|
||||
state.forEachTracked((topic, isNew, isUnread) => {
|
||||
if (topic.category_id === 7) {
|
||||
if (isNew) {
|
||||
sevenNew += 1;
|
||||
}
|
||||
if (isUnread) {
|
||||
sevenUnread += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (topic.tags && topic.tags.indexOf("random") > -1) {
|
||||
if (isNew) {
|
||||
randomNew += 1;
|
||||
}
|
||||
if (isUnread) {
|
||||
randomUnread += 1;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
assert.equal(randomNew, 1, "random new");
|
||||
assert.equal(randomUnread, 0, "random unread");
|
||||
assert.equal(sevenNew, 0, "seven unread");
|
||||
assert.equal(sevenUnread, 2, "seven unread");
|
||||
});
|
||||
|
||||
test("sync", function (assert) {
|
||||
const state = TopicTrackingState.create();
|
||||
state.states["t111"] = { last_read_post_number: null };
|
||||
|
||||
state.updateSeen(111, 7);
|
||||
const list = {
|
||||
topics: [
|
||||
{
|
||||
highest_post_number: null,
|
||||
id: 111,
|
||||
unread: 10,
|
||||
new_posts: 10,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
state.sync(list, "new");
|
||||
assert.equal(
|
||||
list.topics.length,
|
||||
0,
|
||||
"expect new topic to be removed as it was seen"
|
||||
);
|
||||
});
|
||||
|
||||
test("subscribe to category", function (assert) {
|
||||
const store = createStore();
|
||||
const darth = store.createRecord("category", { id: 1, slug: "darth" }),
|
||||
luke = store.createRecord("category", {
|
||||
id: 2,
|
||||
slug: "luke",
|
||||
parentCategory: darth,
|
||||
}),
|
||||
categoryList = [darth, luke];
|
||||
|
||||
sinon.stub(Category, "list").returns(categoryList);
|
||||
|
||||
const state = TopicTrackingState.create();
|
||||
|
||||
state.trackIncoming("c/darth/1/l/latest");
|
||||
|
||||
state.notify({
|
||||
message_type: "new_topic",
|
||||
topic_id: 1,
|
||||
payload: { category_id: 2, topic_id: 1 },
|
||||
});
|
||||
state.notify({
|
||||
message_type: "new_topic",
|
||||
topic_id: 2,
|
||||
payload: { category_id: 3, topic_id: 2 },
|
||||
});
|
||||
state.notify({
|
||||
message_type: "new_topic",
|
||||
topic_id: 3,
|
||||
payload: { category_id: 1, topic_id: 3 },
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
state.get("incomingCount"),
|
||||
2,
|
||||
"expect to properly track incoming for category"
|
||||
);
|
||||
|
||||
state.resetTracking();
|
||||
state.trackIncoming("c/darth/luke/2/l/latest");
|
||||
|
||||
state.notify({
|
||||
message_type: "new_topic",
|
||||
topic_id: 1,
|
||||
payload: { category_id: 2, topic_id: 1 },
|
||||
});
|
||||
state.notify({
|
||||
message_type: "new_topic",
|
||||
topic_id: 2,
|
||||
payload: { category_id: 3, topic_id: 2 },
|
||||
});
|
||||
state.notify({
|
||||
message_type: "new_topic",
|
||||
topic_id: 3,
|
||||
payload: { category_id: 1, topic_id: 3 },
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
state.get("incomingCount"),
|
||||
1,
|
||||
"expect to properly track incoming for subcategory"
|
||||
);
|
||||
});
|
||||
|
||||
test("getSubCategoryIds", function (assert) {
|
||||
const store = createStore();
|
||||
const foo = store.createRecord("category", { id: 1, slug: "foo" });
|
||||
const bar = store.createRecord("category", {
|
||||
id: 2,
|
||||
slug: "luke",
|
||||
parentCategory: darth,
|
||||
}),
|
||||
categoryList = [darth, luke];
|
||||
slug: "bar",
|
||||
parent_category_id: foo.id,
|
||||
});
|
||||
const baz = store.createRecord("category", {
|
||||
id: 3,
|
||||
slug: "baz",
|
||||
parent_category_id: bar.id,
|
||||
});
|
||||
sinon.stub(Category, "list").returns([foo, bar, baz]);
|
||||
|
||||
sinon.stub(Category, "list").returns(categoryList);
|
||||
|
||||
const state = TopicTrackingState.create();
|
||||
|
||||
state.trackIncoming("c/darth/1/l/latest");
|
||||
|
||||
state.notify({
|
||||
message_type: "new_topic",
|
||||
topic_id: 1,
|
||||
payload: { category_id: 2, topic_id: 1 },
|
||||
});
|
||||
state.notify({
|
||||
message_type: "new_topic",
|
||||
topic_id: 2,
|
||||
payload: { category_id: 3, topic_id: 2 },
|
||||
});
|
||||
state.notify({
|
||||
message_type: "new_topic",
|
||||
topic_id: 3,
|
||||
payload: { category_id: 1, topic_id: 3 },
|
||||
const state = TopicTrackingState.create();
|
||||
assert.deepEqual(Array.from(state.getSubCategoryIds(1)), [1, 2, 3]);
|
||||
assert.deepEqual(Array.from(state.getSubCategoryIds(2)), [2, 3]);
|
||||
assert.deepEqual(Array.from(state.getSubCategoryIds(3)), [3]);
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
state.get("incomingCount"),
|
||||
2,
|
||||
"expect to properly track incoming for category"
|
||||
);
|
||||
test("countNew", function (assert) {
|
||||
const store = createStore();
|
||||
const foo = store.createRecord("category", {
|
||||
id: 1,
|
||||
slug: "foo",
|
||||
});
|
||||
const bar = store.createRecord("category", {
|
||||
id: 2,
|
||||
slug: "bar",
|
||||
parent_category_id: foo.id,
|
||||
});
|
||||
const baz = store.createRecord("category", {
|
||||
id: 3,
|
||||
slug: "baz",
|
||||
parent_category_id: bar.id,
|
||||
});
|
||||
const qux = store.createRecord("category", {
|
||||
id: 4,
|
||||
slug: "qux",
|
||||
});
|
||||
sinon.stub(Category, "list").returns([foo, bar, baz, qux]);
|
||||
|
||||
state.resetTracking();
|
||||
state.trackIncoming("c/darth/luke/2/l/latest");
|
||||
let currentUser = User.create({
|
||||
username: "chuck",
|
||||
muted_category_ids: [4],
|
||||
});
|
||||
|
||||
state.notify({
|
||||
message_type: "new_topic",
|
||||
topic_id: 1,
|
||||
payload: { category_id: 2, topic_id: 1 },
|
||||
});
|
||||
state.notify({
|
||||
message_type: "new_topic",
|
||||
topic_id: 2,
|
||||
payload: { category_id: 3, topic_id: 2 },
|
||||
});
|
||||
state.notify({
|
||||
message_type: "new_topic",
|
||||
topic_id: 3,
|
||||
payload: { category_id: 1, topic_id: 3 },
|
||||
const state = TopicTrackingState.create({ currentUser });
|
||||
|
||||
assert.equal(state.countNew(1), 0);
|
||||
assert.equal(state.countNew(2), 0);
|
||||
assert.equal(state.countNew(3), 0);
|
||||
|
||||
state.states["t112"] = {
|
||||
last_read_post_number: null,
|
||||
id: 112,
|
||||
notification_level: NotificationLevels.TRACKING,
|
||||
category_id: 2,
|
||||
};
|
||||
|
||||
assert.equal(state.countNew(1), 1);
|
||||
assert.equal(state.countNew(1, "missing-tag"), 0);
|
||||
assert.equal(state.countNew(2), 1);
|
||||
assert.equal(state.countNew(3), 0);
|
||||
|
||||
state.states["t113"] = {
|
||||
last_read_post_number: null,
|
||||
id: 113,
|
||||
notification_level: NotificationLevels.TRACKING,
|
||||
category_id: 3,
|
||||
tags: ["amazing"],
|
||||
};
|
||||
|
||||
assert.equal(state.countNew(1), 2);
|
||||
assert.equal(state.countNew(2), 2);
|
||||
assert.equal(state.countNew(3), 1);
|
||||
assert.equal(state.countNew(3, "amazing"), 1);
|
||||
assert.equal(state.countNew(3, "missing"), 0);
|
||||
|
||||
state.states["t111"] = {
|
||||
last_read_post_number: null,
|
||||
id: 111,
|
||||
notification_level: NotificationLevels.TRACKING,
|
||||
category_id: 1,
|
||||
};
|
||||
|
||||
assert.equal(state.countNew(1), 3);
|
||||
assert.equal(state.countNew(2), 2);
|
||||
assert.equal(state.countNew(3), 1);
|
||||
|
||||
state.states["t115"] = {
|
||||
last_read_post_number: null,
|
||||
id: 115,
|
||||
category_id: 4,
|
||||
};
|
||||
assert.equal(state.countNew(4), 0);
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
state.get("incomingCount"),
|
||||
1,
|
||||
"expect to properly track incoming for subcategory"
|
||||
);
|
||||
});
|
||||
|
||||
test("getSubCategoryIds", function (assert) {
|
||||
const store = createStore();
|
||||
const foo = store.createRecord("category", { id: 1, slug: "foo" });
|
||||
const bar = store.createRecord("category", {
|
||||
id: 2,
|
||||
slug: "bar",
|
||||
parent_category_id: foo.id,
|
||||
});
|
||||
const baz = store.createRecord("category", {
|
||||
id: 3,
|
||||
slug: "baz",
|
||||
parent_category_id: bar.id,
|
||||
});
|
||||
sinon.stub(Category, "list").returns([foo, bar, baz]);
|
||||
|
||||
const state = TopicTrackingState.create();
|
||||
assert.deepEqual(Array.from(state.getSubCategoryIds(1)), [1, 2, 3]);
|
||||
assert.deepEqual(Array.from(state.getSubCategoryIds(2)), [2, 3]);
|
||||
assert.deepEqual(Array.from(state.getSubCategoryIds(3)), [3]);
|
||||
});
|
||||
|
||||
test("countNew", function (assert) {
|
||||
const store = createStore();
|
||||
const foo = store.createRecord("category", {
|
||||
id: 1,
|
||||
slug: "foo",
|
||||
});
|
||||
const bar = store.createRecord("category", {
|
||||
id: 2,
|
||||
slug: "bar",
|
||||
parent_category_id: foo.id,
|
||||
});
|
||||
const baz = store.createRecord("category", {
|
||||
id: 3,
|
||||
slug: "baz",
|
||||
parent_category_id: bar.id,
|
||||
});
|
||||
const qux = store.createRecord("category", {
|
||||
id: 4,
|
||||
slug: "qux",
|
||||
});
|
||||
sinon.stub(Category, "list").returns([foo, bar, baz, qux]);
|
||||
|
||||
let currentUser = User.create({
|
||||
username: "chuck",
|
||||
muted_category_ids: [4],
|
||||
});
|
||||
|
||||
const state = TopicTrackingState.create({ currentUser });
|
||||
|
||||
assert.equal(state.countNew(1), 0);
|
||||
assert.equal(state.countNew(2), 0);
|
||||
assert.equal(state.countNew(3), 0);
|
||||
|
||||
state.states["t112"] = {
|
||||
last_read_post_number: null,
|
||||
id: 112,
|
||||
notification_level: NotificationLevels.TRACKING,
|
||||
category_id: 2,
|
||||
};
|
||||
|
||||
assert.equal(state.countNew(1), 1);
|
||||
assert.equal(state.countNew(1, "missing-tag"), 0);
|
||||
assert.equal(state.countNew(2), 1);
|
||||
assert.equal(state.countNew(3), 0);
|
||||
|
||||
state.states["t113"] = {
|
||||
last_read_post_number: null,
|
||||
id: 113,
|
||||
notification_level: NotificationLevels.TRACKING,
|
||||
category_id: 3,
|
||||
tags: ["amazing"],
|
||||
};
|
||||
|
||||
assert.equal(state.countNew(1), 2);
|
||||
assert.equal(state.countNew(2), 2);
|
||||
assert.equal(state.countNew(3), 1);
|
||||
assert.equal(state.countNew(3, "amazing"), 1);
|
||||
assert.equal(state.countNew(3, "missing"), 0);
|
||||
|
||||
state.states["t111"] = {
|
||||
last_read_post_number: null,
|
||||
id: 111,
|
||||
notification_level: NotificationLevels.TRACKING,
|
||||
category_id: 1,
|
||||
};
|
||||
|
||||
assert.equal(state.countNew(1), 3);
|
||||
assert.equal(state.countNew(2), 2);
|
||||
assert.equal(state.countNew(3), 1);
|
||||
|
||||
state.states["t115"] = {
|
||||
last_read_post_number: null,
|
||||
id: 115,
|
||||
category_id: 4,
|
||||
};
|
||||
assert.equal(state.countNew(4), 0);
|
||||
});
|
||||
|
||||
test("mute topic", function (assert) {
|
||||
let currentUser = User.create({
|
||||
username: "chuck",
|
||||
muted_category_ids: [],
|
||||
});
|
||||
|
||||
const state = TopicTrackingState.create({ currentUser });
|
||||
|
||||
state.trackMutedTopic(1);
|
||||
assert.equal(currentUser.muted_topics[0].topicId, 1);
|
||||
|
||||
state.pruneOldMutedTopics();
|
||||
assert.equal(state.isMutedTopic(1), true);
|
||||
|
||||
this.clock.tick(60000);
|
||||
state.pruneOldMutedTopics();
|
||||
assert.equal(state.isMutedTopic(1), false);
|
||||
test("mute topic", function (assert) {
|
||||
let currentUser = User.create({
|
||||
username: "chuck",
|
||||
muted_category_ids: [],
|
||||
});
|
||||
|
||||
const state = TopicTrackingState.create({ currentUser });
|
||||
|
||||
state.trackMutedTopic(1);
|
||||
assert.equal(currentUser.muted_topics[0].topicId, 1);
|
||||
|
||||
state.pruneOldMutedTopics();
|
||||
assert.equal(state.isMutedTopic(1), true);
|
||||
|
||||
this.clock.tick(60000);
|
||||
state.pruneOldMutedTopics();
|
||||
assert.equal(state.isMutedTopic(1), false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,31 +1,31 @@
|
||||
import { test, module } from "qunit";
|
||||
import UserAction from "discourse/models/user-action";
|
||||
|
||||
module("model: user-action");
|
||||
module("Unit | Model | user-action", function () {
|
||||
test("collapsing likes", function (assert) {
|
||||
var actions = UserAction.collapseStream([
|
||||
UserAction.create({
|
||||
action_type: UserAction.TYPES.likes_given,
|
||||
topic_id: 1,
|
||||
user_id: 1,
|
||||
post_number: 1,
|
||||
}),
|
||||
UserAction.create({
|
||||
action_type: UserAction.TYPES.edits,
|
||||
topic_id: 2,
|
||||
user_id: 1,
|
||||
post_number: 1,
|
||||
}),
|
||||
UserAction.create({
|
||||
action_type: UserAction.TYPES.likes_given,
|
||||
topic_id: 1,
|
||||
user_id: 2,
|
||||
post_number: 1,
|
||||
}),
|
||||
]);
|
||||
|
||||
test("collapsing likes", function (assert) {
|
||||
var actions = UserAction.collapseStream([
|
||||
UserAction.create({
|
||||
action_type: UserAction.TYPES.likes_given,
|
||||
topic_id: 1,
|
||||
user_id: 1,
|
||||
post_number: 1,
|
||||
}),
|
||||
UserAction.create({
|
||||
action_type: UserAction.TYPES.edits,
|
||||
topic_id: 2,
|
||||
user_id: 1,
|
||||
post_number: 1,
|
||||
}),
|
||||
UserAction.create({
|
||||
action_type: UserAction.TYPES.likes_given,
|
||||
topic_id: 1,
|
||||
user_id: 2,
|
||||
post_number: 1,
|
||||
}),
|
||||
]);
|
||||
|
||||
assert.equal(actions.length, 2);
|
||||
assert.equal(actions[0].get("children.length"), 1);
|
||||
assert.equal(actions[0].get("children")[0].items.length, 2);
|
||||
assert.equal(actions.length, 2);
|
||||
assert.equal(actions[0].get("children.length"), 1);
|
||||
assert.equal(actions[0].get("children")[0].items.length, 2);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,59 +2,59 @@ import { test, module } from "qunit";
|
||||
import UserBadge from "discourse/models/user-badge";
|
||||
import badgeFixtures from "discourse/tests/fixtures/user-badges";
|
||||
|
||||
module("model:user-badge");
|
||||
module("Unit | Model | user-badge", function () {
|
||||
test("createFromJson single", function (assert) {
|
||||
const userBadge = UserBadge.createFromJson(
|
||||
JSON.parse(JSON.stringify(badgeFixtures["/user_badges"]))
|
||||
);
|
||||
assert.ok(!Array.isArray(userBadge), "does not return an array");
|
||||
assert.equal(
|
||||
userBadge.get("badge.name"),
|
||||
"Badge 2",
|
||||
"badge reference is set"
|
||||
);
|
||||
assert.equal(
|
||||
userBadge.get("badge.badge_type.name"),
|
||||
"Silver 2",
|
||||
"badge.badge_type reference is set"
|
||||
);
|
||||
assert.equal(
|
||||
userBadge.get("granted_by.username"),
|
||||
"anne3",
|
||||
"granted_by reference is set"
|
||||
);
|
||||
});
|
||||
|
||||
test("createFromJson single", function (assert) {
|
||||
const userBadge = UserBadge.createFromJson(
|
||||
JSON.parse(JSON.stringify(badgeFixtures["/user_badges"]))
|
||||
);
|
||||
assert.ok(!Array.isArray(userBadge), "does not return an array");
|
||||
assert.equal(
|
||||
userBadge.get("badge.name"),
|
||||
"Badge 2",
|
||||
"badge reference is set"
|
||||
);
|
||||
assert.equal(
|
||||
userBadge.get("badge.badge_type.name"),
|
||||
"Silver 2",
|
||||
"badge.badge_type reference is set"
|
||||
);
|
||||
assert.equal(
|
||||
userBadge.get("granted_by.username"),
|
||||
"anne3",
|
||||
"granted_by reference is set"
|
||||
);
|
||||
});
|
||||
test("createFromJson array", function (assert) {
|
||||
const userBadges = UserBadge.createFromJson(
|
||||
JSON.parse(JSON.stringify(badgeFixtures["/user-badges/:username"]))
|
||||
);
|
||||
assert.ok(Array.isArray(userBadges), "returns an array");
|
||||
assert.equal(
|
||||
userBadges[0].get("granted_by"),
|
||||
null,
|
||||
"granted_by reference is not set when null"
|
||||
);
|
||||
});
|
||||
|
||||
test("createFromJson array", function (assert) {
|
||||
const userBadges = UserBadge.createFromJson(
|
||||
JSON.parse(JSON.stringify(badgeFixtures["/user-badges/:username"]))
|
||||
);
|
||||
assert.ok(Array.isArray(userBadges), "returns an array");
|
||||
assert.equal(
|
||||
userBadges[0].get("granted_by"),
|
||||
null,
|
||||
"granted_by reference is not set when null"
|
||||
);
|
||||
});
|
||||
test("findByUsername", async function (assert) {
|
||||
const badges = await UserBadge.findByUsername("anne3");
|
||||
assert.ok(Array.isArray(badges), "returns an array");
|
||||
});
|
||||
|
||||
test("findByUsername", async function (assert) {
|
||||
const badges = await UserBadge.findByUsername("anne3");
|
||||
assert.ok(Array.isArray(badges), "returns an array");
|
||||
});
|
||||
test("findByBadgeId", async function (assert) {
|
||||
const badges = await UserBadge.findByBadgeId(880);
|
||||
assert.ok(Array.isArray(badges), "returns an array");
|
||||
});
|
||||
|
||||
test("findByBadgeId", async function (assert) {
|
||||
const badges = await UserBadge.findByBadgeId(880);
|
||||
assert.ok(Array.isArray(badges), "returns an array");
|
||||
});
|
||||
test("grant", async function (assert) {
|
||||
const userBadge = await UserBadge.grant(1, "username");
|
||||
assert.ok(!Array.isArray(userBadge), "does not return an array");
|
||||
});
|
||||
|
||||
test("grant", async function (assert) {
|
||||
const userBadge = await UserBadge.grant(1, "username");
|
||||
assert.ok(!Array.isArray(userBadge), "does not return an array");
|
||||
});
|
||||
|
||||
test("revoke", async function (assert) {
|
||||
assert.expect(0);
|
||||
const userBadge = UserBadge.create({ id: 1 });
|
||||
await userBadge.revoke();
|
||||
test("revoke", async function (assert) {
|
||||
assert.expect(0);
|
||||
const userBadge = UserBadge.create({ id: 1 });
|
||||
await userBadge.revoke();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,31 +4,35 @@ import UserDraft from "discourse/models/user-draft";
|
||||
import { NEW_TOPIC_KEY } from "discourse/models/composer";
|
||||
import User from "discourse/models/user";
|
||||
|
||||
module("model:user-drafts");
|
||||
module("Unit | Model | user-draft", function () {
|
||||
test("stream", function (assert) {
|
||||
const user = User.create({ id: 1, username: "eviltrout" });
|
||||
const stream = user.get("userDraftsStream");
|
||||
assert.present(stream, "a user has a drafts stream by default");
|
||||
assert.equal(
|
||||
stream.get("itemsLoaded"),
|
||||
0,
|
||||
"no items are loaded by default"
|
||||
);
|
||||
assert.blank(stream.get("content"), "no content by default");
|
||||
});
|
||||
|
||||
test("stream", function (assert) {
|
||||
const user = User.create({ id: 1, username: "eviltrout" });
|
||||
const stream = user.get("userDraftsStream");
|
||||
assert.present(stream, "a user has a drafts stream by default");
|
||||
assert.equal(stream.get("itemsLoaded"), 0, "no items are loaded by default");
|
||||
assert.blank(stream.get("content"), "no content by default");
|
||||
});
|
||||
|
||||
test("draft", function (assert) {
|
||||
const drafts = [
|
||||
UserDraft.create({
|
||||
draft_key: "topic_1",
|
||||
post_number: "10",
|
||||
}),
|
||||
UserDraft.create({
|
||||
draft_key: NEW_TOPIC_KEY,
|
||||
}),
|
||||
];
|
||||
|
||||
assert.equal(drafts.length, 2, "drafts count is right");
|
||||
assert.equal(
|
||||
drafts[1].get("draftType"),
|
||||
I18n.t("drafts.new_topic"),
|
||||
"loads correct draftType label"
|
||||
);
|
||||
test("draft", function (assert) {
|
||||
const drafts = [
|
||||
UserDraft.create({
|
||||
draft_key: "topic_1",
|
||||
post_number: "10",
|
||||
}),
|
||||
UserDraft.create({
|
||||
draft_key: NEW_TOPIC_KEY,
|
||||
}),
|
||||
];
|
||||
|
||||
assert.equal(drafts.length, 2, "drafts count is right");
|
||||
assert.equal(
|
||||
drafts[1].get("draftType"),
|
||||
I18n.t("drafts.new_topic"),
|
||||
"loads correct draftType label"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,31 +2,39 @@ import { test, module } from "qunit";
|
||||
import UserAction from "discourse/models/user-action";
|
||||
import User from "discourse/models/user";
|
||||
|
||||
module("model: UserStream");
|
||||
module("Unit | Model | user-stream", function () {
|
||||
test("basics", function (assert) {
|
||||
var user = User.create({ id: 1, username: "eviltrout" });
|
||||
var stream = user.get("stream");
|
||||
assert.present(stream, "a user has a stream by default");
|
||||
assert.equal(
|
||||
stream.get("user"),
|
||||
user,
|
||||
"the stream points back to the user"
|
||||
);
|
||||
|
||||
test("basics", function (assert) {
|
||||
var user = User.create({ id: 1, username: "eviltrout" });
|
||||
var stream = user.get("stream");
|
||||
assert.present(stream, "a user has a stream by default");
|
||||
assert.equal(stream.get("user"), user, "the stream points back to the user");
|
||||
assert.equal(
|
||||
stream.get("itemsLoaded"),
|
||||
0,
|
||||
"no items are loaded by default"
|
||||
);
|
||||
assert.blank(stream.get("content"), "no content by default");
|
||||
assert.blank(stream.get("filter"), "no filter by default");
|
||||
|
||||
assert.equal(stream.get("itemsLoaded"), 0, "no items are loaded by default");
|
||||
assert.blank(stream.get("content"), "no content by default");
|
||||
assert.blank(stream.get("filter"), "no filter by default");
|
||||
assert.ok(!stream.get("loaded"), "the stream is not loaded by default");
|
||||
});
|
||||
|
||||
assert.ok(!stream.get("loaded"), "the stream is not loaded by default");
|
||||
});
|
||||
|
||||
test("filterParam", function (assert) {
|
||||
var user = User.create({ id: 1, username: "eviltrout" });
|
||||
var stream = user.get("stream");
|
||||
|
||||
// defaults to posts/topics
|
||||
assert.equal(stream.get("filterParam"), "4,5");
|
||||
|
||||
stream.set("filter", UserAction.TYPES.likes_given);
|
||||
assert.equal(stream.get("filterParam"), UserAction.TYPES.likes_given);
|
||||
|
||||
stream.set("filter", UserAction.TYPES.replies);
|
||||
assert.equal(stream.get("filterParam"), "6,9");
|
||||
test("filterParam", function (assert) {
|
||||
var user = User.create({ id: 1, username: "eviltrout" });
|
||||
var stream = user.get("stream");
|
||||
|
||||
// defaults to posts/topics
|
||||
assert.equal(stream.get("filterParam"), "4,5");
|
||||
|
||||
stream.set("filter", UserAction.TYPES.likes_given);
|
||||
assert.equal(stream.get("filterParam"), UserAction.TYPES.likes_given);
|
||||
|
||||
stream.set("filter", UserAction.TYPES.replies);
|
||||
assert.equal(stream.get("filterParam"), "6,9");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,128 +5,128 @@ import Group from "discourse/models/group";
|
||||
import * as ajaxlib from "discourse/lib/ajax";
|
||||
import pretender from "discourse/tests/helpers/create-pretender";
|
||||
|
||||
module("model:user");
|
||||
module("Unit | Model | user", function () {
|
||||
test("staff", function (assert) {
|
||||
var user = User.create({ id: 1, username: "eviltrout" });
|
||||
|
||||
test("staff", function (assert) {
|
||||
var user = User.create({ id: 1, username: "eviltrout" });
|
||||
assert.ok(!user.get("staff"), "user is not staff");
|
||||
|
||||
assert.ok(!user.get("staff"), "user is not staff");
|
||||
user.toggleProperty("moderator");
|
||||
assert.ok(user.get("staff"), "moderators are staff");
|
||||
|
||||
user.toggleProperty("moderator");
|
||||
assert.ok(user.get("staff"), "moderators are staff");
|
||||
|
||||
user.setProperties({ moderator: false, admin: true });
|
||||
assert.ok(user.get("staff"), "admins are staff");
|
||||
});
|
||||
|
||||
test("searchContext", function (assert) {
|
||||
var user = User.create({ id: 1, username: "EvilTrout" });
|
||||
|
||||
assert.deepEqual(
|
||||
user.get("searchContext"),
|
||||
{ type: "user", id: "eviltrout", user: user },
|
||||
"has a search context"
|
||||
);
|
||||
});
|
||||
|
||||
test("isAllowedToUploadAFile", function (assert) {
|
||||
var user = User.create({ trust_level: 0, admin: true });
|
||||
assert.ok(
|
||||
user.isAllowedToUploadAFile("image"),
|
||||
"admin can always upload a file"
|
||||
);
|
||||
|
||||
user.setProperties({ admin: false, moderator: true });
|
||||
assert.ok(
|
||||
user.isAllowedToUploadAFile("image"),
|
||||
"moderator can always upload a file"
|
||||
);
|
||||
});
|
||||
|
||||
test("canMangeGroup", function (assert) {
|
||||
let user = User.create({ admin: true });
|
||||
let group = Group.create({ automatic: true });
|
||||
|
||||
assert.equal(
|
||||
user.canManageGroup(group),
|
||||
false,
|
||||
"automatic groups cannot be managed."
|
||||
);
|
||||
|
||||
group.set("automatic", false);
|
||||
group.setProperties({ can_admin_group: true });
|
||||
|
||||
assert.equal(
|
||||
user.canManageGroup(group),
|
||||
true,
|
||||
"an admin should be able to manage the group"
|
||||
);
|
||||
|
||||
user.set("admin", false);
|
||||
group.setProperties({ is_group_owner: true });
|
||||
|
||||
assert.equal(
|
||||
user.canManageGroup(group),
|
||||
true,
|
||||
"a group owner should be able to manage the group"
|
||||
);
|
||||
});
|
||||
|
||||
test("resolvedTimezone", function (assert) {
|
||||
const tz = "Australia/Brisbane";
|
||||
let user = User.create({ timezone: tz, username: "chuck", id: 111 });
|
||||
let stub = sinon.stub(moment.tz, "guess").returns("America/Chicago");
|
||||
|
||||
pretender.put("/u/chuck.json", () => {
|
||||
return [200, { "Content-Type": "application/json" }, {}];
|
||||
user.setProperties({ moderator: false, admin: true });
|
||||
assert.ok(user.get("staff"), "admins are staff");
|
||||
});
|
||||
|
||||
let spy = sinon.spy(ajaxlib, "ajax");
|
||||
assert.equal(
|
||||
user.resolvedTimezone(user),
|
||||
tz,
|
||||
"if the user already has a timezone return it"
|
||||
);
|
||||
assert.ok(
|
||||
spy.notCalled,
|
||||
"if the user already has a timezone do not call AJAX update"
|
||||
);
|
||||
user = User.create({ username: "chuck", id: 111 });
|
||||
assert.equal(
|
||||
user.resolvedTimezone(user),
|
||||
"America/Chicago",
|
||||
"if the user has no timezone guess it with moment"
|
||||
);
|
||||
assert.ok(
|
||||
spy.calledWith("/u/chuck.json", {
|
||||
type: "PUT",
|
||||
dataType: "json",
|
||||
data: { timezone: "America/Chicago" },
|
||||
}),
|
||||
"if the user has no timezone save it with an AJAX update"
|
||||
);
|
||||
test("searchContext", function (assert) {
|
||||
var user = User.create({ id: 1, username: "EvilTrout" });
|
||||
|
||||
let otherUser = User.create({ username: "howardhamlin", id: 999 });
|
||||
assert.equal(
|
||||
otherUser.resolvedTimezone(user),
|
||||
null,
|
||||
"if the user has no timezone and the user is not the current user, do NOT guess with moment"
|
||||
);
|
||||
assert.not(
|
||||
spy.calledWith("/u/howardhamlin.json", {
|
||||
type: "PUT",
|
||||
dataType: "json",
|
||||
data: { timezone: "America/Chicago" },
|
||||
}),
|
||||
"if the user has no timezone, and the user is not the current user, do NOT save it with an AJAX update"
|
||||
);
|
||||
assert.deepEqual(
|
||||
user.get("searchContext"),
|
||||
{ type: "user", id: "eviltrout", user: user },
|
||||
"has a search context"
|
||||
);
|
||||
});
|
||||
|
||||
stub.restore();
|
||||
});
|
||||
|
||||
test("muted ids", function (assert) {
|
||||
let user = User.create({ username: "chuck", muted_category_ids: [] });
|
||||
|
||||
assert.deepEqual(user.calculateMutedIds(0, 1, "muted_category_ids"), [1]);
|
||||
assert.deepEqual(user.calculateMutedIds(1, 1, "muted_category_ids"), []);
|
||||
test("isAllowedToUploadAFile", function (assert) {
|
||||
var user = User.create({ trust_level: 0, admin: true });
|
||||
assert.ok(
|
||||
user.isAllowedToUploadAFile("image"),
|
||||
"admin can always upload a file"
|
||||
);
|
||||
|
||||
user.setProperties({ admin: false, moderator: true });
|
||||
assert.ok(
|
||||
user.isAllowedToUploadAFile("image"),
|
||||
"moderator can always upload a file"
|
||||
);
|
||||
});
|
||||
|
||||
test("canMangeGroup", function (assert) {
|
||||
let user = User.create({ admin: true });
|
||||
let group = Group.create({ automatic: true });
|
||||
|
||||
assert.equal(
|
||||
user.canManageGroup(group),
|
||||
false,
|
||||
"automatic groups cannot be managed."
|
||||
);
|
||||
|
||||
group.set("automatic", false);
|
||||
group.setProperties({ can_admin_group: true });
|
||||
|
||||
assert.equal(
|
||||
user.canManageGroup(group),
|
||||
true,
|
||||
"an admin should be able to manage the group"
|
||||
);
|
||||
|
||||
user.set("admin", false);
|
||||
group.setProperties({ is_group_owner: true });
|
||||
|
||||
assert.equal(
|
||||
user.canManageGroup(group),
|
||||
true,
|
||||
"a group owner should be able to manage the group"
|
||||
);
|
||||
});
|
||||
|
||||
test("resolvedTimezone", function (assert) {
|
||||
const tz = "Australia/Brisbane";
|
||||
let user = User.create({ timezone: tz, username: "chuck", id: 111 });
|
||||
let stub = sinon.stub(moment.tz, "guess").returns("America/Chicago");
|
||||
|
||||
pretender.put("/u/chuck.json", () => {
|
||||
return [200, { "Content-Type": "application/json" }, {}];
|
||||
});
|
||||
|
||||
let spy = sinon.spy(ajaxlib, "ajax");
|
||||
assert.equal(
|
||||
user.resolvedTimezone(user),
|
||||
tz,
|
||||
"if the user already has a timezone return it"
|
||||
);
|
||||
assert.ok(
|
||||
spy.notCalled,
|
||||
"if the user already has a timezone do not call AJAX update"
|
||||
);
|
||||
user = User.create({ username: "chuck", id: 111 });
|
||||
assert.equal(
|
||||
user.resolvedTimezone(user),
|
||||
"America/Chicago",
|
||||
"if the user has no timezone guess it with moment"
|
||||
);
|
||||
assert.ok(
|
||||
spy.calledWith("/u/chuck.json", {
|
||||
type: "PUT",
|
||||
dataType: "json",
|
||||
data: { timezone: "America/Chicago" },
|
||||
}),
|
||||
"if the user has no timezone save it with an AJAX update"
|
||||
);
|
||||
|
||||
let otherUser = User.create({ username: "howardhamlin", id: 999 });
|
||||
assert.equal(
|
||||
otherUser.resolvedTimezone(user),
|
||||
null,
|
||||
"if the user has no timezone and the user is not the current user, do NOT guess with moment"
|
||||
);
|
||||
assert.not(
|
||||
spy.calledWith("/u/howardhamlin.json", {
|
||||
type: "PUT",
|
||||
dataType: "json",
|
||||
data: { timezone: "America/Chicago" },
|
||||
}),
|
||||
"if the user has no timezone, and the user is not the current user, do NOT save it with an AJAX update"
|
||||
);
|
||||
|
||||
stub.restore();
|
||||
});
|
||||
|
||||
test("muted ids", function (assert) {
|
||||
let user = User.create({ username: "chuck", muted_category_ids: [] });
|
||||
|
||||
assert.deepEqual(user.calculateMutedIds(0, 1, "muted_category_ids"), [1]);
|
||||
assert.deepEqual(user.calculateMutedIds(1, 1, "muted_category_ids"), []);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,56 +2,57 @@ import { test } from "qunit";
|
||||
import { discourseModule } from "discourse/tests/helpers/qunit-helpers";
|
||||
import { currentUser } from "discourse/tests/helpers/qunit-helpers";
|
||||
|
||||
discourseModule("service:document-title", {
|
||||
beforeEach() {
|
||||
discourseModule("Unit | Service | document-title", function (hooks) {
|
||||
hooks.beforeEach(function () {
|
||||
this.documentTitle = this.container.lookup("service:document-title");
|
||||
this.documentTitle.currentUser = null;
|
||||
this.container.lookup("session:main").hasFocus = true;
|
||||
},
|
||||
afterEach() {
|
||||
});
|
||||
|
||||
hooks.afterEach(function () {
|
||||
this.documentTitle.reset();
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test("it updates the document title", function (assert) {
|
||||
this.documentTitle.setTitle("Test Title");
|
||||
assert.equal(document.title, "Test Title", "title is correct");
|
||||
});
|
||||
test("it updates the document title", function (assert) {
|
||||
this.documentTitle.setTitle("Test Title");
|
||||
assert.equal(document.title, "Test Title", "title is correct");
|
||||
});
|
||||
|
||||
test("it doesn't display notification counts for anonymous users", function (assert) {
|
||||
this.documentTitle.setTitle("test notifications");
|
||||
this.documentTitle.updateNotificationCount(5);
|
||||
assert.equal(document.title, "test notifications");
|
||||
this.documentTitle.setFocus(false);
|
||||
this.documentTitle.updateNotificationCount(6);
|
||||
assert.equal(document.title, "test notifications");
|
||||
});
|
||||
test("it doesn't display notification counts for anonymous users", function (assert) {
|
||||
this.documentTitle.setTitle("test notifications");
|
||||
this.documentTitle.updateNotificationCount(5);
|
||||
assert.equal(document.title, "test notifications");
|
||||
this.documentTitle.setFocus(false);
|
||||
this.documentTitle.updateNotificationCount(6);
|
||||
assert.equal(document.title, "test notifications");
|
||||
});
|
||||
|
||||
test("it displays notification counts for logged in users", function (assert) {
|
||||
this.documentTitle.currentUser = currentUser();
|
||||
this.documentTitle.currentUser.dynamic_favicon = false;
|
||||
this.documentTitle.setTitle("test notifications");
|
||||
this.documentTitle.updateNotificationCount(5);
|
||||
assert.equal(document.title, "test notifications");
|
||||
this.documentTitle.setFocus(false);
|
||||
this.documentTitle.updateNotificationCount(6);
|
||||
assert.equal(document.title, "(6) test notifications");
|
||||
this.documentTitle.setFocus(true);
|
||||
assert.equal(document.title, "test notifications");
|
||||
});
|
||||
test("it displays notification counts for logged in users", function (assert) {
|
||||
this.documentTitle.currentUser = currentUser();
|
||||
this.documentTitle.currentUser.dynamic_favicon = false;
|
||||
this.documentTitle.setTitle("test notifications");
|
||||
this.documentTitle.updateNotificationCount(5);
|
||||
assert.equal(document.title, "test notifications");
|
||||
this.documentTitle.setFocus(false);
|
||||
this.documentTitle.updateNotificationCount(6);
|
||||
assert.equal(document.title, "(6) test notifications");
|
||||
this.documentTitle.setFocus(true);
|
||||
assert.equal(document.title, "test notifications");
|
||||
});
|
||||
|
||||
test("it doesn't increment background context counts when focused", function (assert) {
|
||||
this.documentTitle.setTitle("background context");
|
||||
this.documentTitle.setFocus(true);
|
||||
this.documentTitle.incrementBackgroundContextCount();
|
||||
assert.equal(document.title, "background context");
|
||||
});
|
||||
test("it doesn't increment background context counts when focused", function (assert) {
|
||||
this.documentTitle.setTitle("background context");
|
||||
this.documentTitle.setFocus(true);
|
||||
this.documentTitle.incrementBackgroundContextCount();
|
||||
assert.equal(document.title, "background context");
|
||||
});
|
||||
|
||||
test("it increments background context counts when not focused", function (assert) {
|
||||
this.documentTitle.setTitle("background context");
|
||||
this.documentTitle.setFocus(false);
|
||||
this.documentTitle.incrementBackgroundContextCount();
|
||||
assert.equal(document.title, "(1) background context");
|
||||
this.documentTitle.setFocus(true);
|
||||
assert.equal(document.title, "background context");
|
||||
test("it increments background context counts when not focused", function (assert) {
|
||||
this.documentTitle.setTitle("background context");
|
||||
this.documentTitle.setFocus(false);
|
||||
this.documentTitle.incrementBackgroundContextCount();
|
||||
assert.equal(document.title, "(1) background context");
|
||||
this.documentTitle.setFocus(true);
|
||||
assert.equal(document.title, "background context");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
import { test, module } from "qunit";
|
||||
import createStore from "discourse/tests/helpers/create-store";
|
||||
|
||||
module("Unit | Service | store", function () {
|
||||
test("createRecord", function (assert) {
|
||||
const store = createStore();
|
||||
const widget = store.createRecord("widget", { id: 111, name: "hello" });
|
||||
|
||||
assert.ok(!widget.get("isNew"), "it is not a new record");
|
||||
assert.equal(widget.get("name"), "hello");
|
||||
assert.equal(widget.get("id"), 111);
|
||||
});
|
||||
|
||||
test("createRecord without an `id`", function (assert) {
|
||||
const store = createStore();
|
||||
const widget = store.createRecord("widget", { name: "hello" });
|
||||
|
||||
assert.ok(widget.get("isNew"), "it is a new record");
|
||||
assert.ok(!widget.get("id"), "there is no id");
|
||||
});
|
||||
|
||||
test("createRecord doesn't modify the input `id` field", function (assert) {
|
||||
const store = createStore();
|
||||
const widget = store.createRecord("widget", { id: 1, name: "hello" });
|
||||
|
||||
const obj = { id: 1, name: "something" };
|
||||
|
||||
const other = store.createRecord("widget", obj);
|
||||
assert.equal(widget, other, "returns the same record");
|
||||
assert.equal(widget.name, "something", "it updates the properties");
|
||||
assert.equal(obj.id, 1, "it does not remove the id from the input");
|
||||
});
|
||||
|
||||
test("createRecord without attributes", function (assert) {
|
||||
const store = createStore();
|
||||
const widget = store.createRecord("widget");
|
||||
|
||||
assert.ok(!widget.get("id"), "there is no id");
|
||||
assert.ok(widget.get("isNew"), "it is a new record");
|
||||
});
|
||||
|
||||
test("createRecord with a record as attributes returns that record from the map", function (assert) {
|
||||
const store = createStore();
|
||||
const widget = store.createRecord("widget", { id: 33 });
|
||||
const secondWidget = store.createRecord("widget", { id: 33 });
|
||||
|
||||
assert.equal(widget, secondWidget, "they should be the same");
|
||||
});
|
||||
|
||||
test("find", async function (assert) {
|
||||
const store = createStore();
|
||||
|
||||
const widget = await store.find("widget", 123);
|
||||
assert.equal(widget.get("name"), "Trout Lure");
|
||||
assert.equal(widget.get("id"), 123);
|
||||
assert.ok(!widget.get("isNew"), "found records are not new");
|
||||
assert.equal(
|
||||
widget.get("extras.hello"),
|
||||
"world",
|
||||
"extra attributes are set"
|
||||
);
|
||||
|
||||
// A second find by id returns the same object
|
||||
const widget2 = await store.find("widget", 123);
|
||||
assert.equal(widget, widget2);
|
||||
assert.equal(
|
||||
widget.get("extras.hello"),
|
||||
"world",
|
||||
"extra attributes are set"
|
||||
);
|
||||
});
|
||||
|
||||
test("find with object id", async function (assert) {
|
||||
const store = createStore();
|
||||
const widget = await store.find("widget", { id: 123 });
|
||||
assert.equal(widget.get("firstObject.name"), "Trout Lure");
|
||||
});
|
||||
|
||||
test("find with query param", async function (assert) {
|
||||
const store = createStore();
|
||||
const widget = await store.find("widget", { name: "Trout Lure" });
|
||||
assert.equal(widget.get("firstObject.id"), 123);
|
||||
});
|
||||
|
||||
test("findStale with no stale results", async function (assert) {
|
||||
const store = createStore();
|
||||
const stale = store.findStale("widget", { name: "Trout Lure" });
|
||||
|
||||
assert.ok(!stale.hasResults, "there are no stale results");
|
||||
assert.ok(!stale.results, "results are present");
|
||||
const widget = await stale.refresh();
|
||||
assert.equal(
|
||||
widget.get("firstObject.id"),
|
||||
123,
|
||||
"a `refresh()` method provides results for stale"
|
||||
);
|
||||
});
|
||||
|
||||
test("update", async function (assert) {
|
||||
const store = createStore();
|
||||
const result = await store.update("widget", 123, { name: "hello" });
|
||||
assert.ok(result);
|
||||
});
|
||||
|
||||
test("update with a multi world name", async function (assert) {
|
||||
const store = createStore();
|
||||
const result = await store.update("cool-thing", 123, { name: "hello" });
|
||||
assert.ok(result);
|
||||
assert.equal(result.payload.name, "hello");
|
||||
});
|
||||
|
||||
test("findAll", async function (assert) {
|
||||
const store = createStore();
|
||||
const result = await store.findAll("widget");
|
||||
assert.equal(result.get("length"), 2);
|
||||
|
||||
const widget = result.findBy("id", 124);
|
||||
assert.ok(!widget.get("isNew"), "found records are not new");
|
||||
assert.equal(widget.get("name"), "Evil Repellant");
|
||||
});
|
||||
|
||||
test("destroyRecord", async function (assert) {
|
||||
const store = createStore();
|
||||
const widget = await store.find("widget", 123);
|
||||
|
||||
assert.ok(await store.destroyRecord("widget", widget));
|
||||
});
|
||||
|
||||
test("destroyRecord when new", async function (assert) {
|
||||
const store = createStore();
|
||||
const widget = store.createRecord("widget", { name: "hello" });
|
||||
|
||||
assert.ok(await store.destroyRecord("widget", widget));
|
||||
});
|
||||
|
||||
test("find embedded", async function (assert) {
|
||||
const store = createStore();
|
||||
const fruit = await store.find("fruit", 1);
|
||||
assert.ok(fruit.get("farmer"), "it has the embedded object");
|
||||
|
||||
const fruitCols = fruit.get("colors");
|
||||
assert.equal(fruitCols.length, 2);
|
||||
assert.equal(fruitCols[0].get("id"), 1);
|
||||
assert.equal(fruitCols[1].get("id"), 2);
|
||||
|
||||
assert.ok(fruit.get("category"), "categories are found automatically");
|
||||
});
|
||||
|
||||
test("embedded records can be cleared", async function (assert) {
|
||||
const store = createStore();
|
||||
let fruit = await store.find("fruit", 4);
|
||||
fruit.set("farmer", { dummy: "object" });
|
||||
|
||||
fruit = await store.find("fruit", 4);
|
||||
assert.ok(!fruit.get("farmer"));
|
||||
});
|
||||
|
||||
test("meta types", async function (assert) {
|
||||
const store = createStore();
|
||||
const barn = await store.find("barn", 1);
|
||||
assert.equal(
|
||||
barn.get("owner.name"),
|
||||
"Old MacDonald",
|
||||
"it has the embedded farmer"
|
||||
);
|
||||
});
|
||||
|
||||
test("findAll embedded", async function (assert) {
|
||||
const store = createStore();
|
||||
const fruits = await store.findAll("fruit");
|
||||
assert.equal(fruits.objectAt(0).get("farmer.name"), "Old MacDonald");
|
||||
assert.equal(
|
||||
fruits.objectAt(0).get("farmer"),
|
||||
fruits.objectAt(1).get("farmer"),
|
||||
"points at the same object"
|
||||
);
|
||||
assert.equal(
|
||||
fruits.get("extras.hello"),
|
||||
"world",
|
||||
"it can supply extra information"
|
||||
);
|
||||
|
||||
const fruitCols = fruits.objectAt(0).get("colors");
|
||||
assert.equal(fruitCols.length, 2);
|
||||
assert.equal(fruitCols[0].get("id"), 1);
|
||||
assert.equal(fruitCols[1].get("id"), 2);
|
||||
|
||||
assert.equal(fruits.objectAt(2).get("farmer.name"), "Luke Skywalker");
|
||||
});
|
||||
|
||||
test("custom primaryKey", async function (assert) {
|
||||
const store = createStore();
|
||||
const cats = await store.findAll("cat");
|
||||
assert.equal(cats.objectAt(0).name, "souna");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user