Skip to content

Commit dbedeec

Browse files
authored
🐛 Fixed comment counts for lazy loaded posts (#28545)
ref https://linear.app/ghost/issue/ONC-1832/comment-counts-missing-for-post-cards-on-archive-pages Their comment count markers were detected, but the mutation observer created a debounced fetch without invoking it, so the new counts were never requested.
1 parent 27ceea3 commit dbedeec

2 files changed

Lines changed: 89 additions & 1 deletion

File tree

ghost/core/core/frontend/src/comment-counts/comment-counts.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,13 +88,15 @@
8888
renderCounts();
8989
};
9090

91+
const debouncedFetchCounts = debounce(fetchCounts);
92+
9193
const countElemObserver = new MutationObserver((mutationsList) => {
9294
mutationsList.forEach((mutation) => {
9395
mutation.addedNodes.forEach((addedNode) => {
9496
addIdsFromElement(addedNode);
95-
debounce(fetchCounts);
9697
});
9798
});
99+
debouncedFetchCounts();
98100
});
99101

100102
countElemObserver.observe(document.body, {subtree: true, childList: true});
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/* global expect, vi */
2+
3+
const fs = require('fs');
4+
const path = require('path');
5+
const {createBrowserEnvironment, loadScript} = require('../../../utils/browser-test-utils');
6+
7+
describe('comment-counts.js', function () {
8+
let env;
9+
let scriptContent;
10+
11+
const countMarker = id => `
12+
<script
13+
data-ghost-comment-count="${id}"
14+
data-ghost-comment-count-empty="No comments"
15+
data-ghost-comment-count-singular="comment"
16+
data-ghost-comment-count-plural="comments"
17+
data-ghost-comment-count-tag="span"
18+
data-ghost-comment-count-class-name="comment-count"
19+
data-ghost-comment-count-autowrap="true"
20+
></script>
21+
`;
22+
23+
beforeAll(function () {
24+
scriptContent = fs.readFileSync(path.join(__dirname, '../../../../core/frontend/src/comment-counts/comment-counts.js'), 'utf8');
25+
});
26+
27+
afterEach(function () {
28+
vi.restoreAllMocks();
29+
30+
if (env) {
31+
env.dom.window.close();
32+
env = null;
33+
}
34+
});
35+
36+
function setupEnvironment() {
37+
env = createBrowserEnvironment({
38+
html: `<!DOCTYPE html><html><body><article class="post-card">${countMarker('post-1')}</article></body></html>`
39+
});
40+
41+
env.window.fetch = vi.fn(async (url) => {
42+
const counts = {
43+
'https://example.com/members/api/comments/counts/?ids=post-1': {'post-1': 1},
44+
'https://example.com/members/api/comments/counts/?ids=post-2': {'post-2': 2}
45+
};
46+
47+
return {
48+
status: 200,
49+
json: async () => counts[url]
50+
};
51+
});
52+
}
53+
54+
async function flushAsyncWork() {
55+
await Promise.resolve();
56+
await new Promise((resolve) => {
57+
setImmediate(resolve);
58+
});
59+
}
60+
61+
it('fetches and renders counts for post cards added after initial page load', async function () {
62+
setupEnvironment();
63+
64+
loadScript(env, scriptContent, {
65+
dataAttributes: {
66+
'ghost-comments-counts-api': 'https://example.com/members/api/comments/counts/'
67+
}
68+
});
69+
70+
await flushAsyncWork();
71+
expect(env.document.body.textContent).toMatch(/1 comment/);
72+
73+
const article = env.document.createElement('article');
74+
article.className = 'post-card';
75+
article.innerHTML = countMarker('post-2');
76+
env.document.body.appendChild(article);
77+
78+
await new Promise((resolve) => {
79+
env.window.setTimeout(resolve, 150);
80+
});
81+
await flushAsyncWork();
82+
83+
expect(env.window.fetch).toHaveBeenCalledWith('https://example.com/members/api/comments/counts/?ids=post-2', expect.any(Object));
84+
expect(article.textContent).toMatch(/2 comments/);
85+
});
86+
});

0 commit comments

Comments
 (0)