Skip to content

Commit

Permalink
feat: support all hashnode social media links (#1060)
Browse files Browse the repository at this point in the history
  • Loading branch information
scissorsneedfoodtoo authored Nov 15, 2024
1 parent 0b610f4 commit 52ffb61
Show file tree
Hide file tree
Showing 24 changed files with 365 additions and 159 deletions.
218 changes: 163 additions & 55 deletions cypress/e2e/english/author/author.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,41 @@ const selectors = {
authorPostCount: "[data-test-label='author-post-count']",
bullet: "[data-test-label='bullet']",
socialMedia: {
facebook: {
link: "[data-test-label='facebook-link']",
icon: "[data-test-label='facebook-icon']"
},
rss: {
link: "[data-test-label='rss-link']",
icon: "[data-test-label='rss-icon']"
},
website: {
link: "[data-test-label='website-link']",
icon: "[data-test-label='website-icon']"
},
x: {
link: "[data-test-label='x-link']",
icon: "[data-test-label='x-icon']"
},
facebook: {
link: "[data-test-label='facebook-link']",
icon: "[data-test-label='facebook-icon']"
},
github: {
link: "[data-test-label='github-link']",
icon: "[data-test-label='github-icon']"
},
stackoverflow: {
link: "[data-test-label='stackoverflow-link']",
icon: "[data-test-label='stackoverflow-icon']"
},
linkedin: {
link: "[data-test-label='linkedin-link']",
icon: "[data-test-label='linkedin-icon']"
},
instagram: {
link: "[data-test-label='instagram-link']",
icon: "[data-test-label='instagram-icon']"
},
youtube: {
link: "[data-test-label='youtube-link']",
icon: "[data-test-label='youtube-icon']"
},
rss: {
link: "[data-test-label='rss-link']",
icon: "[data-test-label='rss-icon']"
}
}
};
Expand Down Expand Up @@ -86,35 +106,33 @@ describe('Author page (Hashnode sourced)', () => {
});
});

// TODO: Usernames / author slugs from Hashnode are not all lowercase by default.
// For tests that go to the freeCodeCamp author page, we need to use the correct case
// for the username, otherwise we'll get a 404. Consider lowercasing all author slugs
// when pulling in data from Hashnode.
context('Social media', () => {
// TODO: Add tests for other social media links that Hashnode supports
// (GitHub, LinkedIn, Instagram, YouTube, etc.)
context('Facebook', () => {
context('An author with no Facebook profile link', () => {
context('Website', () => {
context('Author with no website link', () => {
before(() => {
cy.visit('/author/abbeyrenn/');
cy.visit('/author/beaucarnes/');
});

it('should not show an X link and icon', () => {
cy.get(selectors.socialMedia.facebook.link).should('not.exist');
cy.get(selectors.socialMedia.facebook.icon).should('not.exist');
it('should not show a website link and icon', () => {
cy.get(selectors.socialMedia.website.link).should('not.exist');
cy.get(selectors.socialMedia.website.icon).should('not.exist');
});
});

context('An author with a Facebook profile link', () => {
// TODO: Usernames / author slugs from Hashnode are not all lowercase by default.
// This is not a problem on the live site, but the 11ty dev server will 404
// unless the casing matches the author's actual Hashnode username. Consider
// lowercasing all author slugs when pulling in data from Hashnode.
context('Author with a website link', () => {
before(() => {
cy.visit('/author/freeCodeCamp/');
});

it('should show a Facebook link and icon', () => {
cy.get(selectors.socialMedia.facebook.link)
.should('have.attr', 'href', 'https://facebook.com/freecodecamp')
it('should show a website link and icon with the expected attributes', () => {
cy.get(selectors.socialMedia.website.link)
.should('have.attr', 'href', 'https://www.freecodecamp.org/')
.find('svg')
.should('have.attr', 'data-test-label', 'facebook-icon');
.should('have.attr', 'data-test-label', 'website-icon');
});
});
});
Expand All @@ -136,7 +154,7 @@ describe('Author page (Hashnode sourced)', () => {
cy.visit('/author/quincy/');
});

it('should show an X link and icon', () => {
it('should show an X link and icon with the expected attributes', () => {
cy.get(selectors.socialMedia.x.link)
.should('have.attr', 'href', 'https://x.com/ossia')
.find('svg')
Expand All @@ -146,60 +164,150 @@ describe('Author page (Hashnode sourced)', () => {

context('Author with an X profile link', () => {
before(() => {
cy.visit('/author/abbeyrenn/');
cy.visit('/author/freeCodeCamp/');
});

it('should show an X link and icon', () => {
it('should show an X link and icon with the expected attributes', () => {
cy.get(selectors.socialMedia.x.link)
.should('have.attr', 'href', 'https://x.com/abbeyrenn')
.should('have.attr', 'href', 'https://x.com/freecodecamp')
.find('svg')
.should('have.attr', 'data-test-label', 'x-icon');
});
});
});

context('Facebook', () => {
context('An author with no Facebook profile link', () => {
before(() => {
cy.visit('/author/beaucarnes/');
});

it('should not show a Facebook link and icon', () => {
cy.get(selectors.socialMedia.facebook.link).should('not.exist');
cy.get(selectors.socialMedia.facebook.icon).should('not.exist');
});
});

context('An author with a Facebook profile link', () => {
before(() => {
cy.visit('/author/freeCodeCamp/');
});

it('should show a Facebook link and icon with the expected attributes', () => {
cy.get(selectors.socialMedia.facebook.link)
.should(
'have.attr',
'href',
'https://www.facebook.com/freecodecamp'
)
.find('svg')
.should('have.attr', 'data-test-label', 'facebook-icon');
});
});
});

context('GitHub', () => {
context('An author with no GitHub profile link', () => {
before(() => {
cy.visit('/author/beaucarnes/');
});

it('should not show a GitHub link and icon', () => {
cy.get(selectors.socialMedia.github.link).should('not.exist');
cy.get(selectors.socialMedia.github.icon).should('not.exist');
});
});

context('Website', () => {
context('Author with no website link', () => {
before(() => {
cy.visit('/author/beaucarnes/');
});
context('An author with a GitHub profile link', () => {
before(() => {
cy.visit('/author/freeCodeCamp/');
});

it('should not show a website link and icon', () => {
cy.get(selectors.socialMedia.website.link).should('not.exist');
cy.get(selectors.socialMedia.website.icon).should('not.exist');
});
it('should show a GitHub link and icon with the expected attributes', () => {
cy.get(selectors.socialMedia.github.link)
.should('have.attr', 'href', 'https://github.com/freecodecamp')
.find('svg')
.should('have.attr', 'data-test-label', 'github-icon');
});
});
});

context('Author with a website link', () => {
before(() => {
cy.visit('/author/quincy/');
});
context('Stack Overflow', () => {
context('An author with no Stack Overflow profile link', () => {
before(() => {
cy.visit('/author/beaucarnes/');
});

it('should show a website link and icon', () => {
cy.get(selectors.socialMedia.website.link)
.should('have.attr', 'href', 'https://www.freecodecamp.org')
.find('svg')
.should('have.attr', 'data-test-label', 'website-icon');
});
it('should not show a Stack Overflow link and icon', () => {
cy.get(selectors.socialMedia.stackoverflow.link).should('not.exist');
cy.get(selectors.socialMedia.stackoverflow.icon).should('not.exist');
});
});

// Note: All authors should have an RSS link and icon
context('RSS', () => {
context('An author with a Stack Overflow profile link', () => {
before(() => {
cy.visit('/author/quincy/');
cy.visit('/author/freeCodeCamp/');
});

it('should show an RSS link and icon', () => {
cy.get(selectors.socialMedia.rss.link)
it('should show a Stack Overflow link and icon with the expected attributes', () => {
cy.get(selectors.socialMedia.stackoverflow.link)
.should(
'have.attr',
'href',
'https://feedly.com/i/subscription/feed/http://localhost:8080/news/author/quincy/rss/'
'https://stackoverflow.com/users/1373672/quincy-larson' // Note: Using Quincy's SO profile for testing purposes
)
.find('svg')
.should('have.attr', 'data-test-label', 'rss-icon');
.should('have.attr', 'data-test-label', 'stackoverflow-icon');
});
});
});

context('LinkedIn', () => {
context('An author with no LinkedIn profile link', () => {
before(() => {
cy.visit('/author/beaucarnes/');
});

it('should not show a LinkedIn link and icon', () => {
cy.get(selectors.socialMedia.linkedin.link).should('not.exist');
cy.get(selectors.socialMedia.linkedin.icon).should('not.exist');
});
});

context('An author with a LinkedIn profile link', () => {
before(() => {
cy.visit('/author/freeCodeCamp/');
});

it('should show a LinkedIn link and icon with the expected attributes', () => {
cy.get(selectors.socialMedia.linkedin.link)
.should(
'have.attr',
'href',
'https://www.linkedin.com/school/free-code-camp/'
)
.find('svg')
.should('have.attr', 'data-test-label', 'linkedin-icon');
});
});
});

// Note: All authors should have an RSS link and icon
context('RSS', () => {
before(() => {
cy.visit('/author/beaucarnes/');
});

it('should show an RSS link and icon with the expected attributes', () => {
cy.get(selectors.socialMedia.rss.link)
.should(
'have.attr',
'href',
'https://feedly.com/i/subscription/feed/http://localhost:8080/news/author/beaucarnes/rss/'
)
.find('svg')
.should('have.attr', 'data-test-label', 'rss-icon');
});
});
});
});
6 changes: 5 additions & 1 deletion cypress/e2e/espanol/author/author.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,11 @@ describe('Author page (Ghost sourced)', () => {

it('should show a Facebook link and icon', () => {
cy.get(selectors.socialMedia.facebook.link)
.should('have.attr', 'href', 'https://facebook.com/freecodecamp')
.should(
'have.attr',
'href',
'https://www.facebook.com/freecodecamp'
)
.find('svg')
.should('have.attr', 'data-test-label', 'facebook-icon');
});
Expand Down
11 changes: 8 additions & 3 deletions cypress/fixtures/mock-hashnode-posts.json
Original file line number Diff line number Diff line change
Expand Up @@ -166,9 +166,14 @@
"tagline": "Learn to code. Build projects. Earn certifications—All for free.",
"profilePicture": "https://cdn.hashnode.com/res/hashnode/image/upload/v1712072540742/C7c8AOPN6.png",
"socialMediaLinks": {
"website": "https://www.freecodecamp.org",
"twitter": "https://twitter.com/freecodecamp",
"facebook": "https://www.facebook.com/freecodecamp"
"website": "https://www.freecodecamp.org/",
"twitter": "https://x.com/freecodecamp",
"facebook": "https://www.facebook.com/freecodecamp",
"instagram": "https://www.instagram.com/freecodecamp/",
"youtube": "https://www.youtube.com/freecodecamp",
"github": "https://github.com/freecodecamp",
"stackoverflow": "https://stackoverflow.com/users/1373672/quincy-larson",
"linkedin": "https://www.linkedin.com/school/free-code-camp/"
},
"location": "Just here on Earth... for now"
},
Expand Down
6 changes: 4 additions & 2 deletions src/_data/site.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ const {
getRoundedTotalRecords
} = require('../../utils/search-bar-placeholder-number');
const { currentLocale_i18nISOCode, siteURL } = require('../../config');
const getTwitterHandle = require('../../utils/get-twitter-handle');
const getUsername = require('../../utils/get-username');
const translate = require('../../utils/translate');

// Get X / Twitter profile based on links in config/i18n/locales/lang/links.json --
// falls back to English Twitter profile if one for the current UI locale
// isn't found
const twitterURL = translate('links:twitter');
const twitterHandle = twitterURL
? `@${getTwitterHandle(twitterURL)}`
? `@${getUsername(twitterURL)}`
: '@freecodecamp';
const logoURL =
'https://cdn.freecodecamp.org/platform/universal/fcc_primary.svg';
Expand All @@ -26,7 +26,9 @@ module.exports = async () => {
lang: currentLocale_i18nISOCode.toLowerCase(),
title: 'freeCodeCamp.org',
facebook: 'https://www.facebook.com/freecodecamp',
facebook_username: 'freecodecamp',
twitter_handle: twitterHandle,
twitter: `https://x.com/${twitterHandle}`,
logo: logoURL,
cover_image: coverImageURL,
og_image: coverImageURL,
Expand Down
10 changes: 4 additions & 6 deletions src/_includes/assets/css/screen.css
Original file line number Diff line number Diff line change
Expand Up @@ -322,13 +322,10 @@ a.nav-forum {
}

.social-links {
flex-shrink: 0;
display: flex;
align-items: center;
}

.social-links a:last-of-type {
padding-right: 20px;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
}

.social-link {
Expand Down Expand Up @@ -1869,6 +1866,7 @@ p:has(mjx-container.MathJax) {
font-family: var(--font-family-sans-serif);
font-display: swap;
font-style: italic;
flex-wrap: wrap;
}

.under-header-content .author-location svg {
Expand Down
6 changes: 3 additions & 3 deletions src/_includes/layouts/author.njk
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@
<meta property="og:description" content="{{ author.bio if author.bio else defaultDescription }}">
<meta property="og:url" content="{{ author.path | htmlBaseUrl(site.url) }}">
<meta property="og:image" content="{{ author.cover_image if author.cover_image else site.cover_image }}">
<meta property="article:publisher" content="https://www.facebook.com/freecodecamp">
{% if author.facebook %}
<meta property="article:author" content="{{ author.facebook }}">
<meta property="article:publisher" content="{{ site.facebook }}">
{% if author.facebook_username %}
<meta property="article:author" content="{{ author.facebook_username }}">
{% endif %}

{# X / Twitter Card #}
Expand Down
2 changes: 1 addition & 1 deletion src/_includes/layouts/default.njk
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@
<meta property="og:description" content="{% t 'meta-tags:description' %}">
<meta property="og:url" content="{{ page.url | htmlBaseUrl(site.url) }}">
<meta property="og:image" content="{{ site.cover_image }}">
<meta property="article:publisher" content="https://www.facebook.com/freecodecamp">
<meta property="article:publisher" content="{{ site.facebook }}">

{# X / Twitter Card #}
<meta name="twitter:card" content="summary_large_image">
Expand Down
Loading

0 comments on commit 52ffb61

Please sign in to comment.