mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
272 lines
8.9 KiB
TypeScript
272 lines
8.9 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
import {
|
|
getFeedPostHeightEstimate,
|
|
getReplyHeightEstimates,
|
|
getReplyItemSizeFromElement,
|
|
resolveCatalogVirtualizationMode,
|
|
resolveFeedVirtualizationMode,
|
|
resolveReplyVirtualizationMode,
|
|
} from '../pretext-height-estimates';
|
|
import { EXPANDED_MEDIA_DATA_ATTRIBUTE } from '../measurement-attributes';
|
|
|
|
describe('pretext-height-estimates', () => {
|
|
beforeEach(() => {
|
|
Object.defineProperty(HTMLCanvasElement.prototype, 'getContext', {
|
|
configurable: true,
|
|
value: vi.fn(() => ({
|
|
font: '',
|
|
measureText: vi.fn((text: string) => ({
|
|
actualBoundingBoxAscent: 10,
|
|
actualBoundingBoxDescent: 3,
|
|
fontBoundingBoxAscent: 10,
|
|
fontBoundingBoxDescent: 3,
|
|
width: text.length * 7,
|
|
})),
|
|
})),
|
|
writable: true,
|
|
});
|
|
});
|
|
|
|
it('parses reply virtualization mode from search params', () => {
|
|
expect(resolveReplyVirtualizationMode('?pretextReplies=item-size')).toBe('item-size');
|
|
expect(resolveReplyVirtualizationMode(new URLSearchParams('pretextReplies=off'))).toBe('off');
|
|
expect(resolveReplyVirtualizationMode('?pretextReplies=unknown')).toBe('item-size');
|
|
});
|
|
|
|
it('parses feed virtualization mode from search params', () => {
|
|
expect(resolveFeedVirtualizationMode('?pretextFeed=item-size')).toBe('item-size');
|
|
expect(resolveFeedVirtualizationMode(new URLSearchParams('pretextFeed=off'))).toBe('off');
|
|
expect(resolveFeedVirtualizationMode('?pretextFeed=unknown')).toBe('off');
|
|
});
|
|
|
|
it('parses catalog virtualization mode from search params', () => {
|
|
expect(resolveCatalogVirtualizationMode('?pretextCatalog=item-size')).toBe('item-size');
|
|
expect(resolveCatalogVirtualizationMode(new URLSearchParams('pretextCatalog=off'))).toBe('off');
|
|
expect(resolveCatalogVirtualizationMode('?pretextCatalog=unknown')).toBe('off');
|
|
});
|
|
|
|
it('falls back to hash-fragment query params for hash-routed URLs', () => {
|
|
const previousHref = window.location.href;
|
|
window.history.replaceState({}, '', '/#/mu?pretextFeed=item-size&pretextReplies=off');
|
|
|
|
expect(resolveFeedVirtualizationMode(window.location.search)).toBe('item-size');
|
|
expect(resolveReplyVirtualizationMode(window.location.search)).toBe('off');
|
|
expect(resolveCatalogVirtualizationMode(window.location.search)).toBe('off');
|
|
|
|
window.history.replaceState({}, '', previousHref);
|
|
});
|
|
|
|
it('prefers cached Pretext height when itemSize reads a reply row', () => {
|
|
const element = document.createElement('div');
|
|
Object.defineProperty(element, 'offsetHeight', { configurable: true, value: 321 });
|
|
Object.defineProperty(element, 'offsetWidth', { configurable: true, value: 654 });
|
|
element.dataset.pretextHeight = '123';
|
|
|
|
expect(getReplyItemSizeFromElement(element, 'offsetHeight')).toBe(123);
|
|
expect(getReplyItemSizeFromElement(element, 'offsetWidth')).toBe(654);
|
|
|
|
delete element.dataset.pretextHeight;
|
|
expect(getReplyItemSizeFromElement(element, 'offsetHeight')).toBe(321);
|
|
});
|
|
|
|
it('falls back to a nested Pretext height when the wrapper has none', () => {
|
|
const element = document.createElement('div');
|
|
const child = document.createElement('div');
|
|
|
|
Object.defineProperty(element, 'offsetHeight', { configurable: true, value: 321 });
|
|
Object.defineProperty(element, 'offsetWidth', { configurable: true, value: 654 });
|
|
|
|
child.dataset.pretextHeight = '222';
|
|
element.appendChild(child);
|
|
|
|
expect(getReplyItemSizeFromElement(element, 'offsetHeight')).toBe(222);
|
|
});
|
|
|
|
it('uses live DOM height when expanded media is inside an estimated item', () => {
|
|
const element = document.createElement('div');
|
|
const child = document.createElement('div');
|
|
|
|
Object.defineProperty(element, 'offsetHeight', { configurable: true, value: 421 });
|
|
element.dataset.pretextHeight = '123';
|
|
child.setAttribute(EXPANDED_MEDIA_DATA_ATTRIBUTE, 'true');
|
|
element.appendChild(child);
|
|
|
|
expect(getReplyItemSizeFromElement(element, 'offsetHeight')).toBe(421);
|
|
});
|
|
|
|
it('adds desktop board-label height for multiboard text posts without media', () => {
|
|
const metrics = {
|
|
abbrFontSizePx: 13,
|
|
bodyFontFamily: 'Arial, Helvetica, sans-serif',
|
|
bodyFontSizePx: 13,
|
|
mobileContentFontSizePx: 15,
|
|
};
|
|
const post = {
|
|
cid: 'post-1',
|
|
communityAddress: 'music-posting.eth',
|
|
content: 'A short desktop board post',
|
|
postCid: 'post-1',
|
|
state: 'succeeded',
|
|
};
|
|
|
|
const withoutBoardLabel = getFeedPostHeightEstimate({
|
|
isMobile: false,
|
|
metrics,
|
|
post,
|
|
previewReplies: [],
|
|
windowWidth: 1280,
|
|
});
|
|
const withBoardLabel = getFeedPostHeightEstimate({
|
|
isMobile: false,
|
|
metrics,
|
|
post,
|
|
previewReplies: [],
|
|
showBoardLabel: true,
|
|
windowWidth: 1280,
|
|
});
|
|
|
|
expect(withBoardLabel).toBeGreaterThan(withoutBoardLabel);
|
|
});
|
|
|
|
it('accounts for the board-view long-comment notice on truncated desktop posts', () => {
|
|
const metrics = {
|
|
abbrFontSizePx: 13,
|
|
bodyFontFamily: 'Arial, Helvetica, sans-serif',
|
|
bodyFontSizePx: 13,
|
|
mobileContentFontSizePx: 15,
|
|
};
|
|
const visiblePrefix = 'word '.repeat(200);
|
|
const shortPost = {
|
|
cid: 'post-short',
|
|
communityAddress: 'music-posting.eth',
|
|
content: visiblePrefix,
|
|
postCid: 'post-short',
|
|
state: 'succeeded',
|
|
};
|
|
const truncatedPost = {
|
|
...shortPost,
|
|
cid: 'post-long',
|
|
content: `${visiblePrefix}${'overflow '.repeat(40)}`,
|
|
postCid: 'post-long',
|
|
};
|
|
|
|
const shortEstimate = getFeedPostHeightEstimate({
|
|
isMobile: false,
|
|
metrics,
|
|
post: shortPost,
|
|
previewReplies: [],
|
|
windowWidth: 1280,
|
|
});
|
|
const truncatedEstimate = getFeedPostHeightEstimate({
|
|
isMobile: false,
|
|
metrics,
|
|
post: truncatedPost,
|
|
previewReplies: [],
|
|
windowWidth: 1280,
|
|
});
|
|
|
|
expect(truncatedEstimate).toBeGreaterThan(shortEstimate);
|
|
});
|
|
|
|
it('adds desktop preview reply estimates without subtracting a per-reply calibration', () => {
|
|
const metrics = {
|
|
abbrFontSizePx: 13,
|
|
bodyFontFamily: 'Arial, Helvetica, sans-serif',
|
|
bodyFontSizePx: 13,
|
|
mobileContentFontSizePx: 15,
|
|
};
|
|
const post = {
|
|
cid: 'post-preview-parent',
|
|
communityAddress: 'music-posting.eth',
|
|
content: 'A longer desktop board post '.repeat(24),
|
|
postCid: 'post-preview-parent',
|
|
state: 'succeeded',
|
|
};
|
|
const previewReply = {
|
|
cid: 'post-preview-reply',
|
|
communityAddress: 'music-posting.eth',
|
|
content: 'A preview reply',
|
|
parentCid: 'post-preview-parent',
|
|
postCid: 'post-preview-parent',
|
|
state: 'succeeded',
|
|
};
|
|
|
|
const previewEstimate = getReplyHeightEstimates({
|
|
isMobile: false,
|
|
metrics,
|
|
replies: [previewReply],
|
|
windowWidth: 1280,
|
|
})[0];
|
|
|
|
const withoutPreview = getFeedPostHeightEstimate({
|
|
isMobile: false,
|
|
metrics,
|
|
post,
|
|
previewReplies: [],
|
|
windowWidth: 1280,
|
|
});
|
|
const withPreview = getFeedPostHeightEstimate({
|
|
isMobile: false,
|
|
metrics,
|
|
post,
|
|
previewReplies: [previewReply],
|
|
previewReplyEstimates: [previewEstimate],
|
|
windowWidth: 1280,
|
|
});
|
|
|
|
expect(withPreview - withoutPreview).toBe(previewEstimate);
|
|
});
|
|
|
|
it('keeps most mobile preview-reply height on board cards', () => {
|
|
const metrics = {
|
|
abbrFontSizePx: 13,
|
|
bodyFontFamily: 'Arial, Helvetica, sans-serif',
|
|
bodyFontSizePx: 13,
|
|
mobileContentFontSizePx: 15,
|
|
};
|
|
const post = {
|
|
cid: 'post-mobile-preview-parent',
|
|
communityAddress: 'music-posting.eth',
|
|
content: 'A mobile board post with preview replies',
|
|
link: 'https://example.com/image.jpg',
|
|
linkHeight: 720,
|
|
linkWidth: 1280,
|
|
postCid: 'post-mobile-preview-parent',
|
|
state: 'succeeded',
|
|
};
|
|
const previewReplies = Array.from({ length: 5 }, (_, index) => ({
|
|
cid: `post-mobile-preview-reply-${index}`,
|
|
communityAddress: 'music-posting.eth',
|
|
content: `Preview reply ${index} ${'text '.repeat(index + 1)}`,
|
|
parentCid: 'post-mobile-preview-parent',
|
|
postCid: 'post-mobile-preview-parent',
|
|
state: 'succeeded',
|
|
}));
|
|
|
|
const previewEstimates = getReplyHeightEstimates({
|
|
isMobile: true,
|
|
metrics,
|
|
replies: previewReplies,
|
|
windowWidth: 393,
|
|
});
|
|
const withoutPreview = getFeedPostHeightEstimate({
|
|
isMobile: true,
|
|
metrics,
|
|
post,
|
|
previewReplies: [],
|
|
windowWidth: 393,
|
|
});
|
|
const withPreview = getFeedPostHeightEstimate({
|
|
isMobile: true,
|
|
metrics,
|
|
post,
|
|
previewReplies,
|
|
previewReplyEstimates: previewEstimates,
|
|
windowWidth: 393,
|
|
});
|
|
|
|
expect(withPreview).toBeGreaterThan(withoutPreview + 300);
|
|
expect(withPreview - withoutPreview).toBeGreaterThan(previewEstimates.reduce((sum, value) => sum + value, 0) - 220);
|
|
});
|
|
});
|