diff --git a/src/components/Footer/PdapFooter.vue b/src/components/Footer/PdapFooter.vue index 715ca7f..d60fccf 100644 --- a/src/components/Footer/PdapFooter.vue +++ b/src/components/Footer/PdapFooter.vue @@ -1,166 +1,161 @@ - - diff --git a/src/components/Footer/__snapshots__/footer.spec.ts.snap b/src/components/Footer/__snapshots__/footer.spec.ts.snap index 46fde8a..631db5c 100644 --- a/src/components/Footer/__snapshots__/footer.spec.ts.snap +++ b/src/components/Footer/__snapshots__/footer.spec.ts.snap @@ -1,43 +1,76 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`Footer component > Renders a footer 1`] = ` - `; diff --git a/src/components/Footer/constants.ts b/src/components/Footer/constants.ts new file mode 100644 index 0000000..4253d3d --- /dev/null +++ b/src/components/Footer/constants.ts @@ -0,0 +1,11 @@ +/** + * Icons for particular footer links + */ +export const FOOTER_LINK_ICONS = { + GITHUB: 'github', + LINKEDIN: 'linkedin', + DISCORD: 'discord', + JOBS: 'jobs', + NEWSLETTER: 'newsletter', + DOCS: 'docs', +} as const; diff --git a/src/components/Footer/footer.spec.ts b/src/components/Footer/footer.spec.ts index 11703c9..b05db62 100644 --- a/src/components/Footer/footer.spec.ts +++ b/src/components/Footer/footer.spec.ts @@ -1,62 +1,130 @@ -// Component import PdapFooter from './PdapFooter.vue'; - -// Utils import { RouterLinkStub, mount } from '@vue/test-utils'; import { describe, expect, test } from 'vitest'; +const mockFundraisingData = { + raised: 5000, + goal: 10000, +}; + const base = { + props: { + fundraisingData: mockFundraisingData, + }, stubs: { RouterLink: RouterLinkStub, + FontAwesomeIcon: true, }, }; -// Test describe('Footer component', () => { // Render test('Renders a footer', () => { const wrapper = mount(PdapFooter, base); - - expect(wrapper.find('.pdap-footer').exists()).toBe(true); - expect(wrapper.find('.pdap-footer-social-links').exists()).toBe(true); - expect(wrapper.classes()).toContain('pdap-footer'); + expect(wrapper.find('footer').exists()).toBe(true); expect(wrapper.html()).toMatchSnapshot(); }); - // Props - // Props - logo src - test('Renders footer with default logo src', () => { + // Social Links + test('Renders all social links', () => { const wrapper = mount(PdapFooter, base); - expect(wrapper.props().logoImageSrc).toBe('/src/assets/acronym.svg'); + const links = wrapper.findAll('ul li a'); + expect(links).toHaveLength(6); // Based on the injected links array + + // Verify specific links exist + expect(wrapper.html()).toContain('Github'); + expect(wrapper.html()).toContain('Discord'); + expect(wrapper.html()).toContain('LinkedIn'); + expect(wrapper.html()).toContain('Jobs'); + expect(wrapper.html()).toContain('Newsletter'); + expect(wrapper.html()).toContain('Docs'); }); - test('Renders footer with custom logo src', () => { - const wrapper = mount(PdapFooter, { - ...base, - props: { logoImageSrc: 'test' }, + // Fundraising Section + describe('Fundraising meter', () => { + test('Displays correct fundraising amounts', () => { + const wrapper = mount(PdapFooter, base); + const fundraisingText = wrapper + .find('a[href="https://pdap.io/donate"]') + .text(); + + expect(fundraisingText).toContain('$5000'); + expect(fundraisingText).toContain('$10000'); + }); + + test('Shows celebration emoji when goal is met', () => { + const wrapper = mount(PdapFooter, { + ...base, + props: { + fundraisingData: { + raised: 10000, + goal: 10000, + }, + }, + }); + + expect(wrapper.html()).toContain('🎉'); + }); + + test('Does not show celebration emoji when goal is not met', () => { + const wrapper = mount(PdapFooter, base); + expect(wrapper.html()).not.toContain('🎉'); + }); + + test('Sets fundraising progress style variable correctly', async () => { + const wrapper = mount(PdapFooter, base); + await wrapper.vm.$nextTick(); // Wait for mounted hook + + const footerElement = wrapper.element as HTMLElement; + const progressStyle = footerElement.style.getPropertyValue( + '--fundraising-progress' + ); + expect(progressStyle).toBe('50%'); // 5000/10000 * 100 }); - expect(wrapper.props().logoImageSrc).toBe('test'); }); - // Props - anchor path - test('Renders footer with default logo anchor path', () => { + // Copyright Section + test('Renders current year in copyright notice', () => { const wrapper = mount(PdapFooter, base); - expect(wrapper.props().logoImageAnchorPath).toBe('/'); + const currentYear = new Date().getFullYear(); + expect(wrapper.html()).toContain(`© ${currentYear}`); }); - test('Renders footer with custom logo anchor path', () => { - const wrapper = mount(PdapFooter, { - ...base, - props: { logoImageAnchorPath: '/test' }, - }); - expect(wrapper.props().logoImageAnchorPath).toBe('/test'); + test('Renders EIN number', () => { + const wrapper = mount(PdapFooter, base); + expect(wrapper.html()).toContain('EIN: 85-4207132'); + }); + + test('Renders Guidestar badge', () => { + const wrapper = mount(PdapFooter, base); + const guidestarImg = wrapper.find('img[alt="platinum transparency"]'); + + expect(guidestarImg.exists()).toBe(true); + expect(guidestarImg.attributes('src')).toBe( + 'https://widgets.guidestar.org/gximage2?o=9973356&l=v4' + ); }); - test('Renders footer with custom logo anchor href', () => { - const wrapper = mount(PdapFooter, { - ...base, - props: { logoImageAnchorPath: 'www.test.com' }, + describe('Link behavior', () => { + test('External links should open in new tab', () => { + const wrapper = mount(PdapFooter, base); + const externalLinks = wrapper.findAll('a[href^="http"]'); + + // Verify that all external links have the correct attributes + externalLinks.forEach((link) => { + expect(link.attributes('target')).toBe('_blank'); + expect(link.attributes('rel')).toBe('noreferrer'); + }); + }); + + test('Internal links should not open in new tab', () => { + const wrapper = mount(PdapFooter, base); + const internalLinks = wrapper.findAll('a:not([href^="http"])'); + + internalLinks.forEach((link) => { + expect(link.attributes('target')).toBeFalsy(); + expect(link.attributes('rel')).toBeFalsy(); + }); }); - expect(wrapper.props().logoImageAnchorPath).toBe('www.test.com'); }); }); diff --git a/src/components/Footer/index.ts b/src/components/Footer/index.ts index 6073e3d..19695fc 100644 --- a/src/components/Footer/index.ts +++ b/src/components/Footer/index.ts @@ -1,3 +1,4 @@ import PdapFooter from './PdapFooter.vue'; +export { FOOTER_LINK_ICONS } from './constants'; export { PdapFooter as Footer }; diff --git a/src/components/Footer/types.ts b/src/components/Footer/types.ts index 359d87c..560ed99 100644 --- a/src/components/Footer/types.ts +++ b/src/components/Footer/types.ts @@ -1,10 +1,17 @@ +import { FOOTER_LINK_ICONS } from './constants'; + export interface PdapFooterSocialLinks { href?: string; path?: string; text: string; + icon?: FooterIconName; } +export type FooterIconName = + (typeof FOOTER_LINK_ICONS)[keyof typeof FOOTER_LINK_ICONS]; export interface PdapFooterProps { - logoImageSrc?: string; - logoImageAnchorPath?: string; + fundraisingData: { + raised: number; + goal: number; + }; } diff --git a/src/demo/App.vue b/src/demo/App.vue index a96b1e7..1600518 100644 --- a/src/demo/App.vue +++ b/src/demo/App.vue @@ -45,5 +45,5 @@ provide('navLinks', links); diff --git a/src/demo/pages/ComponentDemo.vue b/src/demo/pages/ComponentDemo.vue index 64dd2ee..655365f 100644 --- a/src/demo/pages/ComponentDemo.vue +++ b/src/demo/pages/ComponentDemo.vue @@ -1,5 +1,5 @@