feat: added isTemplate badge & refactored console.logs (#146)

* feat: template option added husky added for same commit disable console in test \
logger utils added env checked for log modified git ignore

* changed are done as per the suggesstion

* changed style and font

* text color dynamic

* fix border and using .bagde class as common

* simplified the badge svg code through a common method

* chore: updated css & fixed tests

Co-authored-by: anuraghazra <hazru.anurag@gmail.com>
This commit is contained in:
Joydip Roy 2020-07-23 13:05:50 +05:30 committed by GitHub
parent 03f55e809e
commit 5ed75e11be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 78 additions and 33 deletions

2
.gitignore vendored
View File

@ -2,4 +2,6 @@
.env .env
node_modules node_modules
package-lock.json package-lock.json
*.lock
.vscode/
coverage coverage

View File

@ -4,6 +4,7 @@ const {
parseBoolean, parseBoolean,
clampValue, clampValue,
CONSTANTS, CONSTANTS,
logger,
} = require("../src/utils"); } = require("../src/utils");
const fetchRepo = require("../src/fetchRepo"); const fetchRepo = require("../src/fetchRepo");
const renderRepoCard = require("../src/renderRepoCard"); const renderRepoCard = require("../src/renderRepoCard");
@ -28,7 +29,7 @@ module.exports = async (req, res) => {
try { try {
repoData = await fetchRepo(username, repo); repoData = await fetchRepo(username, repo);
} catch (err) { } catch (err) {
console.log(err); logger.error(err);
return res.send(renderError(err.message)); return res.send(renderError(err.message));
} }
@ -52,7 +53,7 @@ module.exports = async (req, res) => {
} }
res.setHeader("Cache-Control", `public, max-age=${cacheSeconds}`); res.setHeader("Cache-Control", `public, max-age=${cacheSeconds}`);
res.send( res.send(
renderRepoCard(repoData, { renderRepoCard(repoData, {
title_color, title_color,

View File

@ -15,9 +15,15 @@
"axios": "^0.19.2", "axios": "^0.19.2",
"axios-mock-adapter": "^1.18.1", "axios-mock-adapter": "^1.18.1",
"css-to-object": "^1.1.0", "css-to-object": "^1.1.0",
"husky": "^4.2.5",
"jest": "^26.1.0" "jest": "^26.1.0"
}, },
"dependencies": { "dependencies": {
"dotenv": "^8.2.0" "dotenv": "^8.2.0"
},
"husky": {
"hooks": {
"pre-commit": "npm test"
}
} }
} }

View File

@ -10,6 +10,7 @@ const fetcher = (variables, token) => {
nameWithOwner nameWithOwner
isPrivate isPrivate
isArchived isArchived
isTemplate
stargazers { stargazers {
totalCount totalCount
} }

View File

@ -1,4 +1,4 @@
const { request } = require("./utils"); const { request, logger } = require("./utils");
const retryer = require("./retryer"); const retryer = require("./retryer");
const calculateRank = require("./calculateRank"); const calculateRank = require("./calculateRank");
require("dotenv").config(); require("dotenv").config();
@ -61,7 +61,7 @@ async function fetchStats(username) {
let res = await retryer(fetcher, { login: username }); let res = await retryer(fetcher, { login: username });
if (res.data.errors) { if (res.data.errors) {
console.log(res.data.errors); logger.error(res.data.errors);
throw Error(res.data.errors[0].message || "Could not fetch user"); throw Error(res.data.errors[0].message || "Could not fetch user");
} }

View File

@ -1,4 +1,4 @@
const { request } = require("./utils"); const { request, logger } = require("./utils");
const retryer = require("./retryer"); const retryer = require("./retryer");
require("dotenv").config(); require("dotenv").config();
@ -38,7 +38,7 @@ async function fetchTopLanguages(username) {
let res = await retryer(fetcher, { login: username }); let res = await retryer(fetcher, { login: username });
if (res.data.errors) { if (res.data.errors) {
console.log(res.data.errors); logger.error(res.data.errors);
throw Error(res.data.errors[0].message || "Could not fetch user"); throw Error(res.data.errors[0].message || "Could not fetch user");
} }

View File

@ -14,6 +14,7 @@ const renderRepoCard = (repo, options = {}) => {
primaryLanguage, primaryLanguage,
stargazers, stargazers,
isArchived, isArchived,
isTemplate,
forkCount, forkCount,
} = repo; } = repo;
const { const {
@ -49,14 +50,20 @@ const renderRepoCard = (repo, options = {}) => {
const totalStars = kFormatter(stargazers.totalCount); const totalStars = kFormatter(stargazers.totalCount);
const totalForks = kFormatter(forkCount); const totalForks = kFormatter(forkCount);
const archiveBadge = isArchived const getBadgeSVG = (label) => `
? ` <g data-testid="badge" class="badge" transform="translate(320, 38)">
<g data-testid="archive-badge" class="archive-badge" transform="translate(320, 38)">
<rect stroke="${textColor}" stroke-width="1" width="70" height="20" x="-12" y="-14" ry="10" rx="10"></rect> <rect stroke="${textColor}" stroke-width="1" width="70" height="20" x="-12" y="-14" ry="10" rx="10"></rect>
<text fill="${textColor}">Archived</text> <text
x="23" y="-5"
alignment-baseline="central"
dominant-baseline="central"
text-anchor="middle"
fill="${textColor}"
>
${label}
</text>
</g> </g>
` `;
: "";
const svgLanguage = ` const svgLanguage = `
<g transform="translate(30, 100)"> <g transform="translate(30, 100)">
@ -90,17 +97,25 @@ const renderRepoCard = (repo, options = {}) => {
.description { font: 400 13px 'Segoe UI', Ubuntu, Sans-Serif; fill: ${textColor} } .description { font: 400 13px 'Segoe UI', Ubuntu, Sans-Serif; fill: ${textColor} }
.gray { font: 400 12px 'Segoe UI', Ubuntu, Sans-Serif; fill: ${textColor} } .gray { font: 400 12px 'Segoe UI', Ubuntu, Sans-Serif; fill: ${textColor} }
.icon { fill: ${iconColor} } .icon { fill: ${iconColor} }
.archive-badge { font: 600 12px 'Segoe UI', Ubuntu, Sans-Serif; } .badge { font: 600 11px 'Segoe UI', Ubuntu, Sans-Serif; }
.archive-badge rect { opacity: 0.2 } .badge rect { opacity: 0.2 }
</style> </style>
<rect data-testid="card-bg" x="0.5" y="0.5" width="399" height="99%" rx="4.5" fill="${bgColor}" stroke="#E4E2E2"/> <rect data-testid="card-bg" x="0.5" y="0.5" width="399" height="99%" rx="4.5" fill="${bgColor}" stroke="#E4E2E2"/>
<svg class="icon" x="25" y="25" viewBox="0 0 16 16" version="1.1" width="16" height="16"> <svg class="icon" x="25" y="25" viewBox="0 0 16 16" version="1.1" width="16" height="16">
${icons.contribs} ${icons.contribs}
</svg> </svg>
${archiveBadge}
<text x="50" y="38" class="header">${header}</text> <text x="50" y="38" class="header">${header}</text>
${
isTemplate
? getBadgeSVG("Template")
: isArchived
? getBadgeSVG("Archived")
: ""
}
<text class="description" x="25" y="70">${encodeHTML(desc)}</text> <text class="description" x="25" y="70">${encodeHTML(desc)}</text>
${svgLanguage} ${svgLanguage}

View File

@ -1,9 +1,11 @@
const { logger } = require("./utils");
const retryer = async (fetcher, variables, retries = 0) => { const retryer = async (fetcher, variables, retries = 0) => {
if (retries > 7) { if (retries > 7) {
throw new Error("Maximum retries exceeded"); throw new Error("Maximum retries exceeded");
} }
try { try {
console.log(`Trying PAT_${retries + 1}`); logger.log(`Trying PAT_${retries + 1}`);
// try to fetch with the first token since RETRIES is 0 index i'm adding +1 // try to fetch with the first token since RETRIES is 0 index i'm adding +1
let response = await fetcher( let response = await fetcher(
@ -18,7 +20,7 @@ const retryer = async (fetcher, variables, retries = 0) => {
// if rate limit is hit increase the RETRIES and recursively call the retryer // if rate limit is hit increase the RETRIES and recursively call the retryer
// with username, and current RETRIES // with username, and current RETRIES
if (isRateExceeded) { if (isRateExceeded) {
console.log(`PAT_${retries + 1} Failed`); logger.log(`PAT_${retries + 1} Failed`);
retries++; retries++;
// directly return from the function // directly return from the function
return retryer(fetcher, variables, retries); return retryer(fetcher, variables, retries);
@ -32,7 +34,7 @@ const retryer = async (fetcher, variables, retries = 0) => {
const isBadCredential = err.response.data && err.response.data.message === "Bad credentials"; const isBadCredential = err.response.data && err.response.data.message === "Bad credentials";
if (isBadCredential) { if (isBadCredential) {
console.log(`PAT_${retries + 1} Failed`); logger.log(`PAT_${retries + 1} Failed`);
retries++; retries++;
// directly return from the function // directly return from the function
return retryer(fetcher, variables, retries); return retryer(fetcher, variables, retries);

View File

@ -116,6 +116,10 @@ function getCardColors({
return { titleColor, iconColor, textColor, bgColor }; return { titleColor, iconColor, textColor, bgColor };
} }
const fn = () => {};
// return console instance based on the environment
const logger = process.env.NODE_ENV !== "test" ? console : { log: fn, error: fn };
const CONSTANTS = { const CONSTANTS = {
THIRTY_MINUTES: 1800, THIRTY_MINUTES: 1800,
TWO_HOURS: 7200, TWO_HOURS: 7200,
@ -133,5 +137,6 @@ module.exports = {
FlexLayout, FlexLayout,
getCardColors, getCardColors,
clampValue, clampValue,
logger,
CONSTANTS, CONSTANTS,
}; };

View File

@ -17,6 +17,7 @@ const data_repo = {
name: "TypeScript", name: "TypeScript",
}, },
forkCount: 100, forkCount: 100,
isTemplate: false
}, },
}; };

View File

@ -217,17 +217,6 @@ describe("Test renderRepoCard", () => {
); );
}); });
it("should render archive badge if repo is archived", () => {
document.body.innerHTML = renderRepoCard({
...data_repo.repository,
isArchived: true,
});
expect(queryByTestId(document.body, "archive-badge")).toHaveTextContent(
"Archived"
);
});
it("should not render star count or fork count if either of the are zero", () => { it("should not render star count or fork count if either of the are zero", () => {
document.body.innerHTML = renderRepoCard({ document.body.innerHTML = renderRepoCard({
...data_repo.repository, ...data_repo.repository,
@ -235,7 +224,7 @@ describe("Test renderRepoCard", () => {
}); });
expect(queryByTestId(document.body, "stargazers")).toBeNull(); expect(queryByTestId(document.body, "stargazers")).toBeNull();
expect(queryByTestId(document.body, "forkcount")).toBeDefined(); expect(queryByTestId(document.body, "forkcount")).toBeInTheDocument();
document.body.innerHTML = renderRepoCard({ document.body.innerHTML = renderRepoCard({
...data_repo.repository, ...data_repo.repository,
@ -243,7 +232,7 @@ describe("Test renderRepoCard", () => {
forkCount: 0, forkCount: 0,
}); });
expect(queryByTestId(document.body, "stargazers")).toBeDefined(); expect(queryByTestId(document.body, "stargazers")).toBeInTheDocument();
expect(queryByTestId(document.body, "forkcount")).toBeNull(); expect(queryByTestId(document.body, "forkcount")).toBeNull();
document.body.innerHTML = renderRepoCard({ document.body.innerHTML = renderRepoCard({
@ -255,4 +244,26 @@ describe("Test renderRepoCard", () => {
expect(queryByTestId(document.body, "stargazers")).toBeNull(); expect(queryByTestId(document.body, "stargazers")).toBeNull();
expect(queryByTestId(document.body, "forkcount")).toBeNull(); expect(queryByTestId(document.body, "forkcount")).toBeNull();
}); });
it("should render badges", () => {
document.body.innerHTML = renderRepoCard({
...data_repo.repository,
isArchived: true,
});
expect(queryByTestId(document.body, "badge")).toHaveTextContent("Archived");
document.body.innerHTML = renderRepoCard({
...data_repo.repository,
isTemplate: true,
});
expect(queryByTestId(document.body, "badge")).toHaveTextContent("Template");
});
it("should not render template", () => {
document.body.innerHTML = renderRepoCard({
...data_repo.repository,
});
expect(queryByTestId(document.body, "badge")).toBeNull();
});
}); });

View File

@ -1,8 +1,9 @@
require("@testing-library/jest-dom"); require("@testing-library/jest-dom");
const retryer = require("../src/retryer"); const retryer = require("../src/retryer");
const { logger } = require("../src/utils");
const fetcher = jest.fn((variables, token) => { const fetcher = jest.fn((variables, token) => {
console.log(variables, token); logger.log(variables, token);
return new Promise((res, rej) => res({ data: "ok" })); return new Promise((res, rej) => res({ data: "ok" }));
}); });