mirror of
https://github.com/Aviortheking/codestats-readme.git
synced 2025-04-22 10:42:08 +00:00
test: added test & refactored files
This commit is contained in:
parent
223bd659ab
commit
45941f6c70
140
api/index.js
140
api/index.js
@ -1,138 +1,6 @@
|
|||||||
const axios = require("axios");
|
const { renderError } = require("../src/utils");
|
||||||
const { renderError, kFormatter } = require("../utils");
|
const fetchStats = require("../src/fetchStats");
|
||||||
require("dotenv").config();
|
const renderStatsCard = require("../src/renderStatsCard");
|
||||||
|
|
||||||
async function fetchStats(username) {
|
|
||||||
if (!username) throw Error("Invalid username");
|
|
||||||
|
|
||||||
const res = await axios({
|
|
||||||
url: "https://api.github.com/graphql",
|
|
||||||
method: "post",
|
|
||||||
headers: {
|
|
||||||
Authorization: `bearer ${process.env.GITHUB_TOKEN}`,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
query: `
|
|
||||||
query userInfo($login: String!) {
|
|
||||||
user(login: $login) {
|
|
||||||
name
|
|
||||||
repositoriesContributedTo(first: 100, contributionTypes: [COMMIT, ISSUE, PULL_REQUEST, REPOSITORY]) {
|
|
||||||
totalCount
|
|
||||||
}
|
|
||||||
contributionsCollection {
|
|
||||||
totalCommitContributions
|
|
||||||
}
|
|
||||||
pullRequests(first: 100) {
|
|
||||||
totalCount
|
|
||||||
}
|
|
||||||
issues(first: 100) {
|
|
||||||
totalCount
|
|
||||||
}
|
|
||||||
repositories(first: 100) {
|
|
||||||
nodes {
|
|
||||||
stargazers {
|
|
||||||
totalCount
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
variables: {
|
|
||||||
login: username,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const stats = {
|
|
||||||
name: "",
|
|
||||||
totalPRs: 0,
|
|
||||||
totalCommits: 0,
|
|
||||||
totalIssues: 0,
|
|
||||||
totalStars: 0,
|
|
||||||
contributedTo: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (res.data.errors) {
|
|
||||||
console.log(res.data.errors);
|
|
||||||
throw Error("Could not fetch user");
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = res.data.data.user;
|
|
||||||
|
|
||||||
stats.name = user.name;
|
|
||||||
stats.totalIssues = user.issues.totalCount;
|
|
||||||
stats.totalCommits = user.contributionsCollection.totalCommitContributions;
|
|
||||||
stats.totalPRs = user.pullRequests.totalCount;
|
|
||||||
stats.contributedTo = user.repositoriesContributedTo.totalCount;
|
|
||||||
|
|
||||||
stats.totalStars = user.repositories.nodes.reduce((prev, curr) => {
|
|
||||||
return prev + curr.stargazers.totalCount;
|
|
||||||
}, 0);
|
|
||||||
|
|
||||||
return stats;
|
|
||||||
}
|
|
||||||
|
|
||||||
const createTextNode = (icon, label, value, lheight) => {
|
|
||||||
const classname = icon === "★" && "star-icon";
|
|
||||||
return `
|
|
||||||
<tspan x="25" dy="${lheight}" class="stat bold">
|
|
||||||
<tspan class="icon ${classname}" fill="#4C71F2">${icon}</tspan> ${label}:</tspan>
|
|
||||||
<tspan x="155" dy="0" class="stat">${kFormatter(value)}</tspan>
|
|
||||||
`;
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderSVG = (stats, options) => {
|
|
||||||
const {
|
|
||||||
name,
|
|
||||||
totalStars,
|
|
||||||
totalCommits,
|
|
||||||
totalIssues,
|
|
||||||
totalPRs,
|
|
||||||
contributedTo,
|
|
||||||
} = stats;
|
|
||||||
const { hide, show_icons, hide_border, line_height } = options || {};
|
|
||||||
|
|
||||||
const lheight = line_height || 25;
|
|
||||||
|
|
||||||
const STAT_MAP = {
|
|
||||||
stars: createTextNode("★", "Total Stars", totalStars, lheight),
|
|
||||||
commits: createTextNode("🕗", "Total Commits", totalCommits, lheight),
|
|
||||||
prs: createTextNode("🔀", "Total PRs", totalPRs, lheight),
|
|
||||||
issues: createTextNode("ⓘ", "Total Issues", totalIssues, lheight),
|
|
||||||
contribs: createTextNode("📕", "Contributed to", contributedTo, lheight),
|
|
||||||
};
|
|
||||||
|
|
||||||
const statItems = Object.keys(STAT_MAP)
|
|
||||||
.filter((key) => !hide.includes(key))
|
|
||||||
.map((key) => STAT_MAP[key]);
|
|
||||||
|
|
||||||
const height = 45 + (statItems.length + 1) * lheight;
|
|
||||||
|
|
||||||
return `
|
|
||||||
<svg width="495" height="${height}" viewBox="0 0 495 ${height}" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<style>
|
|
||||||
.header { font: 600 18px 'Segoe UI', Ubuntu, Sans-Serif; fill: #2F80ED }
|
|
||||||
.stat { font: 600 14px 'Segoe UI', Ubuntu, Sans-Serif; fill: #333 }
|
|
||||||
.star-icon { font: 600 18px 'Segoe UI', Ubuntu, Sans-Serif; }
|
|
||||||
.bold { font-weight: 700 }
|
|
||||||
.icon {
|
|
||||||
display: none;
|
|
||||||
${!!show_icons && "display: block"}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
${
|
|
||||||
!hide_border &&
|
|
||||||
`<rect x="0.5" y="0.5" width="494" height="99%" rx="4.5" fill="#FFFEFE" stroke="#E4E2E2"/>`
|
|
||||||
}
|
|
||||||
|
|
||||||
<text x="25" y="35" class="header">${name}'s GitHub Stats</text>
|
|
||||||
<text y="45">
|
|
||||||
${statItems}
|
|
||||||
</text>
|
|
||||||
</svg>
|
|
||||||
`;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = async (req, res) => {
|
module.exports = async (req, res) => {
|
||||||
const { username, hide, hide_border, show_icons, line_height } = req.query;
|
const { username, hide, hide_border, show_icons, line_height } = req.query;
|
||||||
@ -146,7 +14,7 @@ module.exports = async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
res.send(
|
res.send(
|
||||||
renderSVG(stats, {
|
renderStatsCard(stats, {
|
||||||
hide: JSON.parse(hide || "[]"),
|
hide: JSON.parse(hide || "[]"),
|
||||||
show_icons,
|
show_icons,
|
||||||
hide_border,
|
hide_border,
|
||||||
|
118
api/pin.js
118
api/pin.js
@ -1,120 +1,8 @@
|
|||||||
const axios = require("axios");
|
const { renderError } = require("../src/utils");
|
||||||
const { renderError, kFormatter, encodeHTML } = require("../utils");
|
const fetchRepo = require("../src/fetchRepo");
|
||||||
|
const renderRepoCard = require("../src/renderRepoCard");
|
||||||
require("dotenv").config();
|
require("dotenv").config();
|
||||||
|
|
||||||
async function fetchRepo(username, reponame) {
|
|
||||||
const res = await axios({
|
|
||||||
url: "https://api.github.com/graphql",
|
|
||||||
method: "post",
|
|
||||||
headers: {
|
|
||||||
Authorization: `bearer ${process.env.GITHUB_TOKEN}`,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
query: `
|
|
||||||
fragment RepoInfo on Repository {
|
|
||||||
name
|
|
||||||
stargazers {
|
|
||||||
totalCount
|
|
||||||
}
|
|
||||||
description
|
|
||||||
primaryLanguage {
|
|
||||||
color
|
|
||||||
id
|
|
||||||
name
|
|
||||||
}
|
|
||||||
forkCount
|
|
||||||
}
|
|
||||||
query getRepo($login: String!, $repo: String!) {
|
|
||||||
user(login: $login) {
|
|
||||||
repository(name: $repo) {
|
|
||||||
...RepoInfo
|
|
||||||
}
|
|
||||||
}
|
|
||||||
organization(login: $login) {
|
|
||||||
repository(name: $repo) {
|
|
||||||
...RepoInfo
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
variables: {
|
|
||||||
login: username,
|
|
||||||
repo: reponame,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const data = res.data.data;
|
|
||||||
|
|
||||||
if (!data.user && !data.organization) {
|
|
||||||
throw new Error("Not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.organization === null && data.user) {
|
|
||||||
if (!data.user.repository) {
|
|
||||||
throw new Error("User Repository Not found");
|
|
||||||
}
|
|
||||||
return data.user.repository;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.user === null && data.organization) {
|
|
||||||
if (!data.organization.repository) {
|
|
||||||
throw new Error("Organization Repository Not found");
|
|
||||||
}
|
|
||||||
return data.organization.repository;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const renderRepoCard = (repo) => {
|
|
||||||
const { name, description, primaryLanguage, stargazers, forkCount } = repo;
|
|
||||||
const height = 120;
|
|
||||||
const shiftText = primaryLanguage.name.length > 15 ? 0 : 30;
|
|
||||||
|
|
||||||
let desc = description || "No description provided";
|
|
||||||
if (desc.length > 55) {
|
|
||||||
desc = `${description.slice(0, 55)}..`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return `
|
|
||||||
<svg width="400" height="${height}" viewBox="0 0 400 ${height}" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<style>
|
|
||||||
.header { font: 600 18px 'Segoe UI', Ubuntu, Sans-Serif; fill: #2F80ED }
|
|
||||||
.stat { font: 600 14px 'Segoe UI', Ubuntu, Sans-Serif; fill: #333 }
|
|
||||||
.star-icon { font: 600 18px 'Segoe UI', Ubuntu, Sans-Serif; }
|
|
||||||
.bold { font-weight: 700 }
|
|
||||||
.description { font: 400 13px 'Segoe UI', Ubuntu, Sans-Serif; fill: #586069 }
|
|
||||||
.gray { font: 400 12px 'Segoe UI', Ubuntu, Sans-Serif; fill: #586069 }
|
|
||||||
</style>
|
|
||||||
<rect x="0.5" y="0.5" width="399" height="99%" rx="4.5" fill="#FFFEFE" stroke="#E4E2E2"/>
|
|
||||||
<svg x="25" y="25" viewBox="0 0 16 16" version="1.1" width="16" height="16" fill="#586069">
|
|
||||||
<path fill-rule="evenodd" d="M2 2.5A2.5 2.5 0 014.5 0h8.75a.75.75 0 01.75.75v12.5a.75.75 0 01-.75.75h-2.5a.75.75 0 110-1.5h1.75v-2h-8a1 1 0 00-.714 1.7.75.75 0 01-1.072 1.05A2.495 2.495 0 012 11.5v-9zm10.5-1V9h-8c-.356 0-.694.074-1 .208V2.5a1 1 0 011-1h8zM5 12.25v3.25a.25.25 0 00.4.2l1.45-1.087a.25.25 0 01.3 0L8.6 15.7a.25.25 0 00.4-.2v-3.25a.25.25 0 00-.25-.25h-3.5a.25.25 0 00-.25.25z"></path>
|
|
||||||
</svg>
|
|
||||||
|
|
||||||
<text x="50" y="38" class="header">${name}</text>
|
|
||||||
<text class="description" x="25" y="70">${encodeHTML(desc)}</text>
|
|
||||||
|
|
||||||
<g transform="translate(30, 100)">
|
|
||||||
<circle cx="0" cy="-5" r="6" fill="${primaryLanguage.color}" />
|
|
||||||
<text class="gray" x="15">${primaryLanguage.name}</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g transform="translate(${155 - shiftText}, 100)">
|
|
||||||
<svg y="-12" viewBox="0 0 16 16" version="1.1" width="16" height="16" fill="#586069">
|
|
||||||
<path fill-rule="evenodd" d="M8 .25a.75.75 0 01.673.418l1.882 3.815 4.21.612a.75.75 0 01.416 1.279l-3.046 2.97.719 4.192a.75.75 0 01-1.088.791L8 12.347l-3.766 1.98a.75.75 0 01-1.088-.79l.72-4.194L.818 6.374a.75.75 0 01.416-1.28l4.21-.611L7.327.668A.75.75 0 018 .25zm0 2.445L6.615 5.5a.75.75 0 01-.564.41l-3.097.45 2.24 2.184a.75.75 0 01.216.664l-.528 3.084 2.769-1.456a.75.75 0 01.698 0l2.77 1.456-.53-3.084a.75.75 0 01.216-.664l2.24-2.183-3.096-.45a.75.75 0 01-.564-.41L8 2.694v.001z"></path>
|
|
||||||
</svg>
|
|
||||||
<text class="gray" x="25">${kFormatter(stargazers.totalCount)}</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g transform="translate(${220 - shiftText}, 100)">
|
|
||||||
<svg y="-12" viewBox="0 0 16 16" version="1.1" width="16" height="16" fill="#586069">
|
|
||||||
<path fill-rule="evenodd" d="M5 3.25a.75.75 0 11-1.5 0 .75.75 0 011.5 0zm0 2.122a2.25 2.25 0 10-1.5 0v.878A2.25 2.25 0 005.75 8.5h1.5v2.128a2.251 2.251 0 101.5 0V8.5h1.5a2.25 2.25 0 002.25-2.25v-.878a2.25 2.25 0 10-1.5 0v.878a.75.75 0 01-.75.75h-4.5A.75.75 0 015 6.25v-.878zm3.75 7.378a.75.75 0 11-1.5 0 .75.75 0 011.5 0zm3-8.75a.75.75 0 100-1.5.75.75 0 000 1.5z"></path>
|
|
||||||
</svg>
|
|
||||||
<text class="gray" x="25">${kFormatter(forkCount)}</text>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
`;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = async (req, res) => {
|
module.exports = async (req, res) => {
|
||||||
const { username, repo } = req.query;
|
const { username, repo } = req.query;
|
||||||
|
|
||||||
|
3
jest.config.js
Normal file
3
jest.config.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
module.exports = {
|
||||||
|
clearMocks: true,
|
||||||
|
};
|
5008
package-lock.json
generated
5008
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
11
package.json
11
package.json
@ -1,15 +1,20 @@
|
|||||||
{
|
{
|
||||||
"name": "github-readme-stats",
|
"name": "github-readme-stats",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "",
|
"description": "Dynamically generate stats for your github readmes",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "jest",
|
||||||
|
"test:watch": "jest --watch"
|
||||||
},
|
},
|
||||||
"author": "Anurag Hazra",
|
"author": "Anurag Hazra",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"axios": "^0.19.2"
|
"@testing-library/dom": "^7.20.0",
|
||||||
|
"@testing-library/jest-dom": "^5.11.0",
|
||||||
|
"axios": "^0.19.2",
|
||||||
|
"axios-mock-adapter": "^1.18.1",
|
||||||
|
"jest": "^26.1.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dotenv": "^8.2.0"
|
"dotenv": "^8.2.0"
|
||||||
|
70
src/fetchRepo.js
Normal file
70
src/fetchRepo.js
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
const axios = require("axios");
|
||||||
|
|
||||||
|
async function fetchRepo(username, reponame) {
|
||||||
|
if (!username || !reponame) {
|
||||||
|
throw new Error("Invalid username or reponame");
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await axios({
|
||||||
|
url: "https://api.github.com/graphql",
|
||||||
|
method: "post",
|
||||||
|
headers: {
|
||||||
|
Authorization: `bearer ${process.env.GITHUB_TOKEN}`,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
query: `
|
||||||
|
fragment RepoInfo on Repository {
|
||||||
|
name
|
||||||
|
stargazers {
|
||||||
|
totalCount
|
||||||
|
}
|
||||||
|
description
|
||||||
|
primaryLanguage {
|
||||||
|
color
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
forkCount
|
||||||
|
}
|
||||||
|
query getRepo($login: String!, $repo: String!) {
|
||||||
|
user(login: $login) {
|
||||||
|
repository(name: $repo) {
|
||||||
|
...RepoInfo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
organization(login: $login) {
|
||||||
|
repository(name: $repo) {
|
||||||
|
...RepoInfo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
variables: {
|
||||||
|
login: username,
|
||||||
|
repo: reponame,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = res.data.data;
|
||||||
|
|
||||||
|
if (!data.user && !data.organization) {
|
||||||
|
throw new Error("Not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.organization === null && data.user) {
|
||||||
|
if (!data.user.repository) {
|
||||||
|
throw new Error("User Repository Not found");
|
||||||
|
}
|
||||||
|
return data.user.repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.user === null && data.organization) {
|
||||||
|
if (!data.organization.repository) {
|
||||||
|
throw new Error("Organization Repository Not found");
|
||||||
|
}
|
||||||
|
return data.organization.repository;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = fetchRepo;
|
75
src/fetchStats.js
Normal file
75
src/fetchStats.js
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
const axios = require("axios");
|
||||||
|
require("dotenv").config();
|
||||||
|
|
||||||
|
async function fetchStats(username) {
|
||||||
|
if (!username) throw Error("Invalid username");
|
||||||
|
|
||||||
|
const res = await axios({
|
||||||
|
url: "https://api.github.com/graphql",
|
||||||
|
method: "post",
|
||||||
|
headers: {
|
||||||
|
Authorization: `bearer ${process.env.GITHUB_TOKEN}`,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
query: `
|
||||||
|
query userInfo($login: String!) {
|
||||||
|
user(login: $login) {
|
||||||
|
name
|
||||||
|
repositoriesContributedTo(first: 100, contributionTypes: [COMMIT, ISSUE, PULL_REQUEST, REPOSITORY]) {
|
||||||
|
totalCount
|
||||||
|
}
|
||||||
|
contributionsCollection {
|
||||||
|
totalCommitContributions
|
||||||
|
}
|
||||||
|
pullRequests(first: 100) {
|
||||||
|
totalCount
|
||||||
|
}
|
||||||
|
issues(first: 100) {
|
||||||
|
totalCount
|
||||||
|
}
|
||||||
|
repositories(first: 100) {
|
||||||
|
nodes {
|
||||||
|
stargazers {
|
||||||
|
totalCount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
variables: {
|
||||||
|
login: username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const stats = {
|
||||||
|
name: "",
|
||||||
|
totalPRs: 0,
|
||||||
|
totalCommits: 0,
|
||||||
|
totalIssues: 0,
|
||||||
|
totalStars: 0,
|
||||||
|
contributedTo: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (res.data.errors) {
|
||||||
|
console.log(res.data.errors);
|
||||||
|
throw Error("Could not fetch user");
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = res.data.data.user;
|
||||||
|
|
||||||
|
stats.name = user.name;
|
||||||
|
stats.totalIssues = user.issues.totalCount;
|
||||||
|
stats.totalCommits = user.contributionsCollection.totalCommitContributions;
|
||||||
|
stats.totalPRs = user.pullRequests.totalCount;
|
||||||
|
stats.contributedTo = user.repositoriesContributedTo.totalCount;
|
||||||
|
|
||||||
|
stats.totalStars = user.repositories.nodes.reduce((prev, curr) => {
|
||||||
|
return prev + curr.stargazers.totalCount;
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = fetchStats;
|
59
src/renderRepoCard.js
Normal file
59
src/renderRepoCard.js
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
const { kFormatter, encodeHTML } = require("../src/utils");
|
||||||
|
|
||||||
|
const renderRepoCard = (repo) => {
|
||||||
|
const { name, description, primaryLanguage, stargazers, forkCount } = repo;
|
||||||
|
const height = 120;
|
||||||
|
const shiftText = primaryLanguage.name.length > 15 ? 0 : 30;
|
||||||
|
|
||||||
|
let desc = description || "No description provided";
|
||||||
|
if (desc.length > 55) {
|
||||||
|
desc = `${description.slice(0, 55)}..`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalStars = kFormatter(stargazers.totalCount);
|
||||||
|
const totalForks = kFormatter(forkCount);
|
||||||
|
return `
|
||||||
|
<svg width="400" height="${height}" viewBox="0 0 400 ${height}" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<style>
|
||||||
|
.header { font: 600 18px 'Segoe UI', Ubuntu, Sans-Serif; fill: #2F80ED }
|
||||||
|
.stat { font: 600 14px 'Segoe UI', Ubuntu, Sans-Serif; fill: #333 }
|
||||||
|
.star-icon { font: 600 18px 'Segoe UI', Ubuntu, Sans-Serif; }
|
||||||
|
.bold { font-weight: 700 }
|
||||||
|
.description { font: 400 13px 'Segoe UI', Ubuntu, Sans-Serif; fill: #586069 }
|
||||||
|
.gray { font: 400 12px 'Segoe UI', Ubuntu, Sans-Serif; fill: #586069 }
|
||||||
|
</style>
|
||||||
|
<rect x="0.5" y="0.5" width="399" height="99%" rx="4.5" fill="#FFFEFE" stroke="#E4E2E2"/>
|
||||||
|
<svg x="25" y="25" viewBox="0 0 16 16" version="1.1" width="16" height="16" fill="#586069">
|
||||||
|
<path fill-rule="evenodd" d="M2 2.5A2.5 2.5 0 014.5 0h8.75a.75.75 0 01.75.75v12.5a.75.75 0 01-.75.75h-2.5a.75.75 0 110-1.5h1.75v-2h-8a1 1 0 00-.714 1.7.75.75 0 01-1.072 1.05A2.495 2.495 0 012 11.5v-9zm10.5-1V9h-8c-.356 0-.694.074-1 .208V2.5a1 1 0 011-1h8zM5 12.25v3.25a.25.25 0 00.4.2l1.45-1.087a.25.25 0 01.3 0L8.6 15.7a.25.25 0 00.4-.2v-3.25a.25.25 0 00-.25-.25h-3.5a.25.25 0 00-.25.25z"></path>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<text x="50" y="38" class="header">${name}</text>
|
||||||
|
<text class="description" x="25" y="70">${encodeHTML(desc)}</text>
|
||||||
|
|
||||||
|
<g transform="translate(30, 100)">
|
||||||
|
<circle data-testid="lang-color" cx="0" cy="-5" r="6" fill="${
|
||||||
|
primaryLanguage.color
|
||||||
|
}" />
|
||||||
|
<text data-testid="lang" class="gray" x="15">${
|
||||||
|
primaryLanguage.name
|
||||||
|
}</text>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<g transform="translate(${155 - shiftText}, 100)">
|
||||||
|
<svg y="-12" viewBox="0 0 16 16" version="1.1" width="16" height="16" fill="#586069">
|
||||||
|
<path fill-rule="evenodd" d="M8 .25a.75.75 0 01.673.418l1.882 3.815 4.21.612a.75.75 0 01.416 1.279l-3.046 2.97.719 4.192a.75.75 0 01-1.088.791L8 12.347l-3.766 1.98a.75.75 0 01-1.088-.79l.72-4.194L.818 6.374a.75.75 0 01.416-1.28l4.21-.611L7.327.668A.75.75 0 018 .25zm0 2.445L6.615 5.5a.75.75 0 01-.564.41l-3.097.45 2.24 2.184a.75.75 0 01.216.664l-.528 3.084 2.769-1.456a.75.75 0 01.698 0l2.77 1.456-.53-3.084a.75.75 0 01.216-.664l2.24-2.183-3.096-.45a.75.75 0 01-.564-.41L8 2.694v.001z"></path>
|
||||||
|
</svg>
|
||||||
|
<text data-testid="stargazers" class="gray" x="25">${totalStars}</text>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<g transform="translate(${220 - shiftText}, 100)">
|
||||||
|
<svg y="-12" viewBox="0 0 16 16" version="1.1" width="16" height="16" fill="#586069">
|
||||||
|
<path fill-rule="evenodd" d="M5 3.25a.75.75 0 11-1.5 0 .75.75 0 011.5 0zm0 2.122a2.25 2.25 0 10-1.5 0v.878A2.25 2.25 0 005.75 8.5h1.5v2.128a2.251 2.251 0 101.5 0V8.5h1.5a2.25 2.25 0 002.25-2.25v-.878a2.25 2.25 0 10-1.5 0v.878a.75.75 0 01-.75.75h-4.5A.75.75 0 015 6.25v-.878zm3.75 7.378a.75.75 0 11-1.5 0 .75.75 0 011.5 0zm3-8.75a.75.75 0 100-1.5.75.75 0 000 1.5z"></path>
|
||||||
|
</svg>
|
||||||
|
<text data-testid="forkcount" class="gray" x="25">${totalForks}</text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = renderRepoCard;
|
97
src/renderStatsCard.js
Normal file
97
src/renderStatsCard.js
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
const { kFormatter } = require("../src/utils");
|
||||||
|
|
||||||
|
const createTextNode = ({ icon, label, value, lineHeight, id }) => {
|
||||||
|
const classname = icon === "★" && "star-icon";
|
||||||
|
const kValue = kFormatter(value);
|
||||||
|
return `
|
||||||
|
<tspan x="25" dy="${lineHeight}" class="stat bold">
|
||||||
|
<tspan data-testid="icon" class="icon ${classname}" fill="#4C71F2">${icon}</tspan> ${label}:</tspan>
|
||||||
|
<tspan data-testid="${id}" x="155" dy="0" class="stat">${kValue}</tspan>
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderStatsCard = (stats = {}, options = { hide: [] }) => {
|
||||||
|
const {
|
||||||
|
name,
|
||||||
|
totalStars,
|
||||||
|
totalCommits,
|
||||||
|
totalIssues,
|
||||||
|
totalPRs,
|
||||||
|
contributedTo,
|
||||||
|
} = stats;
|
||||||
|
const {
|
||||||
|
hide = [],
|
||||||
|
show_icons = false,
|
||||||
|
hide_border = false,
|
||||||
|
line_height = 25,
|
||||||
|
} = options;
|
||||||
|
|
||||||
|
const lheight = parseInt(line_height);
|
||||||
|
|
||||||
|
const STAT_MAP = {
|
||||||
|
stars: createTextNode({
|
||||||
|
icon: "★",
|
||||||
|
label: "Total Stars",
|
||||||
|
value: totalStars,
|
||||||
|
lineHeight: lheight,
|
||||||
|
id: "stars",
|
||||||
|
}),
|
||||||
|
commits: createTextNode({
|
||||||
|
icon: "🕗",
|
||||||
|
label: "Total Commits",
|
||||||
|
value: totalCommits,
|
||||||
|
lineHeight: lheight,
|
||||||
|
id: "commits",
|
||||||
|
}),
|
||||||
|
prs: createTextNode({
|
||||||
|
icon: "🔀",
|
||||||
|
label: "Total PRs",
|
||||||
|
value: totalPRs,
|
||||||
|
lineHeight: lheight,
|
||||||
|
id: "prs",
|
||||||
|
}),
|
||||||
|
issues: createTextNode({
|
||||||
|
icon: "ⓘ",
|
||||||
|
label: "Total Issues",
|
||||||
|
value: totalIssues,
|
||||||
|
lineHeight: lheight,
|
||||||
|
id: "issues",
|
||||||
|
}),
|
||||||
|
contribs: createTextNode({
|
||||||
|
icon: "📕",
|
||||||
|
label: "Contributed to",
|
||||||
|
value: contributedTo,
|
||||||
|
lineHeight: lheight,
|
||||||
|
id: "contribs",
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const statItems = Object.keys(STAT_MAP)
|
||||||
|
.filter((key) => !hide.includes(key))
|
||||||
|
.map((key) => STAT_MAP[key]);
|
||||||
|
|
||||||
|
const height = 45 + (statItems.length + 1) * lheight;
|
||||||
|
|
||||||
|
const border = `<rect data-testid="card-border" x="0.5" y="0.5" width="494" height="99%" rx="4.5" fill="#FFFEFE" stroke="#E4E2E2"/>`;
|
||||||
|
return `
|
||||||
|
<svg width="495" height="${height}" viewBox="0 0 495 ${height}" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<style>
|
||||||
|
.header { font: 600 18px 'Segoe UI', Ubuntu, Sans-Serif; fill: #2F80ED }
|
||||||
|
.stat { font: 600 14px 'Segoe UI', Ubuntu, Sans-Serif; fill: #333 }
|
||||||
|
.star-icon { font: 600 18px 'Segoe UI', Ubuntu, Sans-Serif; }
|
||||||
|
.bold { font-weight: 700 }
|
||||||
|
.icon {
|
||||||
|
display: ${!!show_icons ? "block" : "none"};
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
${hide_border ? "" : border}
|
||||||
|
|
||||||
|
<text x="25" y="35" class="header">${name}'s GitHub Stats</text>
|
||||||
|
<text y="45">
|
||||||
|
${statItems.toString().replace(/\,/gm, "")}
|
||||||
|
</text>
|
||||||
|
</svg>
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = renderStatsCard;
|
@ -7,7 +7,7 @@ const renderError = (message) => {
|
|||||||
</style>
|
</style>
|
||||||
<rect x="0.5" y="0.5" width="494" height="99%" rx="4.5" fill="#FFFEFE" stroke="#E4E2E2"/>
|
<rect x="0.5" y="0.5" width="494" height="99%" rx="4.5" fill="#FFFEFE" stroke="#E4E2E2"/>
|
||||||
<text x="25" y="45" class="text">Something went wrong! file an issue at https://git.io/JJmN9</text>
|
<text x="25" y="45" class="text">Something went wrong! file an issue at https://git.io/JJmN9</text>
|
||||||
<text x="25" y="65" class="text small">${message}</text>
|
<text id="message" x="25" y="65" class="text small">${message}</text>
|
||||||
</svg>
|
</svg>
|
||||||
`;
|
`;
|
||||||
};
|
};
|
83
tests/fetchRepo.test.js
Normal file
83
tests/fetchRepo.test.js
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
require("@testing-library/jest-dom");
|
||||||
|
const axios = require("axios");
|
||||||
|
const MockAdapter = require("axios-mock-adapter");
|
||||||
|
const fetchRepo = require("../src/fetchRepo");
|
||||||
|
|
||||||
|
const data_repo = {
|
||||||
|
repository: {
|
||||||
|
name: "convoychat",
|
||||||
|
stargazers: { totalCount: 38000 },
|
||||||
|
description: "Help us take over the world! React + TS + GraphQL Chat App",
|
||||||
|
primaryLanguage: {
|
||||||
|
color: "#2b7489",
|
||||||
|
id: "MDg6TGFuZ3VhZ2UyODc=",
|
||||||
|
name: "TypeScript",
|
||||||
|
},
|
||||||
|
forkCount: 100,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const data_user = {
|
||||||
|
data: {
|
||||||
|
user: { repository: data_repo },
|
||||||
|
organization: null,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const data_org = {
|
||||||
|
data: {
|
||||||
|
user: null,
|
||||||
|
organization: { repository: data_repo },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const mock = new MockAdapter(axios);
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
mock.reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Test fetchRepo", () => {
|
||||||
|
it("should fetch correct user repo", async () => {
|
||||||
|
mock.onPost("https://api.github.com/graphql").reply(200, data_user);
|
||||||
|
|
||||||
|
let repo = await fetchRepo("anuraghazra", "convoychat");
|
||||||
|
expect(repo).toStrictEqual(data_repo);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should fetch correct org repo", async () => {
|
||||||
|
mock.onPost("https://api.github.com/graphql").reply(200, data_org);
|
||||||
|
|
||||||
|
let repo = await fetchRepo("anuraghazra", "convoychat");
|
||||||
|
expect(repo).toStrictEqual(data_repo);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should throw error if user is found but repo is null", async () => {
|
||||||
|
mock
|
||||||
|
.onPost("https://api.github.com/graphql")
|
||||||
|
.reply(200, { data: { user: { repository: null }, organization: null } });
|
||||||
|
|
||||||
|
await expect(fetchRepo("anuraghazra", "convoychat")).rejects.toThrow(
|
||||||
|
"User Repository Not found"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should throw error if org is found but repo is null", async () => {
|
||||||
|
mock
|
||||||
|
.onPost("https://api.github.com/graphql")
|
||||||
|
.reply(200, { data: { user: null, organization: { repository: null } } });
|
||||||
|
|
||||||
|
await expect(fetchRepo("anuraghazra", "convoychat")).rejects.toThrow(
|
||||||
|
"Organization Repository Not found"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should throw error if both user & org data not found", async () => {
|
||||||
|
mock
|
||||||
|
.onPost("https://api.github.com/graphql")
|
||||||
|
.reply(200, { data: { user: null, organization: null } });
|
||||||
|
|
||||||
|
await expect(fetchRepo("anuraghazra", "convoychat")).rejects.toThrow(
|
||||||
|
"Not found"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
66
tests/fetchStats.test.js
Normal file
66
tests/fetchStats.test.js
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
require("@testing-library/jest-dom");
|
||||||
|
const axios = require("axios");
|
||||||
|
const MockAdapter = require("axios-mock-adapter");
|
||||||
|
const fetchStats = require("../src/fetchStats");
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
data: {
|
||||||
|
user: {
|
||||||
|
name: "Anurag Hazra",
|
||||||
|
repositoriesContributedTo: { totalCount: 61 },
|
||||||
|
contributionsCollection: { totalCommitContributions: 100 },
|
||||||
|
pullRequests: { totalCount: 300 },
|
||||||
|
issues: { totalCount: 200 },
|
||||||
|
repositories: {
|
||||||
|
nodes: [
|
||||||
|
{ stargazers: { totalCount: 100 } },
|
||||||
|
{ stargazers: { totalCount: 100 } },
|
||||||
|
{ stargazers: { totalCount: 100 } },
|
||||||
|
{ stargazers: { totalCount: 50 } },
|
||||||
|
{ stargazers: { totalCount: 50 } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const error = {
|
||||||
|
errors: [
|
||||||
|
{
|
||||||
|
type: "NOT_FOUND",
|
||||||
|
path: ["user"],
|
||||||
|
locations: [],
|
||||||
|
message: "Could not resolve to a User with the login of 'noname'.",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const mock = new MockAdapter(axios);
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
mock.reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Test fetchStats", () => {
|
||||||
|
it("should fetch correct stats", async () => {
|
||||||
|
mock.onPost("https://api.github.com/graphql").reply(200, data);
|
||||||
|
|
||||||
|
let stats = await fetchStats("anuraghazra");
|
||||||
|
expect(stats).toStrictEqual({
|
||||||
|
contributedTo: 61,
|
||||||
|
name: "Anurag Hazra",
|
||||||
|
totalCommits: 100,
|
||||||
|
totalIssues: 200,
|
||||||
|
totalPRs: 300,
|
||||||
|
totalStars: 400,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should throw error", async () => {
|
||||||
|
mock.onPost("https://api.github.com/graphql").reply(200, error);
|
||||||
|
|
||||||
|
await expect(fetchStats("anuraghazra")).rejects.toThrow(
|
||||||
|
"Could not fetch user"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
91
tests/renderRepoCard.test.js
Normal file
91
tests/renderRepoCard.test.js
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
require("@testing-library/jest-dom");
|
||||||
|
const renderRepoCard = require("../src/renderRepoCard");
|
||||||
|
|
||||||
|
const { queryByTestId } = require("@testing-library/dom");
|
||||||
|
|
||||||
|
const data_repo = {
|
||||||
|
repository: {
|
||||||
|
name: "convoychat",
|
||||||
|
stargazers: { totalCount: 38000 },
|
||||||
|
description: "Help us take over the world! React + TS + GraphQL Chat App",
|
||||||
|
primaryLanguage: {
|
||||||
|
color: "#2b7489",
|
||||||
|
id: "MDg6TGFuZ3VhZ2UyODc=",
|
||||||
|
name: "TypeScript",
|
||||||
|
},
|
||||||
|
forkCount: 100,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
describe("Test renderRepoCard", () => {
|
||||||
|
it("should render correctly", () => {
|
||||||
|
document.body.innerHTML = renderRepoCard(data_repo.repository);
|
||||||
|
|
||||||
|
expect(document.getElementsByClassName("header")[0]).toHaveTextContent(
|
||||||
|
"convoychat"
|
||||||
|
);
|
||||||
|
expect(document.getElementsByClassName("description")[0]).toHaveTextContent(
|
||||||
|
"Help us take over the world! React + TS + GraphQL Chat .."
|
||||||
|
);
|
||||||
|
expect(queryByTestId(document.body, "stargazers")).toHaveTextContent("38k");
|
||||||
|
expect(queryByTestId(document.body, "forkcount")).toHaveTextContent("100");
|
||||||
|
expect(queryByTestId(document.body, "lang")).toHaveTextContent(
|
||||||
|
"TypeScript"
|
||||||
|
);
|
||||||
|
expect(queryByTestId(document.body, "lang-color")).toHaveAttribute(
|
||||||
|
"fill",
|
||||||
|
"#2b7489"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should trim description", () => {
|
||||||
|
document.body.innerHTML = renderRepoCard({
|
||||||
|
...data_repo.repository,
|
||||||
|
description:
|
||||||
|
"Very long long long long long long long long text it should trim it",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(document.getElementsByClassName("description")[0]).toHaveTextContent(
|
||||||
|
"Very long long long long long long long long text it sh.."
|
||||||
|
);
|
||||||
|
|
||||||
|
// Should not trim
|
||||||
|
document.body.innerHTML = renderRepoCard({
|
||||||
|
...data_repo.repository,
|
||||||
|
description: "Small text should not trim",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(document.getElementsByClassName("description")[0]).toHaveTextContent(
|
||||||
|
"Small text should not trim"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should shift the text position depending on language length", () => {
|
||||||
|
document.body.innerHTML = renderRepoCard({
|
||||||
|
...data_repo.repository,
|
||||||
|
primaryLanguage: {
|
||||||
|
...data_repo.repository.primaryLanguage,
|
||||||
|
name: "Jupyter Notebook",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(document.getElementsByTagName("g")[1]).toHaveAttribute(
|
||||||
|
"transform",
|
||||||
|
"translate(155, 100)"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Small lang
|
||||||
|
document.body.innerHTML = renderRepoCard({
|
||||||
|
...data_repo.repository,
|
||||||
|
primaryLanguage: {
|
||||||
|
...data_repo.repository.primaryLanguage,
|
||||||
|
name: "Ruby",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(document.getElementsByTagName("g")[1]).toHaveAttribute(
|
||||||
|
"transform",
|
||||||
|
"translate(125, 100)"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
54
tests/renderStatsCard.test.js
Normal file
54
tests/renderStatsCard.test.js
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
require("@testing-library/jest-dom");
|
||||||
|
const renderStatsCard = require("../src/renderStatsCard");
|
||||||
|
|
||||||
|
const { getByTestId, queryByTestId } = require("@testing-library/dom");
|
||||||
|
|
||||||
|
describe("Test renderStatsCard", () => {
|
||||||
|
const stats = {
|
||||||
|
name: "Anurag Hazra",
|
||||||
|
totalStars: 100,
|
||||||
|
totalCommits: 200,
|
||||||
|
totalIssues: 300,
|
||||||
|
totalPRs: 400,
|
||||||
|
contributedTo: 500,
|
||||||
|
};
|
||||||
|
|
||||||
|
it("should render correctly", () => {
|
||||||
|
document.body.innerHTML = renderStatsCard(stats);
|
||||||
|
|
||||||
|
expect(document.getElementsByClassName("header")[0].textContent).toBe(
|
||||||
|
"Anurag Hazra's GitHub Stats"
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
document.body.getElementsByTagName("svg")[0].getAttribute("height")
|
||||||
|
).toBe("195");
|
||||||
|
expect(getByTestId(document.body, "stars").textContent).toBe("100");
|
||||||
|
expect(getByTestId(document.body, "commits").textContent).toBe("200");
|
||||||
|
expect(getByTestId(document.body, "issues").textContent).toBe("300");
|
||||||
|
expect(getByTestId(document.body, "prs").textContent).toBe("400");
|
||||||
|
expect(getByTestId(document.body, "contribs").textContent).toBe("500");
|
||||||
|
expect(queryByTestId(document.body, "card-border")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should hide individual stats", () => {
|
||||||
|
document.body.innerHTML = renderStatsCard(stats, {
|
||||||
|
hide: "['issues', 'prs', 'contribs']",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
document.body.getElementsByTagName("svg")[0].getAttribute("height")
|
||||||
|
).toBe("120");
|
||||||
|
expect(queryByTestId(document.body, "stars")).toBeDefined();
|
||||||
|
expect(queryByTestId(document.body, "commits")).toBeDefined();
|
||||||
|
expect(queryByTestId(document.body, "issues")).toBeNull();
|
||||||
|
expect(queryByTestId(document.body, "prs")).toBeNull();
|
||||||
|
expect(queryByTestId(document.body, "contribs")).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should hide_border", () => {
|
||||||
|
document.body.innerHTML = renderStatsCard(stats, { hide_border: true });
|
||||||
|
|
||||||
|
expect(queryByTestId(document.body, "card-border")).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
26
tests/utils.test.js
Normal file
26
tests/utils.test.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
const { kFormatter, encodeHTML, renderError } = require("../src/utils");
|
||||||
|
|
||||||
|
describe("Test utils.js", () => {
|
||||||
|
it("should test kFormatter", () => {
|
||||||
|
expect(kFormatter(1)).toBe(1);
|
||||||
|
expect(kFormatter(-1)).toBe(-1);
|
||||||
|
expect(kFormatter(500)).toBe(500);
|
||||||
|
expect(kFormatter(1000)).toBe("1k");
|
||||||
|
expect(kFormatter(10000)).toBe("10k");
|
||||||
|
expect(kFormatter(12345)).toBe("12.3k");
|
||||||
|
expect(kFormatter(9900000)).toBe("9900k");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should test encodeHTML", () => {
|
||||||
|
expect(encodeHTML(`<html>hello world<,.#4^&^@%!))`)).toBe(
|
||||||
|
"<html>hello world<,.#4^&^@%!))"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should test renderError", () => {
|
||||||
|
document.body.innerHTML = renderError("Something went wrong");
|
||||||
|
expect(document.getElementById("message").textContent).toBe(
|
||||||
|
"Something went wrong"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user