[FIX] mail: scroll to bottom assertion in Chrome 94+
Since (at least) Chrome 94+, some element's sizing computation returns a slightly different value (in the order of a fraction of a pixel). Sadly, due to rounding, this difference has an impact on exact sizing assertion. As the difference is *really* small, introducing a margin of error (i.e. <= 1px) looks reasonnable ; as implemented in this commit. More specifically as Element.scrollHeight can have either an int or a decimal value (cf. fractional-scaling), this commit allows to property handle both usecases. Reference: https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight#determine_if_an_element_has_been_totally_scrolled closes odoo/odoo#92328 X-original-commit: 93348ae0d0b076fe2dfa094a2d7b3ef74442d390 Related: odoo/enterprise#27790 Signed-off-by: Sébastien Theys (seb) <seb@odoo.com> Signed-off-by: Pierre Paridans (app) <app@odoo.com>
This commit is contained in:
@@ -1003,6 +1003,23 @@ function pasteFiles(el, files) {
|
||||
});
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public: DOM utilities
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Determine if a DOM element has been totally scrolled
|
||||
*
|
||||
* A 1px margin of error is given to accomodate subpixel rounding issues and
|
||||
* Element.scrollHeight value being either int or decimal
|
||||
*
|
||||
* @param {DOM.Element} el
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isScrolledToBottom(el) {
|
||||
return Math.abs(el.scrollHeight - el.clientHeight - el.scrollTop) <= 1;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Export
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -1011,6 +1028,7 @@ export {
|
||||
afterNextRender,
|
||||
dragenterFiles,
|
||||
dropFiles,
|
||||
isScrolledToBottom,
|
||||
nextAnimationFrame,
|
||||
nextTick,
|
||||
pasteFiles,
|
||||
|
||||
@@ -4,6 +4,7 @@ import { makeDeferred } from '@mail/utils/deferred';
|
||||
import { patchUiSize, SIZES } from '@mail/../tests/helpers/patch_ui_size';
|
||||
import {
|
||||
afterNextRender,
|
||||
isScrolledToBottom,
|
||||
nextAnimationFrame,
|
||||
start,
|
||||
startServer,
|
||||
@@ -958,7 +959,7 @@ QUnit.test('chat window: scroll conservation on toggle discuss', async function
|
||||
thread &&
|
||||
thread.model === 'mail.channel' &&
|
||||
thread.id === mailChannelId1 &&
|
||||
scrollTop === messageList.scrollHeight - messageList.clientHeight
|
||||
isScrolledToBottom(messageList)
|
||||
);
|
||||
},
|
||||
});
|
||||
@@ -1694,7 +1695,7 @@ QUnit.test('chat window with a thread: keep scroll position in message list on f
|
||||
thread &&
|
||||
thread.model === 'mail.channel' &&
|
||||
thread.id === mailChannelId1 &&
|
||||
scrollTop === messageList.scrollHeight - messageList.clientHeight
|
||||
isScrolledToBottom(messageList)
|
||||
);
|
||||
},
|
||||
});
|
||||
@@ -1782,9 +1783,8 @@ QUnit.test('chat window should scroll to the newly posted message just after pos
|
||||
)
|
||||
);
|
||||
const messageList = document.querySelector('.o_MessageList');
|
||||
assert.strictEqual(
|
||||
messageList.scrollHeight - messageList.scrollTop,
|
||||
messageList.clientHeight,
|
||||
assert.ok(
|
||||
isScrolledToBottom(messageList),
|
||||
"chat window should scroll to the newly posted message just after posting it"
|
||||
);
|
||||
});
|
||||
@@ -1845,7 +1845,7 @@ QUnit.test('chat window with a thread: keep scroll position in message list on t
|
||||
thread &&
|
||||
thread.model === 'mail.channel' &&
|
||||
thread.id === mailChannelId1 &&
|
||||
scrollTop === messageList.scrollHeight - messageList.clientHeight
|
||||
isScrolledToBottom(messageList)
|
||||
);
|
||||
},
|
||||
});
|
||||
@@ -1915,7 +1915,7 @@ QUnit.test('chat window with a thread: keep scroll position in message list on t
|
||||
thread &&
|
||||
thread.model === 'mail.channel' &&
|
||||
thread.id === mailChannelId1 &&
|
||||
scrollTop === messageList.scrollHeight - messageList.clientHeight
|
||||
isScrolledToBottom(messageList)
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -4,6 +4,7 @@ import BusService from 'bus.BusService';
|
||||
|
||||
import {
|
||||
afterNextRender,
|
||||
isScrolledToBottom,
|
||||
nextAnimationFrame,
|
||||
start,
|
||||
startServer,
|
||||
@@ -1636,7 +1637,7 @@ QUnit.test('auto-scroll to bottom of thread', async function (assert) {
|
||||
thread &&
|
||||
thread.model === 'mail.channel' &&
|
||||
thread.id === mailChannelId1 &&
|
||||
scrollTop === messageList.scrollHeight - messageList.clientHeight
|
||||
isScrolledToBottom(messageList)
|
||||
);
|
||||
},
|
||||
},
|
||||
@@ -1649,9 +1650,8 @@ QUnit.test('auto-scroll to bottom of thread', async function (assert) {
|
||||
"should have 25 messages"
|
||||
);
|
||||
const messageList = document.querySelector(`.o_Discuss_thread .o_ThreadView_messageList`);
|
||||
assert.strictEqual(
|
||||
messageList.scrollTop,
|
||||
messageList.scrollHeight - messageList.clientHeight,
|
||||
assert.ok(
|
||||
isScrolledToBottom(messageList),
|
||||
"should have scrolled to bottom of thread"
|
||||
);
|
||||
});
|
||||
@@ -1684,7 +1684,7 @@ QUnit.test('load more messages from channel (auto-load on scroll)', async functi
|
||||
thread &&
|
||||
thread.model === 'mail.channel' &&
|
||||
thread.id === mailChannelId1 &&
|
||||
scrollTop === messageList.scrollHeight - messageList.clientHeight
|
||||
isScrolledToBottom(messageList)
|
||||
);
|
||||
},
|
||||
},
|
||||
@@ -1764,7 +1764,7 @@ QUnit.test('new messages separator [REQUIRE FOCUS]', async function (assert) {
|
||||
thread &&
|
||||
thread.model === 'mail.channel' &&
|
||||
thread.id === mailChannelId1 &&
|
||||
scrollTop === messageList.scrollHeight - messageList.clientHeight
|
||||
isScrolledToBottom(messageList)
|
||||
);
|
||||
},
|
||||
},
|
||||
@@ -1834,7 +1834,7 @@ QUnit.test('new messages separator [REQUIRE FOCUS]', async function (assert) {
|
||||
thread &&
|
||||
thread.model === 'mail.channel' &&
|
||||
thread.id === mailChannelId1 &&
|
||||
scrollTop === messageList.scrollHeight - messageList.clientHeight
|
||||
isScrolledToBottom(messageList)
|
||||
);
|
||||
},
|
||||
});
|
||||
@@ -1898,9 +1898,8 @@ QUnit.test('restore thread scroll position', async function (assert) {
|
||||
.o_Discuss_thread
|
||||
.o_ThreadView_messageList
|
||||
`);
|
||||
assert.strictEqual(
|
||||
initialMessageList.scrollTop,
|
||||
initialMessageList.scrollHeight - initialMessageList.clientHeight,
|
||||
assert.ok(
|
||||
isScrolledToBottom(initialMessageList),
|
||||
"should have scrolled to bottom of channel 1 initially"
|
||||
);
|
||||
|
||||
@@ -1943,7 +1942,7 @@ QUnit.test('restore thread scroll position', async function (assert) {
|
||||
thread &&
|
||||
thread.model === 'mail.channel' &&
|
||||
thread.id === mailChannelId2 &&
|
||||
scrollTop === messageList.scrollHeight - messageList.clientHeight
|
||||
isScrolledToBottom(messageList)
|
||||
);
|
||||
},
|
||||
});
|
||||
@@ -2006,14 +2005,13 @@ QUnit.test('restore thread scroll position', async function (assert) {
|
||||
thread &&
|
||||
thread.model === 'mail.channel' &&
|
||||
thread.id === mailChannelId2 &&
|
||||
scrollTop === messageList.scrollHeight - messageList.clientHeight
|
||||
isScrolledToBottom(messageList)
|
||||
);
|
||||
},
|
||||
});
|
||||
const messageList = document.querySelector('.o_ThreadView_messageList');
|
||||
assert.strictEqual(
|
||||
messageList.scrollTop,
|
||||
messageList.scrollHeight - messageList.clientHeight,
|
||||
assert.ok(
|
||||
isScrolledToBottom(messageList),
|
||||
"should have recovered scroll position of channel 2 (scroll to bottom)"
|
||||
);
|
||||
});
|
||||
@@ -3472,7 +3470,7 @@ QUnit.test('all messages in "Inbox" in "History" after marked all as read', asyn
|
||||
thread.model === 'mail.box' &&
|
||||
thread.id === 'inbox' &&
|
||||
orderedMessages.length === 30 &&
|
||||
scrollTop === messageList.scrollHeight - messageList.clientHeight
|
||||
isScrolledToBottom(messageList)
|
||||
);
|
||||
},
|
||||
},
|
||||
@@ -3502,7 +3500,7 @@ QUnit.test('all messages in "Inbox" in "History" after marked all as read', asyn
|
||||
thread.model === 'mail.box' &&
|
||||
thread.id === 'history' &&
|
||||
orderedMessages.length === 30 &&
|
||||
scrollTop === messageList.scrollHeight - messageList.clientHeight
|
||||
isScrolledToBottom(messageList)
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -4,6 +4,7 @@ import { insert, insertAndReplace, replace } from '@mail/model/model_field_comma
|
||||
import {
|
||||
afterNextRender,
|
||||
dragenterFiles,
|
||||
isScrolledToBottom,
|
||||
start,
|
||||
startServer,
|
||||
} from '@mail/../tests/helpers/test_utils';
|
||||
@@ -794,9 +795,8 @@ QUnit.test('should scroll to bottom on receiving new message if the list is init
|
||||
predicate: data => threadViewer === data.threadViewer,
|
||||
});
|
||||
const initialMessageList = document.querySelector('.o_ThreadView_messageList');
|
||||
assert.strictEqual(
|
||||
initialMessageList.scrollTop,
|
||||
initialMessageList.scrollHeight - initialMessageList.clientHeight,
|
||||
assert.ok(
|
||||
isScrolledToBottom(initialMessageList),
|
||||
"should have scrolled to bottom of channel 20 initially"
|
||||
);
|
||||
|
||||
@@ -818,9 +818,8 @@ QUnit.test('should scroll to bottom on receiving new message if the list is init
|
||||
predicate: data => threadViewer === data.threadViewer,
|
||||
});
|
||||
const messageList = document.querySelector('.o_ThreadView_messageList');
|
||||
assert.strictEqual(
|
||||
messageList.scrollTop,
|
||||
messageList.scrollHeight - messageList.clientHeight,
|
||||
assert.ok(
|
||||
isScrolledToBottom(messageList),
|
||||
"should scroll to bottom on receiving new message because the list is initially scrolled to bottom"
|
||||
);
|
||||
});
|
||||
@@ -866,9 +865,8 @@ QUnit.test('should not scroll on receiving new message if the list is initially
|
||||
predicate: data => threadViewer === data.threadViewer,
|
||||
});
|
||||
const initialMessageList = document.querySelector('.o_ThreadView_messageList');
|
||||
assert.strictEqual(
|
||||
initialMessageList.scrollTop,
|
||||
initialMessageList.scrollHeight - initialMessageList.clientHeight,
|
||||
assert.ok(
|
||||
isScrolledToBottom(initialMessageList),
|
||||
"should have scrolled to bottom of channel 1 initially"
|
||||
);
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { makeDeferred } from '@mail/utils/deferred';
|
||||
import { patchUiSize, SIZES } from '@mail/../tests/helpers/patch_ui_size';
|
||||
import {
|
||||
afterNextRender,
|
||||
isScrolledToBottom,
|
||||
nextAnimationFrame,
|
||||
start,
|
||||
startServer,
|
||||
@@ -816,9 +817,8 @@ QUnit.test('Form view not scrolled when switching record', async function (asser
|
||||
'scroll'
|
||||
);
|
||||
});
|
||||
assert.strictEqual(
|
||||
controllerContentEl.scrollTop,
|
||||
controllerContentEl.scrollHeight - controllerContentEl.clientHeight,
|
||||
assert.ok(
|
||||
isScrolledToBottom(controllerContentEl),
|
||||
"The controller container should be scrolled to its bottom"
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user