diff --git a/src/pages/HomePage.vue b/src/pages/HomePage.vue index 20a95d29..896b8c0d 100644 --- a/src/pages/HomePage.vue +++ b/src/pages/HomePage.vue @@ -5,7 +5,7 @@
-
+
@@ -37,6 +37,10 @@
+
+ +
+
@@ -56,6 +60,7 @@ import WidgetSponsorPartial from '@partials/WidgetSponsorPartial.vue'; import FeaturedProjectsPartial from '@partials/FeaturedProjectsPartial.vue'; import WidgetSkillsSkeletonPartial from '@partials/WidgetSkillsSkeletonPartial.vue'; import WidgetOullinPartial from '@partials/WidgetOullinPartial.vue'; +import BackToTopLink from '@partials/BackToTopLink.vue'; import { onMounted, ref } from 'vue'; import { useApiStore } from '@api/store.ts'; diff --git a/src/pages/PostPage.vue b/src/pages/PostPage.vue index d0c8c080..915ffb6f 100644 --- a/src/pages/PostPage.vue +++ b/src/pages/PostPage.vue @@ -90,7 +90,7 @@ -

{{ post.title }}

+

{{ post.title }}

@@ -107,11 +107,16 @@
+
+ +
+ @@ -134,6 +139,8 @@ import SideNavPartial from '@partials/SideNavPartial.vue'; import type { PostResponse } from '@api/response/index.ts'; import { siteUrlFor, useSeoFromPost } from '@/support/seo'; import WidgetSponsorPartial from '@partials/WidgetSponsorPartial.vue'; +import WidgetSocialPartial from '@partials/WidgetSocialPartial.vue'; +import BackToTopLink from '@partials/BackToTopLink.vue'; import { onMounted, ref, computed, watch, nextTick, watchEffect } from 'vue'; import { initializeHighlighter, renderMarkdown } from '@/support/markdown.ts'; import CoverImageLoader from '@components/CoverImageLoader.vue'; diff --git a/src/partials/RecommendationPartial.vue b/src/partials/RecommendationPartial.vue index aa0e4e64..b800baa9 100644 --- a/src/partials/RecommendationPartial.vue +++ b/src/partials/RecommendationPartial.vue @@ -16,7 +16,7 @@
{{ item.person.full_name }}
{{ item.person.company }}
-
+
{{ item.person.designation }}
diff --git a/tests/pages/HomePage.test.ts b/tests/pages/HomePage.test.ts index 182eb807..6308a1f5 100644 --- a/tests/pages/HomePage.test.ts +++ b/tests/pages/HomePage.test.ts @@ -71,4 +71,27 @@ describe('HomePage', () => { const { debugError } = await import('@api/http-error.ts'); expect(debugError).toHaveBeenCalledWith(error); }); + + it('renders a back to top link targeting the home container', async () => { + const wrapper = mount(HomePage, { + global: { + stubs: { + SideNavPartial: true, + HeaderPartial: true, + HeroPartial: true, + FooterPartial: true, + ArticlesListPartial: true, + FeaturedProjectsPartial: true, + TalksPartial: true, + WidgetSponsorPartial: true, + WidgetSkillsPartial: true, + }, + }, + }); + + await flushPromises(); + + const backToTopLink = wrapper.find('a[href="#home-top"]'); + expect(backToTopLink.exists()).toBe(true); + }); }); diff --git a/tests/pages/PostPage.test.ts b/tests/pages/PostPage.test.ts index 98de0b3f..9438f0c9 100644 --- a/tests/pages/PostPage.test.ts +++ b/tests/pages/PostPage.test.ts @@ -52,24 +52,28 @@ vi.mock('@/public.ts', () => ({ getReadingTime: () => '', })); +const mountComponent = () => + mount(PostPage, { + global: { + stubs: { + SideNavPartial: true, + HeaderPartial: true, + FooterPartial: true, + WidgetSponsorPartial: true, + WidgetSocialPartial: true, + WidgetSkillsPartial: true, + RouterLink: { template: '' }, + }, + }, + }); + describe('PostPage', () => { beforeEach(() => { vi.clearAllMocks(); }); it('fetches post on mount', async () => { - const wrapper = mount(PostPage, { - global: { - stubs: { - SideNavPartial: true, - HeaderPartial: true, - FooterPartial: true, - WidgetSponsorPartial: true, - WidgetSkillsPartial: true, - RouterLink: { template: '' }, - }, - }, - }); + const wrapper = mountComponent(); const skeleton = wrapper.find('[data-testid="post-page-skeleton"]'); expect(skeleton.exists()).toBe(true); expect(skeleton.classes()).toContain('min-h-[25rem]'); @@ -80,18 +84,7 @@ describe('PostPage', () => { }); it('initializes highlight.js on mount', async () => { - const wrapper = mount(PostPage, { - global: { - stubs: { - SideNavPartial: true, - HeaderPartial: true, - FooterPartial: true, - WidgetSponsorPartial: true, - WidgetSkillsPartial: true, - RouterLink: { template: '' }, - }, - }, - }); + const wrapper = mountComponent(); await flushPromises(); const highlightCore = await import('highlight.js/lib/core'); expect(initializeHighlighter).toHaveBeenCalledWith(highlightCore.default); @@ -101,18 +94,7 @@ describe('PostPage', () => { it('processes markdown content', async () => { const DOMPurify = await import('dompurify'); - const wrapper = mount(PostPage, { - global: { - stubs: { - SideNavPartial: true, - HeaderPartial: true, - FooterPartial: true, - WidgetSponsorPartial: true, - WidgetSkillsPartial: true, - RouterLink: { template: '' }, - }, - }, - }); + const wrapper = mountComponent(); await flushPromises(); expect(renderMarkdown).toHaveBeenCalledWith(post.content); expect(DOMPurify.default.sanitize).toHaveBeenCalled(); @@ -122,22 +104,37 @@ describe('PostPage', () => { it('handles post errors gracefully', async () => { const error = new Error('fail'); getPost.mockRejectedValueOnce(error); - const wrapper = mount(PostPage, { - global: { - stubs: { - SideNavPartial: true, - HeaderPartial: true, - FooterPartial: true, - WidgetSponsorPartial: true, - WidgetSkillsPartial: true, - RouterLink: { template: '' }, - }, - }, - }); + const wrapper = mountComponent(); await flushPromises(); const { debugError } = await import('@api/http-error.ts'); expect(debugError).toHaveBeenCalledWith(error); expect(wrapper.find('[data-testid="post-page-skeleton"]').exists()).toBe(false); expect(wrapper.text()).toContain("We couldn't load this post."); }); + + it('renders the follow widget above the sponsor widget', async () => { + const wrapper = mountComponent(); + + await flushPromises(); + + const aside = wrapper.find('aside'); + expect(aside.exists()).toBe(true); + + const container = aside.find('div'); + expect(container.exists()).toBe(true); + + const children = container.element.children; + expect(children.length).toBeGreaterThanOrEqual(2); + expect(children[0].tagName).toBe('WIDGET-SOCIAL-PARTIAL-STUB'); + expect(children[1].tagName).toBe('WIDGET-SPONSOR-PARTIAL-STUB'); + }); + + it('renders a back to top link targeting the post header', async () => { + const wrapper = mountComponent(); + + await flushPromises(); + + const backToTopLink = wrapper.find('a[href="#post-top"]'); + expect(backToTopLink.exists()).toBe(true); + }); });