[REF] mail: move channel commands to discuss folder
Part of task-3265211 closes odoo/odoo#120429 Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com>
This commit is contained in:
@@ -8,7 +8,7 @@ import { _t } from "@web/core/l10n/translation";
|
||||
patch(SuggestionService.prototype, "im_livechat", {
|
||||
getSupportedDelimiters(thread) {
|
||||
return thread?.type === "livechat"
|
||||
? [...this._super(...arguments), ":"]
|
||||
? [...this._super(...arguments), [":"]]
|
||||
: this._super(...arguments);
|
||||
},
|
||||
/**
|
||||
|
||||
@@ -179,7 +179,7 @@ export class Composer extends Component {
|
||||
}
|
||||
|
||||
onInput(ev) {
|
||||
if (!this.typingNotified && !ev.target.value.startsWith("/")) {
|
||||
if (!this.typingNotified && ev.target.value) {
|
||||
this.notifyIsTyping();
|
||||
this.typingNotified = true;
|
||||
browser.setTimeout(() => {
|
||||
|
||||
@@ -38,18 +38,20 @@ export function useSuggestion() {
|
||||
if (selectionStart > 0) {
|
||||
candidatePositions.push(selectionStart - 1);
|
||||
}
|
||||
const supportedDelimiters = suggestionService.getSupportedDelimiters(
|
||||
comp.props.composer.thread
|
||||
);
|
||||
for (const candidatePosition of candidatePositions) {
|
||||
if (candidatePosition < 0 || candidatePosition >= content.length) {
|
||||
continue;
|
||||
}
|
||||
const candidateChar = content[candidatePosition];
|
||||
if (candidateChar === "/" && candidatePosition !== 0) {
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
!suggestionService
|
||||
.getSupportedDelimiters(comp.props.composer.thread)
|
||||
.includes(candidateChar)
|
||||
!supportedDelimiters.find(
|
||||
([delimiter, allowedPosition]) =>
|
||||
delimiter === candidateChar &&
|
||||
(allowedPosition === undefined || allowedPosition === candidatePosition)
|
||||
)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
@@ -124,10 +126,11 @@ export function useSuggestion() {
|
||||
{ thread: comp.props.composer.thread },
|
||||
true
|
||||
);
|
||||
if (!suggestions) {
|
||||
const { type, mainSuggestions, extraSuggestions = [] } = suggestions;
|
||||
if (!mainSuggestions.length && !extraSuggestions.length) {
|
||||
self.clearSearch();
|
||||
return;
|
||||
}
|
||||
const { type, mainSuggestions, extraSuggestions = [] } = suggestions;
|
||||
// arbitrary limit to avoid displaying too many elements at once
|
||||
// ideally a load more mechanism should be introduced
|
||||
const limit = 8;
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
import { cleanTerm } from "@mail/utils/format";
|
||||
import { registry } from "@web/core/registry";
|
||||
|
||||
const commandRegistry = registry.category("discuss.channel_commands");
|
||||
|
||||
export class SuggestionService {
|
||||
constructor(env, services) {
|
||||
this.orm = services.orm;
|
||||
@@ -19,7 +17,7 @@ export class SuggestionService {
|
||||
}
|
||||
|
||||
getSupportedDelimiters(thread) {
|
||||
return ["@", "#", "/"];
|
||||
return [["@"], ["#"]];
|
||||
}
|
||||
|
||||
async fetchSuggestions({ delimiter, term }, { thread, onFetched } = {}) {
|
||||
@@ -32,8 +30,6 @@ export class SuggestionService {
|
||||
case "#":
|
||||
this.fetchThreads(cleanedSearchTerm).then(onFetched);
|
||||
break;
|
||||
case "/":
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +77,7 @@ export class SuggestionService {
|
||||
* Returns suggestions that match the given search term from specified type.
|
||||
*
|
||||
* @param {Object} [param0={}]
|
||||
* @param {String} [param0.delimiter] can be one one of the following: ["@", "#", "/"]
|
||||
* @param {String} [param0.delimiter] can be one one of the following: ["@", "#"]
|
||||
* @param {String} [param0.term]
|
||||
* @param {Object} [options={}]
|
||||
* @param {Integer} [options.thread] prioritize and/or restrict
|
||||
@@ -96,8 +92,6 @@ export class SuggestionService {
|
||||
}
|
||||
case "#":
|
||||
return this.searchChannelSuggestions(cleanedSearchTerm, thread, sort);
|
||||
case "/":
|
||||
return this.searchChannelCommand(cleanedSearchTerm, thread, sort);
|
||||
}
|
||||
return {
|
||||
type: undefined,
|
||||
@@ -106,65 +100,6 @@ export class SuggestionService {
|
||||
};
|
||||
}
|
||||
|
||||
searchChannelCommand(cleanedSearchTerm, thread, sort) {
|
||||
if (!thread.isChannel) {
|
||||
// channel commands are channel specific
|
||||
return;
|
||||
}
|
||||
const commands = commandRegistry
|
||||
.getEntries()
|
||||
.filter(([name, command]) => {
|
||||
if (!cleanTerm(name).includes(cleanedSearchTerm)) {
|
||||
return false;
|
||||
}
|
||||
if (command.channel_types) {
|
||||
return command.channel_types.includes(thread.type);
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.map(([name, command]) => {
|
||||
return {
|
||||
channel_types: command.channel_types,
|
||||
help: command.help,
|
||||
id: command.id,
|
||||
name,
|
||||
};
|
||||
});
|
||||
const sortFunc = (c1, c2) => {
|
||||
if (c1.channel_types && !c2.channel_types) {
|
||||
return -1;
|
||||
}
|
||||
if (!c1.channel_types && c2.channel_types) {
|
||||
return 1;
|
||||
}
|
||||
const cleanedName1 = cleanTerm(c1.name || "");
|
||||
const cleanedName2 = cleanTerm(c2.name || "");
|
||||
if (
|
||||
cleanedName1.startsWith(cleanedSearchTerm) &&
|
||||
!cleanedName2.startsWith(cleanedSearchTerm)
|
||||
) {
|
||||
return -1;
|
||||
}
|
||||
if (
|
||||
!cleanedName1.startsWith(cleanedSearchTerm) &&
|
||||
cleanedName2.startsWith(cleanedSearchTerm)
|
||||
) {
|
||||
return 1;
|
||||
}
|
||||
if (cleanedName1 < cleanedName2) {
|
||||
return -1;
|
||||
}
|
||||
if (cleanedName1 > cleanedName2) {
|
||||
return 1;
|
||||
}
|
||||
return c1.id - c2.id;
|
||||
};
|
||||
return {
|
||||
type: "ChannelCommand",
|
||||
mainSuggestions: sort ? commands.sort(sortFunc) : commands,
|
||||
};
|
||||
}
|
||||
|
||||
searchPartnerSuggestions(cleanedSearchTerm, thread, sort) {
|
||||
let partners;
|
||||
const isNonPublicChannel =
|
||||
|
||||
@@ -10,8 +10,6 @@ import { LinkPreview } from "./link_preview_model";
|
||||
import { NotificationGroup } from "./notification_group_model";
|
||||
import { assignDefined, createLocalId } from "../utils/misc";
|
||||
|
||||
const commandRegistry = registry.category("discuss.channel_commands");
|
||||
|
||||
const { DateTime } = luxon;
|
||||
|
||||
export class MessageService {
|
||||
@@ -66,18 +64,6 @@ export class MessageService {
|
||||
});
|
||||
}
|
||||
|
||||
getCommandFromText(thread, content) {
|
||||
if (content.startsWith("/")) {
|
||||
const firstWord = content.substring(1).split(/\s/)[0];
|
||||
const command = commandRegistry.get(firstWord, false);
|
||||
if (command) {
|
||||
return command.channel_types?.includes(thread.type) || thread.isChannel
|
||||
? command
|
||||
: false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {number}
|
||||
*/
|
||||
|
||||
@@ -879,13 +879,6 @@ export class ThreadService {
|
||||
* @param {string} body
|
||||
*/
|
||||
async post(thread, body, { attachments = [], isNote = false, parentId, rawMentions }) {
|
||||
const command = this.store.user
|
||||
? this.messageService.getCommandFromText(thread, body)
|
||||
: undefined;
|
||||
if (command) {
|
||||
await this.executeCommand(thread, command, body);
|
||||
return;
|
||||
}
|
||||
let tmpMsg;
|
||||
const subtype = isNote ? "mail.mt_note" : "mail.mt_comment";
|
||||
const validMentions = this.store.user
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/* @odoo-module */
|
||||
|
||||
import { Composer } from "@mail/composer/composer";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
|
||||
const commandRegistry = registry.category("discuss.channel_commands");
|
||||
|
||||
patch(Composer.prototype, "discuss", {
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
onInput(ev) {
|
||||
if (this.thread?.model === "discuss.channel" && ev.target.value.startsWith("/")) {
|
||||
const [firstWord] = ev.target.value.substring(1).split(/\s/);
|
||||
const command = commandRegistry.get(firstWord, false);
|
||||
if (
|
||||
ev.target.value === "/" || // suggestions not yet started
|
||||
this.hasSuggestions ||
|
||||
(command &&
|
||||
(!command.channel_types || command.channel_types.includes(this.thread.type)))
|
||||
) {
|
||||
this.stopTyping();
|
||||
return;
|
||||
}
|
||||
}
|
||||
this._super(ev);
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,82 @@
|
||||
/* @odoo-module */
|
||||
|
||||
import { SuggestionService } from "@mail/composer/suggestion_service";
|
||||
import { cleanTerm } from "@mail/utils/format";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
|
||||
const commandRegistry = registry.category("discuss.channel_commands");
|
||||
|
||||
patch(SuggestionService.prototype, "discuss", {
|
||||
getSupportedDelimiters(thread) {
|
||||
const res = this._super(thread);
|
||||
return thread?.model === "discuss.channel" ? [...res, ["/", 0]] : res;
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
searchSuggestions({ delimiter, term }, { thread } = {}, sort = false) {
|
||||
if (delimiter === "/") {
|
||||
return this.searchChannelCommand(cleanTerm(term), thread, sort);
|
||||
}
|
||||
return this._super(...arguments);
|
||||
},
|
||||
searchChannelCommand(cleanedSearchTerm, thread, sort) {
|
||||
if (!thread.isChannel) {
|
||||
// channel commands are channel specific
|
||||
return;
|
||||
}
|
||||
const commands = commandRegistry
|
||||
.getEntries()
|
||||
.filter(([name, command]) => {
|
||||
if (!cleanTerm(name).includes(cleanedSearchTerm)) {
|
||||
return false;
|
||||
}
|
||||
if (command.channel_types) {
|
||||
return command.channel_types.includes(thread.type);
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.map(([name, command]) => {
|
||||
return {
|
||||
channel_types: command.channel_types,
|
||||
help: command.help,
|
||||
id: command.id,
|
||||
name,
|
||||
};
|
||||
});
|
||||
const sortFunc = (c1, c2) => {
|
||||
if (c1.channel_types && !c2.channel_types) {
|
||||
return -1;
|
||||
}
|
||||
if (!c1.channel_types && c2.channel_types) {
|
||||
return 1;
|
||||
}
|
||||
const cleanedName1 = cleanTerm(c1.name || "");
|
||||
const cleanedName2 = cleanTerm(c2.name || "");
|
||||
if (
|
||||
cleanedName1.startsWith(cleanedSearchTerm) &&
|
||||
!cleanedName2.startsWith(cleanedSearchTerm)
|
||||
) {
|
||||
return -1;
|
||||
}
|
||||
if (
|
||||
!cleanedName1.startsWith(cleanedSearchTerm) &&
|
||||
cleanedName2.startsWith(cleanedSearchTerm)
|
||||
) {
|
||||
return 1;
|
||||
}
|
||||
if (cleanedName1 < cleanedName2) {
|
||||
return -1;
|
||||
}
|
||||
if (cleanedName1 > cleanedName2) {
|
||||
return 1;
|
||||
}
|
||||
return c1.id - c2.id;
|
||||
};
|
||||
return {
|
||||
type: "ChannelCommand",
|
||||
mainSuggestions: sort ? commands.sort(sortFunc) : commands,
|
||||
};
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,29 @@
|
||||
/* @odoo-module */
|
||||
|
||||
import { ThreadService } from "@mail/core/thread_service";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
|
||||
const commandRegistry = registry.category("discuss.channel_commands");
|
||||
|
||||
patch(ThreadService.prototype, "discuss", {
|
||||
/**
|
||||
* @override
|
||||
* @param {import("@mail/core/thread_model").Thread} thread
|
||||
* @param {string} body
|
||||
*/
|
||||
async post(thread, body) {
|
||||
if (thread.model === "discuss.channel" && body.startsWith("/")) {
|
||||
const [firstWord] = body.substring(1).split(/\s/);
|
||||
const command = commandRegistry.get(firstWord, false);
|
||||
if (
|
||||
command &&
|
||||
(!command.channel_types || command.channel_types.includes(thread.type))
|
||||
) {
|
||||
await this.executeCommand(thread, command, body);
|
||||
return;
|
||||
}
|
||||
}
|
||||
return this._super(...arguments);
|
||||
},
|
||||
});
|
||||
@@ -267,21 +267,6 @@ QUnit.test(
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test('do not send typing notification on typing "/" command', async (assert) => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({ name: "channel" });
|
||||
const { openDiscuss } = await start({
|
||||
async mockRPC(route, args) {
|
||||
if (route === "/discuss/channel/notify_typing") {
|
||||
assert.step(`notify_typing:${args.is_typing}`);
|
||||
}
|
||||
},
|
||||
});
|
||||
await openDiscuss(channelId);
|
||||
await insertText(".o-mail-Composer-input", "/");
|
||||
assert.verifySteps([], "No rpc done");
|
||||
});
|
||||
|
||||
QUnit.test("composer text input cleared on message post", async (assert) => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({ name: "au-secours-aidez-moi" });
|
||||
@@ -360,49 +345,6 @@ QUnit.test(
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test(
|
||||
'do not send typing notification on typing after selecting suggestion from "/" command',
|
||||
async (assert) => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({ name: "channel" });
|
||||
const { openDiscuss } = await start({
|
||||
async mockRPC(route, args) {
|
||||
if (route === "/discuss/channel/notify_typing") {
|
||||
assert.step(`notify_typing:${args.is_typing}`);
|
||||
}
|
||||
},
|
||||
});
|
||||
await openDiscuss(channelId);
|
||||
await insertText(".o-mail-Composer-input", "/");
|
||||
await click(".o-mail-Composer-suggestion");
|
||||
await insertText(".o-mail-Composer-input", " is user?");
|
||||
assert.verifySteps([], "No rpc done");
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test("add an emoji after a command", async (assert) => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({
|
||||
name: "General",
|
||||
channel_type: "channel",
|
||||
});
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss(channelId);
|
||||
assert.containsNone($, ".o-mail-Composer-suggestionList .o-open");
|
||||
assert.strictEqual($(".o-mail-Composer-input").val(), "");
|
||||
await insertText(".o-mail-Composer-input", "/");
|
||||
await click(".o-mail-Composer-suggestion");
|
||||
assert.strictEqual(
|
||||
$(".o-mail-Composer-input").val().replace(/\s/, " "),
|
||||
"/who ",
|
||||
"previous content + used command + additional whitespace afterwards"
|
||||
);
|
||||
|
||||
await click("button[aria-label='Emojis']");
|
||||
await click(".o-mail-Emoji:contains(😊)");
|
||||
assert.strictEqual($(".o-mail-Composer-input").val().replace(/\s/, " "), "/who 😊");
|
||||
});
|
||||
|
||||
QUnit.test("add an emoji after a partner mention", async (assert) => {
|
||||
const pyEnv = await startServer();
|
||||
const partnerId = pyEnv["res.partner"].create({
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
/* @odoo-module */
|
||||
|
||||
import { click, insertText, start, startServer } from "@mail/../tests/helpers/test_utils";
|
||||
|
||||
import { patchWithCleanup } from "@web/../tests/helpers/utils";
|
||||
import { Composer } from "@mail/composer/composer";
|
||||
|
||||
QUnit.module("composer", {
|
||||
async beforeEach() {
|
||||
// Simulate real user interactions
|
||||
patchWithCleanup(Composer.prototype, {
|
||||
isEventTrusted() {
|
||||
return true;
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
QUnit.test('do not send typing notification on typing "/" command', async (assert) => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({ name: "channel" });
|
||||
const { openDiscuss } = await start({
|
||||
async mockRPC(route, args) {
|
||||
if (route === "/discuss/channel/notify_typing") {
|
||||
assert.step(`notify_typing:${args.is_typing}`);
|
||||
}
|
||||
},
|
||||
});
|
||||
await openDiscuss(channelId);
|
||||
await insertText(".o-mail-Composer-input", "/");
|
||||
assert.verifySteps([], "No rpc done");
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
'do not send typing notification on typing after selecting suggestion from "/" command',
|
||||
async (assert) => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({ name: "channel" });
|
||||
const { openDiscuss } = await start({
|
||||
async mockRPC(route, args) {
|
||||
if (route === "/discuss/channel/notify_typing") {
|
||||
assert.step(`notify_typing:${args.is_typing}`);
|
||||
}
|
||||
},
|
||||
});
|
||||
await openDiscuss(channelId);
|
||||
await insertText(".o-mail-Composer-input", "/");
|
||||
await click(".o-mail-Composer-suggestion");
|
||||
await insertText(".o-mail-Composer-input", " is user?");
|
||||
assert.verifySteps([], "No rpc done");
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test("add an emoji after a command", async (assert) => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({
|
||||
name: "General",
|
||||
channel_type: "channel",
|
||||
});
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss(channelId);
|
||||
assert.containsNone($, ".o-mail-Composer-suggestionList .o-open");
|
||||
assert.strictEqual($(".o-mail-Composer-input").val(), "");
|
||||
await insertText(".o-mail-Composer-input", "/");
|
||||
await click(".o-mail-Composer-suggestion");
|
||||
assert.strictEqual(
|
||||
$(".o-mail-Composer-input").val().replace(/\s/, " "),
|
||||
"/who ",
|
||||
"previous content + used command + additional whitespace afterwards"
|
||||
);
|
||||
|
||||
await click("button[aria-label='Emojis']");
|
||||
await click(".o-mail-Emoji:contains(😊)");
|
||||
assert.strictEqual($(".o-mail-Composer-input").val().replace(/\s/, " "), "/who 😊");
|
||||
});
|
||||
@@ -0,0 +1,64 @@
|
||||
/* @odoo-module */
|
||||
|
||||
import { Composer } from "@mail/composer/composer";
|
||||
import { click, insertText, start, startServer } from "@mail/../tests/helpers/test_utils";
|
||||
import { patchWithCleanup } from "@web/../tests/helpers/utils";
|
||||
|
||||
QUnit.module("suggestion", {
|
||||
async beforeEach() {
|
||||
// Simulate real user interactions
|
||||
patchWithCleanup(Composer.prototype, {
|
||||
isEventTrusted() {
|
||||
return true;
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
QUnit.test('display command suggestions on typing "/"', async (assert) => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({
|
||||
name: "General",
|
||||
channel_type: "channel",
|
||||
});
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss(channelId);
|
||||
assert.containsNone($, ".o-mail-Composer-suggestionList .o-open");
|
||||
await insertText(".o-mail-Composer-input", "/");
|
||||
assert.containsOnce($, ".o-mail-Composer-suggestionList .o-open");
|
||||
});
|
||||
|
||||
QUnit.test("use a command for a specific channel type", async (assert) => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({ channel_type: "chat" });
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss(channelId);
|
||||
assert.containsNone($, ".o-mail-Composer-suggestionList .o-open");
|
||||
assert.strictEqual($(".o-mail-Composer-input").val(), "");
|
||||
await insertText(".o-mail-Composer-input", "/");
|
||||
await click(".o-mail-Composer-suggestion");
|
||||
assert.strictEqual(
|
||||
$(".o-mail-Composer-input").val().replace(/\s/, " "),
|
||||
"/who ",
|
||||
"command + additional whitespace afterwards"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"command suggestion should only open if command is the first character",
|
||||
async (assert) => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({
|
||||
name: "General",
|
||||
channel_type: "channel",
|
||||
});
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss(channelId);
|
||||
assert.containsNone($, ".o-mail-Composer-suggestionList .o-open");
|
||||
assert.strictEqual($(".o-mail-Composer-input").val(), "");
|
||||
await insertText(".o-mail-Composer-input", "bluhbluh ");
|
||||
assert.strictEqual($(".o-mail-Composer-input").val(), "bluhbluh ");
|
||||
await insertText(".o-mail-Composer-input", "/");
|
||||
assert.containsNone($, ".o-mail-Composer-suggestionList .o-open");
|
||||
}
|
||||
);
|
||||
@@ -129,54 +129,6 @@ QUnit.test("select @ mention insert mention text in composer", async (assert) =>
|
||||
assert.strictEqual($(".o-mail-Composer-input").val().trim(), "@TestPartner");
|
||||
});
|
||||
|
||||
QUnit.test('display command suggestions on typing "/"', async (assert) => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({
|
||||
name: "General",
|
||||
channel_type: "channel",
|
||||
});
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss(channelId);
|
||||
assert.containsNone($, ".o-mail-Composer-suggestionList .o-open");
|
||||
await insertText(".o-mail-Composer-input", "/");
|
||||
assert.containsOnce($, ".o-mail-Composer-suggestionList .o-open");
|
||||
});
|
||||
|
||||
QUnit.test("use a command for a specific channel type", async (assert) => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({ channel_type: "chat" });
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss(channelId);
|
||||
assert.containsNone($, ".o-mail-Composer-suggestionList .o-open");
|
||||
assert.strictEqual($(".o-mail-Composer-input").val(), "");
|
||||
await insertText(".o-mail-Composer-input", "/");
|
||||
await click(".o-mail-Composer-suggestion");
|
||||
assert.strictEqual(
|
||||
$(".o-mail-Composer-input").val().replace(/\s/, " "),
|
||||
"/who ",
|
||||
"command + additional whitespace afterwards"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"command suggestion should only open if command is the first character",
|
||||
async (assert) => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({
|
||||
name: "General",
|
||||
channel_type: "channel",
|
||||
});
|
||||
const { openDiscuss } = await start();
|
||||
await openDiscuss(channelId);
|
||||
assert.containsNone($, ".o-mail-Composer-suggestionList .o-open");
|
||||
assert.strictEqual($(".o-mail-Composer-input").val(), "");
|
||||
await insertText(".o-mail-Composer-input", "bluhbluh ");
|
||||
assert.strictEqual($(".o-mail-Composer-input").val(), "bluhbluh ");
|
||||
await insertText(".o-mail-Composer-input", "/");
|
||||
assert.containsNone($, ".o-mail-Composer-suggestionList .o-open");
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test('display channel mention suggestions on typing "#"', async (assert) => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({
|
||||
|
||||
Reference in New Issue
Block a user