[REF] mail: tests: add scrollTop to contains
This is much more robust and faster than relying on arbitrary number of animation frames. closes odoo/odoo#133333 Related: odoo/enterprise#46407 Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com>
This commit is contained in:
@@ -127,19 +127,26 @@ export function useAutoScroll(refName, shouldScrollPredicate = () => true) {
|
||||
const ref = useRef(refName);
|
||||
let el = null;
|
||||
let isScrolled = true;
|
||||
let lastSetValue;
|
||||
const observer = new ResizeObserver(applyScroll);
|
||||
|
||||
function onScroll() {
|
||||
isScrolled = Math.abs(el.scrollTop + el.clientHeight - el.scrollHeight) < 1;
|
||||
}
|
||||
function applyScroll() {
|
||||
if (isScrolled && shouldScrollPredicate()) {
|
||||
async function applyScroll() {
|
||||
if (isScrolled && shouldScrollPredicate() && lastSetValue !== ref.el.scrollHeight) {
|
||||
/**
|
||||
* Avoid setting the same value 2 times in a row. This is not supposed to have an
|
||||
* effect, unless the value was changed from outside in the meantime, in which case
|
||||
* resetting the value would incorrectly override the other change.
|
||||
*/
|
||||
lastSetValue = ref.el.scrollHeight;
|
||||
ref.el.scrollTop = ref.el.scrollHeight;
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
el = ref.el;
|
||||
el.scrollTop = el.scrollHeight;
|
||||
applyScroll();
|
||||
observer.observe(el);
|
||||
el.addEventListener("scroll", onScroll);
|
||||
});
|
||||
|
||||
@@ -8,13 +8,11 @@ import {
|
||||
import { Command } from "@mail/../tests/helpers/command";
|
||||
import { patchUiSize, SIZES } from "@mail/../tests/helpers/patch_ui_size";
|
||||
import {
|
||||
afterNextRender,
|
||||
click,
|
||||
contains,
|
||||
createFile,
|
||||
insertText,
|
||||
isScrolledToBottom,
|
||||
nextAnimationFrame,
|
||||
scroll,
|
||||
start,
|
||||
startServer,
|
||||
} from "@mail/../tests/helpers/test_utils";
|
||||
@@ -774,7 +772,7 @@ QUnit.test("chat window should not open when receiving a new DM from odoobot", a
|
||||
|
||||
QUnit.test(
|
||||
"chat window should scroll to the newly posted message just after posting it",
|
||||
async (assert) => {
|
||||
async () => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({
|
||||
channel_member_ids: [
|
||||
@@ -797,9 +795,11 @@ QUnit.test(
|
||||
});
|
||||
}
|
||||
await start();
|
||||
await contains(".o-mail-Message", 10);
|
||||
await insertText(".o-mail-Composer-input", "WOLOLO");
|
||||
triggerHotkey("Enter");
|
||||
assert.ok(isScrolledToBottom($(".o-mail-Thread")[0]));
|
||||
await contains(".o-mail-Message", 11);
|
||||
await contains(".o-mail-Thread", 1, { scroll: "bottom" });
|
||||
}
|
||||
);
|
||||
|
||||
@@ -967,7 +967,7 @@ QUnit.test(
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test("chat window: scroll conservation on toggle discuss", async (assert) => {
|
||||
QUnit.test("chat window: scroll conservation on toggle discuss", async () => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({});
|
||||
for (let i = 0; i < 100; i++) {
|
||||
@@ -979,26 +979,24 @@ QUnit.test("chat window: scroll conservation on toggle discuss", async (assert)
|
||||
}
|
||||
const { openDiscuss, openView } = await start();
|
||||
await click(".o_menu_systray .dropdown-toggle:has(i[aria-label='Messages'])");
|
||||
/**
|
||||
* The afterNextRender is necessary because otherwise useAutoScroll would
|
||||
* set the scroll to bottom after the manually set value from this test.
|
||||
*/
|
||||
await afterNextRender(async () => await click(".o-mail-NotificationItem"));
|
||||
(await contains(".o-mail-ChatWindow .o-mail-Thread"))[0].scrollTop = 142;
|
||||
await click(".o-mail-NotificationItem");
|
||||
await contains(".o-mail-Message", 31);
|
||||
await contains(".o-mail-ChatWindow .o-mail-Thread", 1, { scroll: "bottom" });
|
||||
await scroll(".o-mail-ChatWindow .o-mail-Thread", 142);
|
||||
openDiscuss(null, { waitUntilMessagesLoaded: false });
|
||||
await contains(".o-mail-ChatWindow", 0);
|
||||
|
||||
openView({
|
||||
res_id: channelId,
|
||||
res_model: "discuss.channel",
|
||||
views: [[false, "list"]],
|
||||
});
|
||||
assert.strictEqual((await contains(".o-mail-ChatWindow .o-mail-Thread"))[0].scrollTop, 142);
|
||||
await contains(".o-mail-Message", 31);
|
||||
await contains(".o-mail-ChatWindow .o-mail-Thread", 1, { scroll: 142 });
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"chat window with a thread: keep scroll position in message list on folded",
|
||||
async (assert) => {
|
||||
async () => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({});
|
||||
for (let i = 0; i < 100; i++) {
|
||||
@@ -1010,25 +1008,24 @@ QUnit.test(
|
||||
}
|
||||
await start();
|
||||
await click(".o_menu_systray .dropdown-toggle:has(i[aria-label='Messages'])");
|
||||
/**
|
||||
* The afterNextRender is necessary because otherwise useAutoScroll would
|
||||
* set the scroll to bottom after the manually set value from this test.
|
||||
*/
|
||||
await afterNextRender(async () => await click(".o-mail-NotificationItem"));
|
||||
(await contains(".o-mail-ChatWindow .o-mail-Thread"))[0].scrollTop = 142;
|
||||
|
||||
await click(".o-mail-NotificationItem");
|
||||
await contains(".o-mail-Message", 31);
|
||||
await contains(".o-mail-ChatWindow .o-mail-Thread", 1, { scroll: "bottom" });
|
||||
await scroll(".o-mail-ChatWindow .o-mail-Thread", 142);
|
||||
// fold chat window
|
||||
await click(".o-mail-ChatWindow-command[title='Fold']");
|
||||
await contains(".o-mail-Message", 0);
|
||||
await contains(".o-mail-ChatWindow .o-mail-Thread", 0);
|
||||
// unfold chat window
|
||||
await click(".o-mail-ChatWindow-command[title='Open']");
|
||||
assert.strictEqual((await contains(".o-mail-ChatWindow .o-mail-Thread"))[0].scrollTop, 142);
|
||||
await contains(".o-mail-Message", 31);
|
||||
await contains(".o-mail-ChatWindow .o-mail-Thread", 1, { scroll: 142 });
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test(
|
||||
"chat window with a thread: keep scroll position in message list on toggle discuss when folded",
|
||||
async (assert) => {
|
||||
async () => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({});
|
||||
for (let i = 0; i < 100; i++) {
|
||||
@@ -1040,18 +1037,14 @@ QUnit.test(
|
||||
}
|
||||
const { openDiscuss, openView } = await start();
|
||||
await click(".o_menu_systray .dropdown-toggle:has(i[aria-label='Messages'])");
|
||||
/**
|
||||
* The afterNextRender is necessary because otherwise useAutoScroll would
|
||||
* set the scroll to bottom after the manually set value from this test.
|
||||
*/
|
||||
await afterNextRender(async () => await click(".o-mail-NotificationItem"));
|
||||
(await contains(".o-mail-ChatWindow .o-mail-Thread"))[0].scrollTop = 142;
|
||||
|
||||
await click(".o-mail-NotificationItem");
|
||||
await contains(".o-mail-Message", 31);
|
||||
await contains(".o-mail-ChatWindow .o-mail-Thread", 1, { scroll: "bottom" });
|
||||
await scroll(".o-mail-ChatWindow .o-mail-Thread", 142);
|
||||
// fold chat window
|
||||
await click(".o-mail-ChatWindow-command[title='Fold']");
|
||||
openDiscuss(null, { waitUntilMessagesLoaded: false });
|
||||
await contains(".o-mail-ChatWindow", 0);
|
||||
|
||||
openView({
|
||||
res_id: channelId,
|
||||
res_model: "discuss.channel",
|
||||
@@ -1059,7 +1052,8 @@ QUnit.test(
|
||||
});
|
||||
// unfold chat window
|
||||
await click(".o-mail-ChatWindow-command[title='Open']");
|
||||
assert.strictEqual((await contains(".o-mail-ChatWindow .o-mail-Thread"))[0].scrollTop, 142);
|
||||
await contains(".o-mail-ChatWindow .o-mail-Message", 31);
|
||||
await contains(".o-mail-ChatWindow .o-mail-Thread", 1, { scroll: 142 });
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1102,8 +1096,7 @@ QUnit.test("Chat window in mobile are not foldable", async () => {
|
||||
await click("button i[aria-label='Messages']");
|
||||
await click(".o-mail-NotificationItem");
|
||||
await contains(".o-mail-ChatWindow-header.cursor-pointer", 0);
|
||||
click(".o-mail-ChatWindow-header").catch(() => {});
|
||||
await nextAnimationFrame();
|
||||
await click(".o-mail-ChatWindow-header");
|
||||
await contains(".o-mail-ChatWindow-content"); // content => non-folded
|
||||
});
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
dropFiles,
|
||||
insertText,
|
||||
pasteFiles,
|
||||
scroll,
|
||||
start,
|
||||
startServer,
|
||||
} from "@mail/../tests/helpers/test_utils";
|
||||
@@ -196,33 +197,33 @@ QUnit.test(
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test("keep emoji picker scroll value when re-opening it", async (assert) => {
|
||||
QUnit.test("keep emoji picker scroll value when re-opening it", async () => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({ name: "roblox-carsurfing" });
|
||||
const { openDiscuss } = await start();
|
||||
openDiscuss(channelId);
|
||||
await click("button[aria-label='Emojis']");
|
||||
(await contains(".o-EmojiPicker-content"))[0].scrollTop = 150;
|
||||
await scroll(".o-EmojiPicker-content", 150);
|
||||
await click("button[aria-label='Emojis']");
|
||||
await click("button[aria-label='Emojis']");
|
||||
assert.strictEqual((await contains(".o-EmojiPicker-content"))[0].scrollTop, 150);
|
||||
await contains(".o-EmojiPicker-content", 1, { scroll: 150 });
|
||||
});
|
||||
|
||||
QUnit.test("reset emoji picker scroll value after an emoji is picked", async (assert) => {
|
||||
QUnit.test("reset emoji picker scroll value after an emoji is picked", async () => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({ name: "roblox-fingerskating" });
|
||||
const { openDiscuss } = await start();
|
||||
openDiscuss(channelId);
|
||||
await click("button[aria-label='Emojis']");
|
||||
(await contains(".o-EmojiPicker-content"))[0].scrollTop = 150;
|
||||
await scroll(".o-EmojiPicker-content", 150);
|
||||
await click(".o-Emoji:contains(😎)");
|
||||
await click("button[aria-label='Emojis']");
|
||||
assert.strictEqual((await contains(".o-EmojiPicker-content"))[0].scrollTop, 0);
|
||||
await contains(".o-EmojiPicker-content", 1, { scroll: 0 });
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"keep emoji picker scroll value independent if two or more different emoji pickers are used",
|
||||
async (assert) => {
|
||||
async () => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({ name: "roblox-jaywalking" });
|
||||
const { openDiscuss } = await start();
|
||||
@@ -235,22 +236,18 @@ QUnit.test(
|
||||
res_id: channelId,
|
||||
});
|
||||
openDiscuss(channelId);
|
||||
|
||||
await triggerEvent(getFixture(), null, "mousedown");
|
||||
await click("button[aria-label='Emojis']");
|
||||
(await contains(".o-EmojiPicker-content"))[0].scrollTop = 150;
|
||||
|
||||
await scroll(".o-EmojiPicker-content", 150);
|
||||
await triggerEvent(getFixture(), null, "mousedown");
|
||||
await click("[title='Add a Reaction']");
|
||||
(await contains(".o-EmojiPicker-content"))[0].scrollTop = 200;
|
||||
|
||||
await scroll(".o-EmojiPicker-content", 200);
|
||||
await triggerEvent(getFixture(), null, "mousedown");
|
||||
await click("button[aria-label='Emojis']");
|
||||
assert.strictEqual((await contains(".o-EmojiPicker-content"))[0].scrollTop, 150);
|
||||
|
||||
await contains(".o-EmojiPicker-content", 1, { scroll: 150 });
|
||||
await triggerEvent(getFixture(), null, "mousedown");
|
||||
await click("[title='Add a Reaction']");
|
||||
assert.strictEqual((await contains(".o-EmojiPicker-content"))[0].scrollTop, 200);
|
||||
await contains(".o-EmojiPicker-content", 1, { scroll: 200 });
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -11,8 +11,7 @@ import {
|
||||
contains,
|
||||
createFile,
|
||||
insertText,
|
||||
isScrolledToBottom,
|
||||
nextAnimationFrame,
|
||||
scroll,
|
||||
start,
|
||||
startServer,
|
||||
} from "@mail/../tests/helpers/test_utils";
|
||||
@@ -261,7 +260,7 @@ QUnit.test("No load more when fetch below fetch limit of 30", async (assert) =>
|
||||
});
|
||||
openDiscuss(channelId);
|
||||
await contains(".o-mail-Message", 29);
|
||||
await contains("button:contains(Load more)", 0);
|
||||
await contains("button:contains(Load More)", 0);
|
||||
});
|
||||
|
||||
QUnit.test("show date separator above mesages of similar date", async (assert) => {
|
||||
@@ -796,17 +795,13 @@ QUnit.test('all messages in "Inbox" in "History" after marked all as read', asyn
|
||||
}
|
||||
const { openDiscuss } = await start();
|
||||
openDiscuss();
|
||||
await contains(".o-mail-Message", 30);
|
||||
await click("button:contains(Mark all read)");
|
||||
await contains(".o-mail-Message", 0);
|
||||
|
||||
/**
|
||||
* The afterNextRender is necessary because otherwise useAutoScroll would
|
||||
* set the scroll to bottom after the manually set value from this test.
|
||||
*/
|
||||
await afterNextRender(async () => await click("button:contains(History)"));
|
||||
await click("button:contains(History)");
|
||||
await contains(".o-mail-Message", 30);
|
||||
|
||||
$(".o-mail-Thread")[0].scrollTop = 0;
|
||||
await contains(".o-mail-Thread", 1, { scroll: "bottom" });
|
||||
await scroll(".o-mail-Thread", 0);
|
||||
await contains(".o-mail-Message", 40);
|
||||
});
|
||||
|
||||
@@ -1439,27 +1434,23 @@ QUnit.test("new messages separator [REQUIRE FOCUS]", async () => {
|
||||
openDiscuss(channelId);
|
||||
await contains(".o-mail-Message", 25);
|
||||
await contains("hr + span:contains(New messages)", 0);
|
||||
|
||||
$(".o-mail-Discuss-content .o-mail-Thread")[0].scrollTop = 0;
|
||||
await contains(".o-mail-Discuss-content .o-mail-Thread", 1, { scroll: "bottom" });
|
||||
await scroll(".o-mail-Discuss-content .o-mail-Thread", 0);
|
||||
// composer is focused by default, we remove that focus
|
||||
$(".o-mail-Composer-input")[0].blur();
|
||||
// simulate receiving a message
|
||||
await afterNextRender(async () =>
|
||||
pyEnv.withUser(userId, () =>
|
||||
env.services.rpc("/mail/message/post", {
|
||||
post_data: { body: "hu", message_type: "comment" },
|
||||
thread_id: channelId,
|
||||
thread_model: "discuss.channel",
|
||||
})
|
||||
)
|
||||
pyEnv.withUser(userId, () =>
|
||||
env.services.rpc("/mail/message/post", {
|
||||
post_data: { body: "hu", message_type: "comment" },
|
||||
thread_id: channelId,
|
||||
thread_model: "discuss.channel",
|
||||
})
|
||||
);
|
||||
await contains(".o-mail-Message", 26);
|
||||
await contains("hr + span:contains(New messages)");
|
||||
const messageList = $(".o-mail-Discuss-content .o-mail-Thread")[0];
|
||||
messageList.scrollTop = messageList.scrollHeight - messageList.clientHeight;
|
||||
await scroll(".o-mail-Discuss-content .o-mail-Thread", "bottom");
|
||||
await contains("hr + span:contains(New messages)");
|
||||
|
||||
await afterNextRender(() => $(".o-mail-Composer-input")[0].focus());
|
||||
$(".o-mail-Composer-input")[0].focus();
|
||||
await contains("hr + span:contains(New messages)", 0);
|
||||
});
|
||||
|
||||
@@ -1654,7 +1645,7 @@ QUnit.test(
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test("restore thread scroll position", async (assert) => {
|
||||
QUnit.test("restore thread scroll position", async () => {
|
||||
const pyEnv = await startServer();
|
||||
const [channelId_1, channelId_2] = pyEnv["discuss.channel"].create([
|
||||
{ name: "Channel1" },
|
||||
@@ -1677,30 +1668,17 @@ QUnit.test("restore thread scroll position", async (assert) => {
|
||||
const { openDiscuss } = await start();
|
||||
openDiscuss(channelId_1);
|
||||
await contains(".o-mail-Message", 25);
|
||||
/**
|
||||
* The nextAnimationFrame is necessary because otherwise useAutoScroll would
|
||||
* set the scroll to bottom after the manually set value from this test.
|
||||
*/
|
||||
await nextAnimationFrame();
|
||||
assert.ok(isScrolledToBottom($(".o-mail-Thread")[0]));
|
||||
$(".o-mail-Thread")[0].scrollTop = 0;
|
||||
await contains(".o-mail-Thread", 1, { scroll: "bottom" });
|
||||
await scroll(".o-mail-Thread", 0);
|
||||
await click("button:contains(Channel2)");
|
||||
await contains(".o-mail-Message", 24);
|
||||
assert.ok(isScrolledToBottom($(".o-mail-Thread")[0]));
|
||||
await contains(".o-mail-Thread", 1, { scroll: "bottom" });
|
||||
await click("button:contains(Channel1)");
|
||||
/**
|
||||
* The nextAnimationFrame is necessary because otherwise the scroll position
|
||||
* would not be restored yet.
|
||||
*/
|
||||
await nextAnimationFrame();
|
||||
assert.strictEqual($(".o-mail-Thread")[0].scrollTop, 0);
|
||||
await contains(".o-mail-Message", 25);
|
||||
await contains(".o-mail-Thread", 1, { scroll: 0 });
|
||||
await click("button:contains(Channel2)");
|
||||
/**
|
||||
* The nextAnimationFrame is necessary because otherwise the scroll position
|
||||
* would not be restored yet.
|
||||
*/
|
||||
await nextAnimationFrame();
|
||||
assert.ok(isScrolledToBottom($(".o-mail-Thread")[0]));
|
||||
await contains(".o-mail-Message", 24);
|
||||
await contains(".o-mail-Thread", 1, { scroll: "bottom" });
|
||||
});
|
||||
|
||||
QUnit.test("Message shows up even if channel data is incomplete", async () => {
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
/* @odoo-module */
|
||||
|
||||
import { PRESENT_THRESHOLD } from "@mail/core/common/thread";
|
||||
import {
|
||||
click,
|
||||
contains,
|
||||
nextAnimationFrame,
|
||||
start,
|
||||
startServer,
|
||||
} from "@mail/../tests/helpers/test_utils";
|
||||
import { click, contains, scroll, start, startServer } from "@mail/../tests/helpers/test_utils";
|
||||
|
||||
import { nextTick, patchWithCleanup } from "@web/../tests/helpers/utils";
|
||||
import { patchWithCleanup } from "@web/../tests/helpers/utils";
|
||||
|
||||
QUnit.module("jump to present");
|
||||
|
||||
@@ -22,11 +16,6 @@ QUnit.test("Basic jump to present when scrolling to outdated messages", async (a
|
||||
});
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({ name: "General" });
|
||||
pyEnv["mail.message"].create({
|
||||
body: "Hello world!",
|
||||
model: "discuss.channel",
|
||||
res_id: channelId,
|
||||
});
|
||||
for (let i = 0; i < 20; i++) {
|
||||
pyEnv["mail.message"].create({
|
||||
body: "Non Empty Body ".repeat(100),
|
||||
@@ -37,24 +26,17 @@ QUnit.test("Basic jump to present when scrolling to outdated messages", async (a
|
||||
}
|
||||
const { openDiscuss } = await start();
|
||||
openDiscuss(channelId);
|
||||
await contains(".o-mail-Message", 20);
|
||||
assert.ok(
|
||||
(await contains(".o-mail-Thread"))[0].scrollHeight > PRESENT_THRESHOLD,
|
||||
"should have enough scroll height to trigger jump to present"
|
||||
);
|
||||
/**
|
||||
* The nextAnimationFrame is necessary because otherwise useAutoScroll would
|
||||
* set the scroll to bottom after the manually set value from this test.
|
||||
*/
|
||||
await nextAnimationFrame();
|
||||
$(".o-mail-Thread").scrollTop(0);
|
||||
await contains(".o-mail-Thread", 1, { scroll: "bottom" });
|
||||
await scroll(".o-mail-Thread", 0);
|
||||
await contains(".o-mail-Thread:contains(You're viewing older messagesJump to Present)");
|
||||
await click(".o-mail-Thread-jumpPresent");
|
||||
await nextTick();
|
||||
await contains(".o-mail-Thread:contains(You're viewing older messagesJump to Present)", 0);
|
||||
assert.ok(
|
||||
$(".o-mail-Thread")[0].scrollHeight - $(".o-mail-Thread")[0].scrollTop <=
|
||||
$(".o-mail-Thread")[0].clientHeight
|
||||
);
|
||||
await contains(".o-mail-Thread", 1, { scroll: "bottom" });
|
||||
});
|
||||
|
||||
QUnit.test("Jump to old reply should prompt jump to presence", async (assert) => {
|
||||
@@ -94,8 +76,9 @@ QUnit.test("Jump to old reply should prompt jump to presence", async (assert) =>
|
||||
});
|
||||
const { openDiscuss } = await start();
|
||||
openDiscuss(channelId);
|
||||
await contains(".o-mail-Message", 30);
|
||||
await click(".o-mail-MessageInReply .cursor-pointer");
|
||||
await nextAnimationFrame(); // wait for scroll into view
|
||||
await contains(".o-mail-Message", 46);
|
||||
assert.isVisible((await contains(".o-mail-Message:contains(Hello world!):eq(0)"))[0]);
|
||||
assert.strictEqual(
|
||||
$(".o-mail-Message-body:contains(Hello world!):eq(0)").text(),
|
||||
@@ -105,8 +88,6 @@ QUnit.test("Jump to old reply should prompt jump to presence", async (assert) =>
|
||||
await contains(".o-mail-Thread:contains(You're viewing older messagesJump to Present)");
|
||||
await click(".o-mail-Thread-jumpPresent");
|
||||
await contains(".o-mail-Thread:contains(You're viewing older messagesJump to Present)", 0);
|
||||
assert.ok(
|
||||
$(".o-mail-Thread")[0].scrollHeight - $(".o-mail-Thread")[0].scrollTop <=
|
||||
$(".o-mail-Thread")[0].clientHeight
|
||||
);
|
||||
await contains(".o-mail-Message", 30);
|
||||
await contains(".o-mail-Thread", 1, { scroll: "bottom" });
|
||||
});
|
||||
|
||||
@@ -599,8 +599,6 @@ export {
|
||||
afterNextRender,
|
||||
dragenterFiles,
|
||||
dropFiles,
|
||||
isScrolledToBottom,
|
||||
isScrolledTo,
|
||||
nextAnimationFrame,
|
||||
nextTick,
|
||||
pasteFiles,
|
||||
@@ -620,6 +618,19 @@ export async function click(selector, { target = document.body } = {}) {
|
||||
await contains(selector, 1, { click: true, target });
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits until exactly one element matching the given selector is present in
|
||||
* `options.target` and then sets its `scrollTop` to the given value.
|
||||
*
|
||||
* @param {string} selector
|
||||
* @param {number|"bottom"} scrollTop
|
||||
* @param {Object} [options={}]
|
||||
* @param {HTMLElement} [options.target=document.body]
|
||||
*/
|
||||
export async function scroll(selector, scrollTop, { target = document.body } = {}) {
|
||||
await contains(selector, 1, { setScroll: scrollTop, target });
|
||||
}
|
||||
|
||||
let hasUsedContainsPositively = false;
|
||||
QUnit.testStart(() => (hasUsedContainsPositively = false));
|
||||
/**
|
||||
@@ -630,12 +641,21 @@ QUnit.testStart(() => (hasUsedContainsPositively = false));
|
||||
* @param {number} [count=1]
|
||||
* @param {Object} [options={}]
|
||||
* @param {boolean} [options.click] if provided, clicks on the found element
|
||||
* @param {number|"bottom"} [options.scroll] if provided, the scrollTop of the found element(s)
|
||||
* must match.
|
||||
* Note: when using one of the scrollTop options, it is advised to ensure the height is not going
|
||||
* to change soon, by checking with a preceding contains that all the expected elements are in DOM.
|
||||
* @param {number|"bottom"} [options.setScroll] if provided, set the scrollTop on the found element
|
||||
* @param {HTMLElement} [options.target=document.body]
|
||||
* @param {string} [options.value] if provided, the input value of the found element(s) must match.
|
||||
* Note: value changes are not observed directly, another mutation must happen to catch them.
|
||||
* @returns {Promise<JQuery<HTMLElement>>}
|
||||
*/
|
||||
export function contains(selector, count = 1, { click, target = document.body, value } = {}) {
|
||||
export function contains(
|
||||
selector,
|
||||
count = 1,
|
||||
{ click, scroll, setScroll, target = document.body, value } = {}
|
||||
) {
|
||||
if (count) {
|
||||
hasUsedContainsPositively = true;
|
||||
} else if (!hasUsedContainsPositively) {
|
||||
@@ -644,10 +664,14 @@ export function contains(selector, count = 1, { click, target = document.body, v
|
||||
);
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
const scrollListeners = new Set();
|
||||
let selectorMessage = `${count} of "${selector}"`;
|
||||
if (value !== undefined) {
|
||||
selectorMessage = `${selectorMessage} with value "${value}"`;
|
||||
}
|
||||
if (scroll !== undefined) {
|
||||
selectorMessage = `${selectorMessage} with scroll "${scroll}"`;
|
||||
}
|
||||
const $res = select();
|
||||
if ($res.length === count) {
|
||||
execute($res, "immediately");
|
||||
@@ -682,9 +706,33 @@ export function contains(selector, count = 1, { click, target = document.body, v
|
||||
reject(new Error(message));
|
||||
}
|
||||
});
|
||||
function onScroll(ev) {
|
||||
const $res = select();
|
||||
if ($res.length === count) {
|
||||
clean();
|
||||
execute($res, "after scroll");
|
||||
}
|
||||
}
|
||||
function select() {
|
||||
const $res = $(target).find(selector);
|
||||
return value === undefined ? $res : $res.filter((i, el) => el.value === value);
|
||||
const $filteredRes = $res.filter(
|
||||
(i, el) =>
|
||||
(value === undefined || el.value === value) &&
|
||||
(scroll === undefined ||
|
||||
(scroll === "bottom" ? isScrolledToBottom(el) : isScrolledTo(el, scroll)))
|
||||
);
|
||||
if (
|
||||
scroll !== undefined &&
|
||||
!scrollListeners.size &&
|
||||
$res.length === count &&
|
||||
$filteredRes.length !== count
|
||||
) {
|
||||
for (const el of $res) {
|
||||
scrollListeners.add(el);
|
||||
el.addEventListener("scroll", onScroll);
|
||||
}
|
||||
}
|
||||
return $filteredRes;
|
||||
}
|
||||
function execute($res, whenMessage) {
|
||||
let message = `Found ${selectorMessage} (${whenMessage})`;
|
||||
@@ -692,12 +740,19 @@ export function contains(selector, count = 1, { click, target = document.body, v
|
||||
message = `${message} and clicked it`;
|
||||
$res[0].click();
|
||||
}
|
||||
if (setScroll !== undefined) {
|
||||
message = `${message} and set scroll to "${setScroll}"`;
|
||||
$res[0].scrollTop = setScroll === "bottom" ? $res[0].scrollHeight : setScroll;
|
||||
}
|
||||
QUnit.assert.ok(true, message);
|
||||
resolve($res);
|
||||
}
|
||||
function clean() {
|
||||
observer.disconnect();
|
||||
clearTimeout(timer);
|
||||
for (const el of scrollListeners) {
|
||||
el.removeEventListener("scroll", onScroll);
|
||||
}
|
||||
done = true;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -287,10 +287,10 @@ QUnit.test("Scroll bar to the top when edit starts", async (assert) => {
|
||||
await click(".o-mail-Message [title='Edit']");
|
||||
const $textarea = await contains(".o-mail-Message .o-mail-Composer-input");
|
||||
assert.ok($textarea[0].scrollHeight > $textarea[0].clientHeight);
|
||||
assert.strictEqual($textarea[0].scrollTop, 0);
|
||||
await contains(".o-mail-Message .o-mail-Composer-input", 1, { scroll: 0 });
|
||||
});
|
||||
|
||||
QUnit.test("mentions are kept when editing message", async (assert) => {
|
||||
QUnit.test("mentions are kept when editing message", async () => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({
|
||||
name: "general",
|
||||
|
||||
@@ -7,9 +7,7 @@ import {
|
||||
contains,
|
||||
dragenterFiles,
|
||||
insertText,
|
||||
isScrolledTo,
|
||||
isScrolledToBottom,
|
||||
nextAnimationFrame,
|
||||
scroll,
|
||||
start,
|
||||
startServer,
|
||||
} from "@mail/../tests/helpers/test_utils";
|
||||
@@ -54,12 +52,9 @@ QUnit.test("load more messages from channel (auto-load on scroll)", async () =>
|
||||
const { openDiscuss } = await start();
|
||||
openDiscuss(channelId);
|
||||
await contains("button:contains(Load More) ~ .o-mail-Message", 30);
|
||||
/**
|
||||
* The nextAnimationFrame is necessary because otherwise useAutoScroll would
|
||||
* set the scroll to bottom after the manually set value from this test.
|
||||
*/
|
||||
await nextAnimationFrame();
|
||||
$(".o-mail-Thread")[0].scrollTop = 0;
|
||||
await contains(".o-mail-Message", 30);
|
||||
await contains(".o-mail-Thread", 1, { scroll: "bottom" });
|
||||
await scroll(".o-mail-Thread", 0);
|
||||
await contains(".o-mail-Message", 60);
|
||||
});
|
||||
|
||||
@@ -100,7 +95,7 @@ QUnit.test("do not show message subject when subject is the same as the thread n
|
||||
await contains(".o-mail-Message:not(:contains(Salutations, voyageur))");
|
||||
});
|
||||
|
||||
QUnit.test("auto-scroll to bottom of thread on load", async (assert) => {
|
||||
QUnit.test("auto-scroll to bottom of thread on load", async () => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({ name: "general" });
|
||||
for (let i = 1; i <= 25; i++) {
|
||||
@@ -113,7 +108,7 @@ QUnit.test("auto-scroll to bottom of thread on load", async (assert) => {
|
||||
const { openDiscuss } = await start();
|
||||
openDiscuss(channelId);
|
||||
await contains(".o-mail-Message", 25);
|
||||
assert.ok(isScrolledToBottom($(".o-mail-Thread")[0]));
|
||||
await contains(".o-mail-Thread", 1, { scroll: "bottom" });
|
||||
});
|
||||
|
||||
QUnit.test("display day separator before first message of the day", async () => {
|
||||
@@ -150,53 +145,42 @@ QUnit.test("do not display day separator if all messages of the day are empty",
|
||||
await contains(".o-mail-Thread-date", 0);
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"scroll position is kept when navigating from one channel to another",
|
||||
async (assert) => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId_1 = pyEnv["discuss.channel"].create({ name: "channel-1" });
|
||||
const channelId_2 = pyEnv["discuss.channel"].create({ name: "channel-2" });
|
||||
// Fill both channels with random messages in order for the scrollbar to
|
||||
// appear.
|
||||
pyEnv["mail.message"].create(
|
||||
Array(40)
|
||||
.fill(0)
|
||||
.map((_, index) => ({
|
||||
body: "Non Empty Body ".repeat(25),
|
||||
message_type: "comment",
|
||||
model: "discuss.channel",
|
||||
res_id: index & 1 ? channelId_1 : channelId_2,
|
||||
}))
|
||||
);
|
||||
const { openDiscuss } = await start();
|
||||
openDiscuss(channelId_1);
|
||||
await contains(".o-mail-Thread");
|
||||
/**
|
||||
* The nextAnimationFrame is necessary because otherwise useAutoScroll would
|
||||
* set the scroll to bottom after the manually set value from this test.
|
||||
*/
|
||||
await nextAnimationFrame();
|
||||
const scrolltop_1 = $(".o-mail-Thread")[0].scrollHeight / 2;
|
||||
$(".o-mail-Thread")[0].scrollTo({ top: scrolltop_1 });
|
||||
await click(".o-mail-DiscussSidebarChannel:contains(channel-2)");
|
||||
await contains(".o-mail-DiscussSidebarChannel:contains(channel-2).o-active");
|
||||
/**
|
||||
* The nextAnimationFrame is necessary because otherwise useAutoScroll would
|
||||
* set the scroll to bottom after the manually set value from this test.
|
||||
*/
|
||||
await nextAnimationFrame();
|
||||
const scrolltop_2 = $(".o-mail-Thread")[0].scrollHeight / 3;
|
||||
$(".o-mail-Thread")[0].scrollTo({ top: scrolltop_2 });
|
||||
await click(".o-mail-DiscussSidebarChannel:contains(channel-1)");
|
||||
await contains(".o-mail-DiscussSidebarChannel:contains(channel-1).o-active");
|
||||
assert.ok(isScrolledTo($(".o-mail-Thread")[0], scrolltop_1));
|
||||
await click(".o-mail-DiscussSidebarChannel:contains(channel-2)");
|
||||
await contains(".o-mail-DiscussSidebarChannel:contains(channel-2).o-active");
|
||||
assert.ok(isScrolledTo($(".o-mail-Thread")[0], scrolltop_2));
|
||||
}
|
||||
);
|
||||
QUnit.test("scroll position is kept when navigating from one channel to another", async () => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId_1 = pyEnv["discuss.channel"].create({ name: "channel-1" });
|
||||
const channelId_2 = pyEnv["discuss.channel"].create({ name: "channel-2" });
|
||||
// Fill both channels with random messages in order for the scrollbar to
|
||||
// appear.
|
||||
pyEnv["mail.message"].create(
|
||||
Array(50)
|
||||
.fill(0)
|
||||
.map((_, index) => ({
|
||||
body: "Non Empty Body ".repeat(25),
|
||||
message_type: "comment",
|
||||
model: "discuss.channel",
|
||||
res_id: index < 20 ? channelId_1 : channelId_2,
|
||||
}))
|
||||
);
|
||||
const { openDiscuss } = await start();
|
||||
openDiscuss(channelId_1);
|
||||
await contains(".o-mail-Message", 20);
|
||||
const scrollValue1 = $(".o-mail-Thread")[0].scrollHeight / 2;
|
||||
await contains(".o-mail-Thread", 1, { scroll: "bottom" });
|
||||
await scroll(".o-mail-Thread", scrollValue1);
|
||||
await click(".o-mail-DiscussSidebarChannel:contains(channel-2)");
|
||||
await contains(".o-mail-Message", 30);
|
||||
const scrollValue2 = $(".o-mail-Thread")[0].scrollHeight / 3;
|
||||
await contains(".o-mail-Thread", 1, { scroll: "bottom" });
|
||||
await scroll(".o-mail-Thread", scrollValue2);
|
||||
await click(".o-mail-DiscussSidebarChannel:contains(channel-1)");
|
||||
await contains(".o-mail-Message", 20);
|
||||
await contains(".o-mail-Thread", 1, { scroll: scrollValue1 });
|
||||
await click(".o-mail-DiscussSidebarChannel:contains(channel-2)");
|
||||
await contains(".o-mail-Message", 30);
|
||||
await contains(".o-mail-Thread", 1, { scroll: scrollValue2 });
|
||||
});
|
||||
|
||||
QUnit.test("thread is still scrolling after scrolling up then to bottom", async (assert) => {
|
||||
QUnit.test("thread is still scrolling after scrolling up then to bottom", async () => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({ name: "channel-1" });
|
||||
pyEnv["mail.message"].create(
|
||||
@@ -211,19 +195,14 @@ QUnit.test("thread is still scrolling after scrolling up then to bottom", async
|
||||
);
|
||||
const { openDiscuss } = await start();
|
||||
openDiscuss(channelId);
|
||||
await contains(".o-mail-Thread");
|
||||
/**
|
||||
* The nextAnimationFrame is necessary because otherwise useAutoScroll would
|
||||
* set the scroll to bottom after the manually set value from this test.
|
||||
*/
|
||||
await nextAnimationFrame();
|
||||
assert.ok(isScrolledToBottom($(".o-mail-Thread")[0]));
|
||||
$(".o-mail-Thread")[0].scrollTo({ top: $(".o-mail-Thread")[0].scrollHeight / 2 });
|
||||
$(".o-mail-Thread")[0].scrollTo({ top: $(".o-mail-Thread")[0].scrollHeight });
|
||||
await contains(".o-mail-Message", 20);
|
||||
await contains(".o-mail-Thread", 1, { scroll: "bottom" });
|
||||
await scroll(".o-mail-Thread", $(".o-mail-Thread")[0].scrollHeight / 2);
|
||||
await scroll(".o-mail-Thread", "bottom");
|
||||
await insertText(".o-mail-Composer-input", "123");
|
||||
await click(".o-mail-Composer-send:not(:disabled)");
|
||||
await contains(".o-mail-Message", 21);
|
||||
assert.ok(isScrolledToBottom($(".o-mail-Thread")[0]));
|
||||
await contains(".o-mail-Thread", 1, { scroll: "bottom" });
|
||||
});
|
||||
|
||||
QUnit.test("mention a channel with space in the name", async () => {
|
||||
@@ -279,17 +258,19 @@ QUnit.test(
|
||||
}
|
||||
},
|
||||
});
|
||||
await contains(".o_menu_systray i[aria-label='Messages']");
|
||||
pyEnv.withUser(userId, () =>
|
||||
env.services.rpc("/mail/message/post", {
|
||||
post_data: { body: "new message", message_type: "comment" },
|
||||
post_data: { body: "Hello!", message_type: "comment" },
|
||||
thread_id: channelId,
|
||||
thread_model: "discuss.channel",
|
||||
})
|
||||
);
|
||||
await contains(".o-mail-Message");
|
||||
assert.verifySteps(["rpc:channel_fetch"]);
|
||||
await contains("hr + span:contains(New messages)");
|
||||
$(".o-mail-Composer-input")[0].focus();
|
||||
await contains("span:contains(New messages)", 0);
|
||||
await contains("hr + span:contains(New messages)", 0);
|
||||
assert.verifySteps(["rpc:set_last_seen_message"]);
|
||||
}
|
||||
);
|
||||
@@ -338,7 +319,7 @@ QUnit.test(
|
||||
|
||||
QUnit.test(
|
||||
"should scroll to bottom on receiving new message if the list is initially scrolled to bottom (asc order)",
|
||||
async (assert) => {
|
||||
async () => {
|
||||
const pyEnv = await startServer();
|
||||
const partnerId = pyEnv["res.partner"].create({ name: "Foreigner partner" });
|
||||
const userId = pyEnv["res.users"].create({ name: "Foreigner user", partner_id: partnerId });
|
||||
@@ -358,25 +339,24 @@ QUnit.test(
|
||||
const { env } = await start();
|
||||
await click(".o_menu_systray i[aria-label='Messages']");
|
||||
await click(".o-mail-NotificationItem");
|
||||
assert.ok(isScrolledToBottom((await contains(".o-mail-Thread"))[0]));
|
||||
|
||||
await contains(".o-mail-Message", 11);
|
||||
await contains(".o-mail-Thread", 1, { scroll: "bottom" });
|
||||
// simulate receiving a message
|
||||
await afterNextRender(() =>
|
||||
pyEnv.withUser(userId, () =>
|
||||
env.services.rpc("/mail/message/post", {
|
||||
post_data: { body: "hello", message_type: "comment" },
|
||||
thread_id: channelId,
|
||||
thread_model: "discuss.channel",
|
||||
})
|
||||
)
|
||||
pyEnv.withUser(userId, () =>
|
||||
env.services.rpc("/mail/message/post", {
|
||||
post_data: { body: "hello", message_type: "comment" },
|
||||
thread_id: channelId,
|
||||
thread_model: "discuss.channel",
|
||||
})
|
||||
);
|
||||
assert.ok(isScrolledToBottom($(".o-mail-Thread")[0]));
|
||||
await contains(".o-mail-Message", 12);
|
||||
await contains(".o-mail-Thread", 1, { scroll: "bottom" });
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test(
|
||||
"should not scroll on receiving new message if the list is initially scrolled anywhere else than bottom (asc order)",
|
||||
async (assert) => {
|
||||
async () => {
|
||||
const pyEnv = await startServer();
|
||||
const partnerId = pyEnv["res.partner"].create({ name: "Foreigner partner" });
|
||||
const userId = pyEnv["res.users"].create({ name: "Foreigner user", partner_id: partnerId });
|
||||
@@ -395,24 +375,20 @@ QUnit.test(
|
||||
}
|
||||
const { env } = await start();
|
||||
await click(".o_menu_systray i[aria-label='Messages']");
|
||||
/**
|
||||
* The afterNextRender is necessary because otherwise useAutoScroll would
|
||||
* set the scroll to bottom after the manually set value from this test.
|
||||
*/
|
||||
await afterNextRender(() => click(".o-mail-NotificationItem"));
|
||||
assert.ok(isScrolledToBottom((await contains(".o-mail-Thread"))[0]));
|
||||
$(".o-mail-Thread").scrollTop(0);
|
||||
await click(".o-mail-NotificationItem");
|
||||
await contains(".o-mail-Message", 11);
|
||||
await contains(".o-mail-Thread", 1, { scroll: "bottom" });
|
||||
await scroll(".o-mail-Thread", 0);
|
||||
// simulate receiving a message
|
||||
await afterNextRender(() =>
|
||||
pyEnv.withUser(userId, () =>
|
||||
env.services.rpc("/mail/message/post", {
|
||||
post_data: { body: "hello", message_type: "comment" },
|
||||
thread_id: channelId,
|
||||
thread_model: "discuss.channel",
|
||||
})
|
||||
)
|
||||
pyEnv.withUser(userId, () =>
|
||||
env.services.rpc("/mail/message/post", {
|
||||
post_data: { body: "hello", message_type: "comment" },
|
||||
thread_id: channelId,
|
||||
thread_model: "discuss.channel",
|
||||
})
|
||||
);
|
||||
assert.strictEqual($(".o-mail-Thread")[0].scrollTop, 0);
|
||||
await contains(".o-mail-Message", 12);
|
||||
await contains(".o-mail-ChatWindow .o-mail-Thread", 1, { scroll: 0 });
|
||||
}
|
||||
);
|
||||
|
||||
@@ -437,7 +413,7 @@ QUnit.test("show empty placeholder when thread contains only empty messages", as
|
||||
|
||||
QUnit.test(
|
||||
"message list with a full page of empty messages should load more messages until there are some non-empty",
|
||||
async (assert) => {
|
||||
async () => {
|
||||
// Technical assumptions :
|
||||
// - /discuss/channel/messages fetching exactly 30 messages,
|
||||
// - empty messages not being displayed
|
||||
@@ -462,40 +438,37 @@ QUnit.test(
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test(
|
||||
"no new messages separator on posting message (some message history)",
|
||||
async (assert) => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({
|
||||
channel_member_ids: [
|
||||
Command.create({ message_unread_counter: 0, partner_id: pyEnv.currentPartnerId }),
|
||||
],
|
||||
channel_type: "channel",
|
||||
name: "General",
|
||||
});
|
||||
const messageId = pyEnv["mail.message"].create({
|
||||
body: "first message",
|
||||
model: "discuss.channel",
|
||||
res_id: channelId,
|
||||
});
|
||||
const [memberId] = pyEnv["discuss.channel.member"].search([
|
||||
["channel_id", "=", channelId],
|
||||
["partner_id", "=", pyEnv.currentPartnerId],
|
||||
]);
|
||||
pyEnv["discuss.channel.member"].write([memberId], { seen_message_id: messageId });
|
||||
const { openDiscuss } = await start();
|
||||
openDiscuss(channelId);
|
||||
await contains(".o-mail-Message");
|
||||
await contains("hr + span:contains(New messages)", 0);
|
||||
QUnit.test("no new messages separator on posting message (some message history)", async () => {
|
||||
const pyEnv = await startServer();
|
||||
const channelId = pyEnv["discuss.channel"].create({
|
||||
channel_member_ids: [
|
||||
Command.create({ message_unread_counter: 0, partner_id: pyEnv.currentPartnerId }),
|
||||
],
|
||||
channel_type: "channel",
|
||||
name: "General",
|
||||
});
|
||||
const messageId = pyEnv["mail.message"].create({
|
||||
body: "first message",
|
||||
model: "discuss.channel",
|
||||
res_id: channelId,
|
||||
});
|
||||
const [memberId] = pyEnv["discuss.channel.member"].search([
|
||||
["channel_id", "=", channelId],
|
||||
["partner_id", "=", pyEnv.currentPartnerId],
|
||||
]);
|
||||
pyEnv["discuss.channel.member"].write([memberId], { seen_message_id: messageId });
|
||||
const { openDiscuss } = await start();
|
||||
openDiscuss(channelId);
|
||||
await contains(".o-mail-Message");
|
||||
await contains("hr + span:contains(New messages)", 0);
|
||||
|
||||
await insertText(".o-mail-Composer-input", "hey!");
|
||||
// need to remove focus from text area to avoid set_last_seen_message
|
||||
(await contains(".o-mail-Composer-send:not(:disabled)"))[0].focus();
|
||||
await click(".o-mail-Composer-send:not(:disabled)");
|
||||
await contains(".o-mail-Message", 2);
|
||||
await contains("hr + span:contains(New messages)", 0);
|
||||
}
|
||||
);
|
||||
await insertText(".o-mail-Composer-input", "hey!");
|
||||
// need to remove focus from text area to avoid set_last_seen_message
|
||||
(await contains(".o-mail-Composer-send:not(:disabled)"))[0].focus();
|
||||
await click(".o-mail-Composer-send:not(:disabled)");
|
||||
await contains(".o-mail-Message", 2);
|
||||
await contains("hr + span:contains(New messages)", 0);
|
||||
});
|
||||
|
||||
QUnit.test("new messages separator on receiving new message [REQUIRE FOCUS]", async () => {
|
||||
patchWithCleanup(transitionConfig, { disabled: true });
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
/* @odoo-module */
|
||||
|
||||
import { patchUiSize, SIZES } from "@mail/../tests/helpers/patch_ui_size";
|
||||
import {
|
||||
click,
|
||||
contains,
|
||||
nextAnimationFrame,
|
||||
start,
|
||||
startServer,
|
||||
} from "@mail/../tests/helpers/test_utils";
|
||||
import { click, contains, scroll, start, startServer } from "@mail/../tests/helpers/test_utils";
|
||||
|
||||
QUnit.module("attachment box");
|
||||
|
||||
@@ -177,9 +171,11 @@ QUnit.test("scroll to attachment box when toggling on", async (assert) => {
|
||||
res_model: "res.partner",
|
||||
views: [[false, "form"]],
|
||||
});
|
||||
$(".o-mail-Chatter").scrollTop(10 * 1000); // to bottom
|
||||
await contains(".o-mail-Message", 30);
|
||||
await scroll(".o-mail-Chatter", "bottom");
|
||||
await click("button[aria-label='Attach files']");
|
||||
await nextAnimationFrame();
|
||||
await contains(".o-mail-AttachmentBox");
|
||||
await contains(".o-mail-Chatter", 1, { scroll: 0 });
|
||||
assert.isVisible($(".o-mail-AttachmentBox"));
|
||||
});
|
||||
|
||||
|
||||
@@ -8,8 +8,7 @@ import {
|
||||
dragenterFiles,
|
||||
dropFiles,
|
||||
insertText,
|
||||
isScrolledTo,
|
||||
nextAnimationFrame,
|
||||
scroll,
|
||||
start,
|
||||
startServer,
|
||||
} from "@mail/../tests/helpers/test_utils";
|
||||
@@ -456,7 +455,7 @@ QUnit.test("should not display subject when subject is the same as the thread na
|
||||
await contains(".o-mail-Message:not(:contains(Salutations, voyageur))");
|
||||
});
|
||||
|
||||
QUnit.test("scroll position is kept when navigating from one record to another", async (assert) => {
|
||||
QUnit.test("scroll position is kept when navigating from one record to another", async () => {
|
||||
patchUiSize({ size: SIZES.XXL });
|
||||
const pyEnv = await startServer();
|
||||
const partnerId_1 = pyEnv["res.partner"].create({ name: "Harry Potter" });
|
||||
@@ -464,48 +463,30 @@ QUnit.test("scroll position is kept when navigating from one record to another",
|
||||
// Fill both channels with random messages in order for the scrollbar to
|
||||
// appear.
|
||||
pyEnv["mail.message"].create(
|
||||
Array(40)
|
||||
Array(50)
|
||||
.fill(0)
|
||||
.map((_, index) => ({
|
||||
body: "Non Empty Body ".repeat(25),
|
||||
model: "res.partner",
|
||||
res_id: index & 1 ? partnerId_1 : partnerId_2,
|
||||
res_id: index < 20 ? partnerId_1 : partnerId_2,
|
||||
}))
|
||||
);
|
||||
const { openFormView } = await start();
|
||||
openFormView("res.partner", partnerId_1);
|
||||
await contains(".o_breadcrumb:contains(Harry Potter)");
|
||||
await contains(".o-mail-Chatter");
|
||||
/**
|
||||
* The nextAnimationFrame is necessary because otherwise useAutoScroll would
|
||||
* set the scroll to bottom after the manually set value from this test.
|
||||
*/
|
||||
await nextAnimationFrame();
|
||||
const scrolltop_1 = $(".o-mail-Chatter")[0].scrollHeight / 2;
|
||||
$(".o-mail-Chatter")[0].scrollTo({ top: scrolltop_1 });
|
||||
await contains(".o-mail-Message", 20);
|
||||
const scrollValue1 = $(".o-mail-Chatter")[0].scrollHeight / 2;
|
||||
await contains(".o-mail-Chatter", 1, { scroll: 0 });
|
||||
await scroll(".o-mail-Chatter", scrollValue1);
|
||||
openFormView("res.partner", partnerId_2);
|
||||
await contains(".o_breadcrumb:contains(Ron Weasley)");
|
||||
/**
|
||||
* The nextAnimationFrame is necessary because otherwise useAutoScroll would
|
||||
* set the scroll to bottom after the manually set value from this test.
|
||||
*/
|
||||
await nextAnimationFrame();
|
||||
const scrolltop_2 = $(".o-mail-Chatter")[0].scrollHeight / 3;
|
||||
$(".o-mail-Chatter")[0].scrollTo({ top: scrolltop_2 });
|
||||
await contains(".o-mail-Message", 30);
|
||||
const scrollValue2 = $(".o-mail-Chatter")[0].scrollHeight / 3;
|
||||
await scroll(".o-mail-Chatter", scrollValue2);
|
||||
openFormView("res.partner", partnerId_1);
|
||||
await contains(".o_breadcrumb:contains(Harry Potter)");
|
||||
/**
|
||||
* The nextAnimationFrame is necessary to give time for scroll to be restored.
|
||||
*/
|
||||
await nextAnimationFrame();
|
||||
assert.ok(isScrolledTo($(".o-mail-Chatter")[0], scrolltop_1));
|
||||
await contains(".o-mail-Message", 20);
|
||||
await contains(".o-mail-Chatter", 1, { scroll: scrollValue1 });
|
||||
openFormView("res.partner", partnerId_2);
|
||||
await contains(".o_breadcrumb:contains(Ron Weasley)");
|
||||
/**
|
||||
* The nextAnimationFrame is necessary to give time for scroll to be restored.
|
||||
*/
|
||||
await nextAnimationFrame();
|
||||
assert.ok(isScrolledTo($(".o-mail-Chatter")[0], scrolltop_2));
|
||||
await contains(".o-mail-Message", 30);
|
||||
await contains(".o-mail-Chatter", 1, { scroll: scrollValue2 });
|
||||
});
|
||||
|
||||
QUnit.test("basic chatter rendering", async () => {
|
||||
|
||||
@@ -242,7 +242,7 @@ QUnit.test("Load 100 followers at once", async () => {
|
||||
await contains(".o-mail-Followers-dropdown span:contains(Load more)", 0);
|
||||
});
|
||||
|
||||
QUnit.test("Load 100 recipients at once", async (assert) => {
|
||||
QUnit.test("Load 100 recipients at once", async () => {
|
||||
const pyEnv = await startServer();
|
||||
const partnerIds = pyEnv["res.partner"].create(
|
||||
[...Array(210).keys()].map((i) => ({
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/* @odoo-module */
|
||||
|
||||
import { patchUiSize, SIZES } from "@mail/../tests/helpers/patch_ui_size";
|
||||
import { click, contains, start, startServer } from "@mail/../tests/helpers/test_utils";
|
||||
import { click, contains, scroll, start, startServer } from "@mail/../tests/helpers/test_utils";
|
||||
|
||||
QUnit.module("Form renderer");
|
||||
|
||||
QUnit.test("Form view not scrolled when switching record", async (assert) => {
|
||||
QUnit.test("Form view not scrolled when switching record", async () => {
|
||||
const pyEnv = await startServer();
|
||||
const [partnerId_1, partnerId_2] = pyEnv["res.partner"].create([
|
||||
{
|
||||
@@ -16,8 +16,9 @@ QUnit.test("Form view not scrolled when switching record", async (assert) => {
|
||||
]);
|
||||
const messages = [...Array(60).keys()].map((id) => {
|
||||
return {
|
||||
body: "not empty",
|
||||
model: "res.partner",
|
||||
res_id: id % 2 ? partnerId_1 : partnerId_2,
|
||||
res_id: id < 29 ? partnerId_1 : partnerId_2,
|
||||
};
|
||||
});
|
||||
pyEnv["mail.message"].create(messages);
|
||||
@@ -43,16 +44,16 @@ QUnit.test("Form view not scrolled when switching record", async (assert) => {
|
||||
},
|
||||
{ resIds: [partnerId_1, partnerId_2] }
|
||||
);
|
||||
await contains(".o_breadcrumb .active:contains(Partner 1)");
|
||||
assert.strictEqual($(".o_content")[0].scrollTop, 0);
|
||||
$(".o_content")[0].scrollTop = 150;
|
||||
|
||||
await contains(".o-mail-Message", 29);
|
||||
await contains(".o_content", 1, { scroll: 0 });
|
||||
await scroll(".o_content", 150);
|
||||
await click(".o_pager_next");
|
||||
await contains(".o_breadcrumb .active:contains(Partner 2)");
|
||||
assert.strictEqual($(".o_content")[0].scrollTop, 0);
|
||||
|
||||
await contains(".o-mail-Message", 30);
|
||||
await contains(".o_content", 1, { scroll: 150 });
|
||||
await scroll(".o_content", 0);
|
||||
await click(".o_pager_previous");
|
||||
assert.strictEqual($(".o_content")[0].scrollTop, 0);
|
||||
await contains(".o-mail-Message", 29);
|
||||
await contains(".o_content", 1, { scroll: 0 });
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
|
||||
Reference in New Issue
Block a user