Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions packages/shared/src/components/Feed.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1310,6 +1310,14 @@ describe('Feed logged in', () => {
},
}));

// Opening the post modal now logs a view for every post type (previously
// only some types tracked here); the `pmid` query param above opens the
// modal on mount, so this must be registered before render.
nock('http://localhost:3000')
.post('/graphql', (body: { query?: string }) =>
Boolean(body.query?.includes('mutation ViewPost(')),
)
.reply(200, { data: { viewPost: { _: true } } });
renderComponent();
await waitForNock();
const [first] = await screen.findAllByLabelText('Comments');
Expand Down Expand Up @@ -1414,6 +1422,23 @@ describe('Feed logged in', () => {
});

const [firstPost, secondPost] = defaultFeedPage.edges;
// Opening the post modal now logs a view for every post type; the
// `pmid` query param above opens the modal on mount, and this test then
// navigates through three different post/id combinations, so mock
// persistently (registered before render) rather than one-off per post.
// Counts calls so the test can wait for the final navigation's view to
// actually fire before finishing — otherwise the request can resolve
// after this test ends and spuriously fail an unrelated later test.
let viewPostCallCount = 0;
nock('http://localhost:3000')
.persist()
.post('/graphql', (body: { query?: string }) =>
Boolean(body.query?.includes('mutation ViewPost(')),
)
.reply(200, () => {
viewPostCallCount += 1;
return { data: { viewPost: { _: true } } };
});
renderComponent();
await waitForNock();

Expand Down Expand Up @@ -1441,9 +1466,24 @@ describe('Feed logged in', () => {
fireEvent.click(previous);
const firstTitle = await screen.findByTestId('post-modal-title');
expect(firstTitle).toHaveTextContent(getPostTitle(firstPost.node, 'first'));

await waitFor(() => expect(viewPostCallCount).toBe(3));
});

it('should report irrelevant tags', async () => {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewer note: this router-mock reset (and the one in the next test) fixes a pre-existing test-isolation gap rather than anything introduced here. The two preceding tests set a pmid router query via mockImplementation, which jest.clearAllMocks() doesn't reset, so these tests were silently rendering with a leftover pmid and auto-opening the post modal all along. That was harmless while articles didn't track views; once they do, the modal fires an unmocked viewPost and nock fails the test — which is how the latent bleed-over surfaced.

// The two preceding tests set a `pmid` router query to drive their own
// post-modal navigation, and `jest.clearAllMocks()` in `beforeEach` does
// not reset a `mockImplementation`. Reset it here so this test doesn't
// inherit that `pmid` and inadvertently auto-open the post modal (which,
// now that opening a post logs a view for every type, would otherwise
// fire an unmocked `viewPost` mutation).
jest.mocked(useRouter).mockImplementation(
() =>
({
pathname: '/',
query: {},
} as unknown as NextRouter),
);
let mutationCalled = false;
renderComponent([
createFeedMock({
Expand Down Expand Up @@ -1501,6 +1541,15 @@ describe('Feed logged in', () => {
});

it('should keep selected irrelevant tags when reason changes', async () => {
// See comment in the preceding test: reset the router mock so this test
// doesn't inherit a leftover `pmid` query and auto-open the post modal.
jest.mocked(useRouter).mockImplementation(
() =>
({
pathname: '/',
query: {},
} as unknown as NextRouter),
);
renderComponent([
createFeedMock({
pageInfo: defaultFeedPage.pageInfo,
Expand Down
2 changes: 1 addition & 1 deletion packages/shared/src/components/post/PostContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ export function PostContentRaw({
hideSubscribeAction,
};

useTrackPostView({ post, shouldTrack: isVideoType });
useTrackPostView({ post });

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one-liner is the actual behavior change of the PR: PostContent renders article (and video/liveRoom) posts, and the shouldTrack: isVideoType gate meant articles never sent viewPost on open — they only got a view server-side when the user clicked "Read article" (which routes through api.daily.dev/r/:postId).

Now opening the page or modal counts, same as freeform/welcome/share/collection/poll/brief/digest already do. The click-through path still exists unchanged; the backend dedups the two within a one-week window per (user, post) — see dailydotdev/daily-api#4026.


const postMainColumn = (
<PostContainer
Expand Down
12 changes: 1 addition & 11 deletions packages/shared/src/components/post/focus/PostFocusCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,6 @@ const PostCodeSnippets = dynamic(() =>

export type FocusCardLeftVariant = 'lean' | 'rich';

const viewTrackedPostTypes = [
PostType.Share,
PostType.Collection,
PostType.Freeform,
PostType.Welcome,
];

interface PostFocusCardProps {
post: Post;
origin: PostOrigin;
Expand Down Expand Up @@ -289,10 +282,7 @@ export const PostFocusCard = ({
const [isVideoExpanded, setIsVideoExpanded] = useState(false);
const readHref = getReadArticleHref(post);

useTrackPostView({
post,
shouldTrack: isVideoPost(post) || viewTrackedPostTypes.includes(post.type),
});
useTrackPostView({ post });

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same change for the layout-v2 focus card, which duplicated the article exclusion via its own viewTrackedPostTypes allowlist (deleted above — it's dead once tracking is unconditional). isVideoPost/PostType imports stay because both are still used elsewhere in this file.


useEffect(() => {
if (!isVideoType || isVideoExpanded) {
Expand Down
44 changes: 44 additions & 0 deletions packages/webapp/__tests__/PostPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,17 @@ function renderPost(
},
result: () => ({ data: { _: true } }),
},
// Opening any post (including articles) now logs a view on mount; tests

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Default catch-all mock so the ~40 existing article-page tests don't each need to care about the new on-mount mutation; the new should log post view on mount test below asserts the behavior explicitly. Registered last so any test-supplied mock for the same request takes precedence.

// that don't assert on this explicitly still need it mocked so the
// mutation doesn't fire an unmatched request against nock. Registered
// last so a test-supplied `mocks` entry for the same request wins.
{
request: {
query: VIEW_POST_MUTATION,
variables: { id: defaultProps.id },
},
result: () => ({ data: { viewPost: { _: true } } }),
},
];

defaultMocks.forEach(mockGraphQL);
Expand Down Expand Up @@ -1035,6 +1046,39 @@ describe('article', () => {
}),
);
});

// Unified view logging: opening an article now logs a view on mount too,
// matching every other post type, rather than only on "Read article" click.
it('should log post view on mount', async () => {
let viewPostMutationCalled = false;
renderPost({}, [
createPostMock(),
createCommentsMock(),
{
request: {
query: VIEW_POST_MUTATION,
variables: {
id: '0e4005b2d3cf191f8c44c2718a457a1e',
},
},
result: () => {
viewPostMutationCalled = true;

return {
data: {
viewPost: {
_: true,
},
},
};
},
},
]);

await waitFor(() => {
expect(viewPostMutationCalled).toBe(true);
});
});
});

describe('post redesign', () => {
Expand Down
Loading