diff --git a/apps/backend/tests/public-instance/__snapshots__/api.test.js.snap b/apps/backend/tests/public-instance/__snapshots__/api.test.js.snap
index 784234ff773d8..dee4aef1f0943 100644
--- a/apps/backend/tests/public-instance/__snapshots__/api.test.js.snap
+++ b/apps/backend/tests/public-instance/__snapshots__/api.test.js.snap
@@ -699,7 +699,7 @@ exports[`Test /api contract > should match the public many-params response snaps
",
- "graphqlRequest": "{"query":"\\nquery userInfo($login: String!, $after: String, $includeMergedPullRequests: Boolean!, $includeDiscussions: Boolean!, $includeDiscussionsAnswers: Boolean!, $startTime: DateTime = null, $ownerAffiliations: [RepositoryAffiliation]) {\\n user(login: $login) {\\n name\\n login\\n commits: contributionsCollection(from: $startTime) {\\n totalCommitContributions\\n }\\n reviews: contributionsCollection {\\n totalPullRequestReviewContributions\\n }\\n repositoriesContributedTo(\\n first: 1\\n contributionTypes: [COMMIT, ISSUE, PULL_REQUEST, REPOSITORY]\\n ) {\\n totalCount\\n }\\n pullRequests(first: 1) {\\n totalCount\\n }\\n mergedPullRequests: pullRequests(states: MERGED) @include(if: $includeMergedPullRequests) {\\n totalCount\\n }\\n openIssues: issues(states: OPEN) {\\n totalCount\\n }\\n closedIssues: issues(states: CLOSED) {\\n totalCount\\n }\\n followers {\\n totalCount\\n }\\n repositoryDiscussions @include(if: $includeDiscussions) {\\n totalCount\\n }\\n repositoryDiscussionComments(onlyAnswers: true) @include(if: $includeDiscussionsAnswers) {\\n totalCount\\n }\\n ...RepoStars\\n }\\n}\\nfragment RepoStars on User {\\n repositories(\\n first: 100\\n after: $after\\n ownerAffiliations: $ownerAffiliations\\n orderBy: {direction: DESC, field: STARGAZERS}\\n ) {\\n totalCount\\n nodes {\\n ...RepoNode\\n }\\n pageInfo {\\n hasNextPage\\n endCursor\\n }\\n }\\n}\\nfragment RepoNode on Repository {\\n name\\n stargazerCount\\n}","variables":{"login":"anuraghazra","after":null,"includeMergedPullRequests":true,"includeDiscussions":true,"includeDiscussionsAnswers":true,"startTime":"2024-01-01T00:00:00Z","ownerAffiliations":["OWNER","COLLABORATOR"]}}",
+ "graphqlRequest": "{"query":"\\nquery userInfo($login: String!, $after: String, $includeMergedPullRequests: Boolean!, $includeDiscussions: Boolean!, $includeDiscussionsAnswers: Boolean!, $startTime: DateTime = null, $ownerAffiliations: [RepositoryAffiliation]) {\\n user(login: $login) {\\n name\\n login\\n commits: contributionsCollection(from: $startTime) {\\n totalCommitContributions\\n }\\n reviews: contributionsCollection {\\n totalPullRequestReviewContributions\\n }\\n repositoriesContributedTo(\\n first: 1\\n contributionTypes: [COMMIT, ISSUE, PULL_REQUEST, REPOSITORY]\\n ) {\\n totalCount\\n }\\n pullRequests(first: 1) {\\n totalCount\\n }\\n mergedPullRequests: pullRequests(states: MERGED) @include(if: $includeMergedPullRequests) {\\n totalCount\\n }\\n openIssues: issues(states: OPEN) {\\n totalCount\\n }\\n closedIssues: issues(states: CLOSED) {\\n totalCount\\n }\\n followers {\\n totalCount\\n }\\n repositoryDiscussions @include(if: $includeDiscussions) {\\n totalCount\\n }\\n repositoryDiscussionComments(onlyAnswers: true) @include(if: $includeDiscussionsAnswers) {\\n totalCount\\n }\\n contributionsCollection {\\n contributionYears\\n }\\n ...RepoStars\\n }\\n}\\nfragment RepoStars on User {\\n repositories(\\n first: 100\\n after: $after\\n ownerAffiliations: $ownerAffiliations\\n orderBy: {direction: DESC, field: STARGAZERS}\\n ) {\\n totalCount\\n nodes {\\n ...RepoNode\\n }\\n pageInfo {\\n hasNextPage\\n endCursor\\n }\\n }\\n}\\nfragment RepoNode on Repository {\\n name\\n stargazerCount\\n}","variables":{"login":"anuraghazra","after":null,"includeMergedPullRequests":true,"includeDiscussions":true,"includeDiscussionsAnswers":true,"startTime":"2024-01-01T00:00:00Z","ownerAffiliations":["OWNER","COLLABORATOR"]}}",
"headers": [
[
"Cache-Control",
diff --git a/apps/backend/tests/utils.js b/apps/backend/tests/utils.js
index 739845ca5b807..069297f7cd2b5 100644
--- a/apps/backend/tests/utils.js
+++ b/apps/backend/tests/utils.js
@@ -8,6 +8,9 @@ export const data_stats = {
name: "Anurag Hazra",
login: "anuraghazra",
repositoriesContributedTo: { totalCount: 51 },
+ contributionsCollection: {
+ contributionYears: [2022, 2024],
+ },
commits: {
totalCommitContributions: 200,
},
diff --git a/apps/frontend/src/pages/Home/buildCardUrl.test.ts b/apps/frontend/src/pages/Home/buildCardUrl.test.ts
index 350eecd12c848..9e3b3820868b6 100644
--- a/apps/frontend/src/pages/Home/buildCardUrl.test.ts
+++ b/apps/frontend/src/pages/Home/buildCardUrl.test.ts
@@ -53,7 +53,7 @@ describe("buildCardUrl", () => {
"?username=john" +
"&hide_title=true" +
// commas are percent-encoded (%2C) now that params go through URLSearchParams
- "&show=reviews%2Cdiscussions_started%2Cdiscussions_answered%2Cprs_merged%2Cprs_merged_percentage%2Cprs_commented%2Cprs_reviewed%2Cissues_commented" +
+ "&show=reviews%2Cdiscussions_started%2Cdiscussions_answered%2Cprs_merged%2Cprs_merged_percentage%2Cprs_commented%2Cprs_reviewed%2Cissues_commented%2Ccontributions" +
"&show_icons=true" +
"&include_all_commits=true",
);
diff --git a/apps/frontend/src/pages/Home/buildCardUrl.ts b/apps/frontend/src/pages/Home/buildCardUrl.ts
index 70743b823612c..28081a0c2564a 100644
--- a/apps/frontend/src/pages/Home/buildCardUrl.ts
+++ b/apps/frontend/src/pages/Home/buildCardUrl.ts
@@ -54,7 +54,7 @@ export function buildCardUrl(
}
if (showAllStats) {
url = url.show(
- "reviews,discussions_started,discussions_answered,prs_merged,prs_merged_percentage,prs_commented,prs_reviewed,issues_commented",
+ "reviews,discussions_started,discussions_answered,prs_merged,prs_merged_percentage,prs_commented,prs_reviewed,issues_commented,contributions",
);
}
if (showIcons) {
diff --git a/packages/core/src/api/index.js b/packages/core/src/api/index.js
index 52c381b122f54..c51328a3f5b75 100644
--- a/packages/core/src/api/index.js
+++ b/packages/core/src/api/index.js
@@ -105,6 +105,7 @@ export default async (
showStats.includes("issues_authored"),
showStats.includes("issues_commented"),
parseArray(role),
+ showStats.includes("contributions"),
pat,
);
diff --git a/packages/core/src/cards/stats.ts b/packages/core/src/cards/stats.ts
index a12e8fd0080b7..b5dc277b05a6c 100644
--- a/packages/core/src/cards/stats.ts
+++ b/packages/core/src/cards/stats.ts
@@ -247,6 +247,7 @@ const renderStatsCard = (
totalPRsReviewed,
totalIssuesAuthored,
totalIssuesCommented,
+ totalContributions,
rank,
} = stats;
const {
@@ -293,6 +294,16 @@ const renderStatsCard = (
value: totalStars,
id: "stars",
};
+
+ if (show.includes("contributions")) {
+ STATS["contributions"] = {
+ icon: icons.contributions,
+ label: i18n.t("statcard.contributions"),
+ value: totalContributions,
+ id: "contributions",
+ };
+ }
+
STATS["commits"] = {
icon: icons.commits,
label: `${i18n.t("statcard.commits")}${getTotalCommitsYearLabel(
diff --git a/packages/core/src/common/icons.ts b/packages/core/src/common/icons.ts
index 64bc3b523f310..9e8b2f636a94c 100644
--- a/packages/core/src/common/icons.ts
+++ b/packages/core/src/common/icons.ts
@@ -38,6 +38,7 @@ const icons = {
discussions_started: ``,
discussions_answered: ``,
comments: ``,
+ contributions: ``,
gist: ``,
};
diff --git a/packages/core/src/fetchers/stats.ts b/packages/core/src/fetchers/stats.ts
index c0f082fa7f718..1ee51fc694597 100644
--- a/packages/core/src/fetchers/stats.ts
+++ b/packages/core/src/fetchers/stats.ts
@@ -11,6 +11,7 @@ import type { GraphQLResponse } from "../common/http.js";
import { logger } from "../common/log.js";
import { buildSearchFilter, parseOwnerAffiliations } from "../common/ops.js";
import { retryer } from "../common/retryer.js";
+import { buildContributionsDocument } from "../graphql/contributionsDocument.js";
import {
UserInfoDocument,
UserReposDocument,
@@ -274,6 +275,63 @@ const fetchRepoUserStats = async (
return stats;
};
+/**
+ * Fetch all-time contributions by building a single GraphQL query
+ * for all the given years.
+ *
+ * Whether private contributions are included depends on the user's profile settings:
+ * https://docs.github.com/en/account-and-profile/how-tos/contribution-settings/manage-visibility-settings-for-private-contributions-and-achievements#changing-the-visibility-of-your-private-contributions
+ */
+const fetchTotalContributions = async (
+ username: string,
+ years: Array,
+ pat: string | null = null,
+): Promise => {
+ if (years.length === 0) {
+ return 0;
+ }
+
+ const contributionsFetcher = createGraphQLFetcher(
+ buildContributionsDocument(years),
+ "bearer",
+ );
+
+ const contribRes = await retryer(
+ contributionsFetcher,
+ { login: username },
+ pat,
+ );
+
+ if (contribRes.data.errors) {
+ logger.error(contribRes.data.errors);
+ const firstError = contribRes.data.errors[0];
+ if (firstError?.message) {
+ throw new CustomError(
+ wrapTextMultiline(firstError.message, 525, 12)[0] ?? "",
+ contribRes.statusText,
+ );
+ }
+ throw new CustomError(
+ "Something went wrong while trying to retrieve the contributions data using the GraphQL API.",
+ CustomError.GRAPHQL_ERROR,
+ );
+ }
+
+ const user = contribRes.data.data.user;
+ if (!user) {
+ return 0;
+ }
+
+ let total = 0;
+ for (const year of years) {
+ const yearBlock = user[`year_${year}`];
+ if (yearBlock?.contributionCalendar.totalContributions) {
+ total += yearBlock.contributionCalendar.totalContributions;
+ }
+ }
+ return total;
+};
+
/**
* Fetch stats for a given username.
*
@@ -292,6 +350,7 @@ const fetchRepoUserStats = async (
* @param include_issues_authored Include count of issues authored.
* @param include_issues_commented Include count of issues commented.
* @param ownerAffiliations Owner affiliations. Default: OWNER.
+ * @param include_contributions Include all-time contributions.
* @param pat Optional PAT override.
* @returns Stats data.
*/
@@ -311,6 +370,7 @@ const fetchStats = async (
include_issues_authored = false,
include_issues_commented = false,
ownerAffiliations: Array = [],
+ include_contributions = false,
pat: string | null = null,
): Promise => {
if (!username) {
@@ -334,6 +394,7 @@ const fetchStats = async (
totalPRsReviewed: 0,
totalIssuesAuthored: 0,
totalIssuesCommented: 0,
+ totalContributions: 0,
rank: { level: "C", percentile: 100 },
};
const affiliations = parseOwnerAffiliations(ownerAffiliations);
@@ -421,6 +482,14 @@ const fetchStats = async (
}
stats.contributedTo = user.repositoriesContributedTo.totalCount;
+ if (include_contributions) {
+ stats.totalContributions = await fetchTotalContributions(
+ username,
+ user.contributionsCollection.contributionYears,
+ pat,
+ );
+ }
+
// Retrieve stars while filtering out repositories to be hidden.
const allExcludedRepos = [
...exclude_repo,
diff --git a/packages/core/src/fetchers/types.ts b/packages/core/src/fetchers/types.ts
index da070f3daa61d..6fa65c44f44ce 100644
--- a/packages/core/src/fetchers/types.ts
+++ b/packages/core/src/fetchers/types.ts
@@ -49,6 +49,7 @@ export interface StatsData {
totalPRsReviewed: number;
totalIssuesAuthored: number;
totalIssuesCommented: number;
+ totalContributions: number;
rank: { level: string; percentile: number };
}
diff --git a/packages/core/src/graphql/contributionsDocument.ts b/packages/core/src/graphql/contributionsDocument.ts
new file mode 100644
index 0000000000000..e9cf51ebfb006
--- /dev/null
+++ b/packages/core/src/graphql/contributionsDocument.ts
@@ -0,0 +1,43 @@
+import type { YearContributionsFragment } from "./generated/stats.js";
+import { graphqlDocument } from "./graphqlDocument.js";
+
+interface ContributionsQueryVariables {
+ login: string;
+}
+
+interface ContributionsQuery {
+ user: Record<`year_${number}`, YearContributionsFragment> | null;
+}
+
+/**
+ * Build the all-time contributions query, one aliased `contributionsCollection` field per year.
+ * The shape is only known at runtime.
+ *
+ * @param years Contribution years, one `year_` alias each.
+ * @returns Document for `createGraphQLFetcher`.
+ */
+const buildContributionsDocument = (years: Array) => {
+ const yearFields = years
+ .map((year) => {
+ // without "to", 2024-01-01 would count toward year=2023
+ const from = `${year}-01-01T00:00:00Z`;
+ const to = `${year}-12-31T23:59:59Z`;
+ return `year_${year}: contributionsCollection(from: "${from}", to: "${to}") { ...YearContributions }`;
+ })
+ .join("\n");
+
+ // fragment must match queries/stats.graphql, which generates its type
+ return graphqlDocument(`
+query userContributions($login: String!) {
+ user(login: $login) {
+ ${yearFields}
+ }
+}
+fragment YearContributions on ContributionsCollection {
+ contributionCalendar {
+ totalContributions
+ }
+}`);
+};
+
+export { buildContributionsDocument };
diff --git a/packages/core/src/graphql/generated/stats.ts b/packages/core/src/graphql/generated/stats.ts
index e8d39af0df2ed..c2b1d718d721a 100644
--- a/packages/core/src/graphql/generated/stats.ts
+++ b/packages/core/src/graphql/generated/stats.ts
@@ -63,6 +63,7 @@ export type UserInfoQuery = {
followers: { totalCount: number };
repositoryDiscussions?: { totalCount: number };
repositoryDiscussionComments?: { totalCount: number };
+ contributionsCollection: { contributionYears: Array };
repositories: {
totalCount: number;
nodes: Array<{ name: string; stargazerCount: number } | null> | null;
@@ -71,6 +72,10 @@ export type UserInfoQuery = {
} | null;
};
+export type YearContributionsFragment = {
+ contributionCalendar: { totalContributions: number };
+};
+
export const UserReposDocument = graphqlDocument<
UserReposQuery,
UserReposQueryVariables
@@ -143,6 +148,9 @@ query userInfo($login: String!, $after: String, $includeMergedPullRequests: Bool
repositoryDiscussionComments(onlyAnswers: true) @include(if: $includeDiscussionsAnswers) {
totalCount
}
+ contributionsCollection {
+ contributionYears
+ }
...RepoStars
}
}
diff --git a/packages/core/src/graphql/graphqlDocument.ts b/packages/core/src/graphql/graphqlDocument.ts
index 355f121db9789..be385eda23851 100644
--- a/packages/core/src/graphql/graphqlDocument.ts
+++ b/packages/core/src/graphql/graphqlDocument.ts
@@ -1,8 +1,7 @@
/**
* A GraphQL query paired with the types of its result and variables.
*
- * `text` is what goes over the wire; the other two members only carry types and are
- * never assigned.
+ * `text` is what goes over the wire; the other two members only carry types and are never assigned.
* Documents are generated from `queries/*.graphql`
* See `scripts/generate-graphql-types.js`.
*/
diff --git a/packages/core/src/graphql/queries/stats.graphql b/packages/core/src/graphql/queries/stats.graphql
index 8a4114fd0cb00..d99a27b086d55 100644
--- a/packages/core/src/graphql/queries/stats.graphql
+++ b/packages/core/src/graphql/queries/stats.graphql
@@ -78,6 +78,15 @@ query userInfo(
@include(if: $includeDiscussionsAnswers) {
totalCount
}
+ contributionsCollection {
+ contributionYears
+ }
...RepoStars
}
}
+
+fragment YearContributions on ContributionsCollection {
+ contributionCalendar {
+ totalContributions
+ }
+}
diff --git a/packages/core/src/translations.ts b/packages/core/src/translations.ts
index 385d4759f0d27..0786e119d9876 100644
--- a/packages/core/src/translations.ts
+++ b/packages/core/src/translations.ts
@@ -524,6 +524,9 @@ const statCardLocales = ({
"statcard.issues-commented": {
en: "Issues Commented",
},
+ "statcard.contributions": {
+ en: "Total Contributions",
+ },
"statcard.prs-merged": {
en: "Total PRs Merged",
ar: "مجموع طلبات السحب المُدمجة",
diff --git a/packages/core/tests/fetchStats.test.ts b/packages/core/tests/fetchStats.test.ts
index 47c277d681b38..8dddbf37d4f6b 100644
--- a/packages/core/tests/fetchStats.test.ts
+++ b/packages/core/tests/fetchStats.test.ts
@@ -17,6 +17,9 @@ const data_stats = {
user: {
name: "Anurag Hazra",
repositoriesContributedTo: { totalCount: 61 },
+ contributionsCollection: {
+ contributionYears: [2022, 2024],
+ },
commits: {
totalCommitContributions: 100,
},
@@ -104,6 +107,15 @@ const data_repo_zero_stars = {
},
};
+const data_contributions = {
+ data: {
+ user: {
+ year_2022: { contributionCalendar: { totalContributions: 150 } },
+ year_2024: { contributionCalendar: { totalContributions: 200 } },
+ },
+ },
+};
+
const error = {
errors: [
{
@@ -129,6 +141,9 @@ beforeEach(() => {
if (req.variables?.startTime?.startsWith("2003")) {
return [200, data_year2003];
}
+ if (req.query.includes("contributionCalendar")) {
+ return [200, data_contributions];
+ }
return [
200,
req.query.includes("totalCommitContributions") ? data_stats : data_repo,
@@ -172,6 +187,7 @@ describe("Test fetchStats", () => {
totalPRsReviewed: 0,
totalIssuesAuthored: 0,
totalIssuesCommented: 0,
+ totalContributions: 0,
rank,
});
});
@@ -213,6 +229,7 @@ describe("Test fetchStats", () => {
totalPRsReviewed: 0,
totalIssuesAuthored: 0,
totalIssuesCommented: 0,
+ totalContributions: 0,
rank,
});
});
@@ -262,6 +279,7 @@ describe("Test fetchStats", () => {
totalPRsReviewed: 0,
totalIssuesAuthored: 0,
totalIssuesCommented: 0,
+ totalContributions: 0,
rank,
});
});
@@ -320,6 +338,7 @@ describe("Test fetchStats", () => {
totalPRsReviewed: 0,
totalIssuesAuthored: 0,
totalIssuesCommented: 0,
+ totalContributions: 0,
rank,
});
});
@@ -357,6 +376,7 @@ describe("Test fetchStats", () => {
totalPRsReviewed: 0,
totalIssuesAuthored: 0,
totalIssuesCommented: 0,
+ totalContributions: 0,
rank,
});
});
@@ -394,6 +414,7 @@ describe("Test fetchStats", () => {
totalPRsReviewed: 0,
totalIssuesAuthored: 0,
totalIssuesCommented: 0,
+ totalContributions: 0,
rank,
});
});
@@ -431,6 +452,7 @@ describe("Test fetchStats", () => {
totalPRsReviewed: 0,
totalIssuesAuthored: 0,
totalIssuesCommented: 0,
+ totalContributions: 0,
rank,
});
});
@@ -510,6 +532,7 @@ describe("Test fetchStats", () => {
totalPRsReviewed: 0,
totalIssuesAuthored: 0,
totalIssuesCommented: 0,
+ totalContributions: 0,
rank,
});
});
@@ -544,6 +567,7 @@ describe("Test fetchStats", () => {
totalPRsReviewed: 0,
totalIssuesAuthored: 0,
totalIssuesCommented: 0,
+ totalContributions: 0,
rank,
});
});
@@ -587,10 +611,110 @@ describe("Test fetchStats", () => {
totalPRsAuthored: 0,
totalPRsCommented: 0,
totalPRsReviewed: 0,
+ totalContributions: 0,
rank,
});
});
+ it("should fetch total contributions when include_contributions is true", async () => {
+ const stats = await fetchStats(
+ "anuraghazra",
+ false,
+ [],
+ false,
+ false,
+ false,
+ undefined,
+ [],
+ [],
+ false,
+ false,
+ false,
+ false,
+ false,
+ [],
+ true, // include_contributions
+ );
+
+ expect(stats.totalContributions).toBe(350);
+ });
+
+ it("should throw when the contributions query returns an error", async () => {
+ mock.onPost("https://api.github.com/graphql").reply((cfg) => {
+ const req = JSON.parse(cfg.data as string) as { query: string };
+ if (req.query.includes("contributionCalendar")) {
+ return [
+ 200,
+ {
+ data: null,
+ errors: [{ message: "Some test GraphQL error" }],
+ },
+ ];
+ }
+ return [
+ 200,
+ req.query.includes("totalCommitContributions") ? data_stats : data_repo,
+ ];
+ });
+
+ await expect(
+ fetchStats(
+ "anuraghazra",
+ false,
+ [],
+ false,
+ false,
+ false,
+ undefined,
+ [],
+ [],
+ false,
+ false,
+ false,
+ false,
+ false,
+ [],
+ true, // include_contributions
+ ),
+ ).rejects.toThrow("Some test GraphQL error");
+ });
+
+ it("should throw a generic error when the contributions query returns an error without a message", async () => {
+ mock.onPost("https://api.github.com/graphql").reply((cfg) => {
+ const req = JSON.parse(cfg.data as string) as { query: string };
+ if (req.query.includes("contributionCalendar")) {
+ return [200, { data: null, errors: [{ type: "SOME_ERROR" }] }];
+ }
+ return [
+ 200,
+ req.query.includes("totalCommitContributions") ? data_stats : data_repo,
+ ];
+ });
+
+ await expect(
+ fetchStats(
+ "anuraghazra",
+ false,
+ [],
+ false,
+ false,
+ false,
+ undefined,
+ [],
+ [],
+ false,
+ false,
+ false,
+ false,
+ false,
+ [],
+ true, // include_contributions
+ ),
+ ).rejects.toThrow(
+ "Something went wrong while trying to retrieve the contributions data using the GraphQL API.",
+ );
+ });
+
it("should return correct data when user don't have any pull requests", async () => {
mock
.onPost("https://api.github.com/graphql")
@@ -624,6 +748,7 @@ describe("Test fetchStats", () => {
totalPRsAuthored: 0,
totalPRsCommented: 0,
totalPRsReviewed: 0,
+ totalContributions: 0,
rank,
});
});
diff --git a/packages/core/tests/renderStatsCard.test.ts b/packages/core/tests/renderStatsCard.test.ts
index 5e3b6831bdee8..8201e5ad8639b 100644
--- a/packages/core/tests/renderStatsCard.test.ts
+++ b/packages/core/tests/renderStatsCard.test.ts
@@ -25,6 +25,7 @@ const stats: StatsData = {
totalPRsReviewed: 100,
totalIssuesAuthored: 100,
totalIssuesCommented: 100,
+ totalContributions: 5000,
rank: { level: "A+", percentile: 40 },
};
@@ -57,6 +58,7 @@ describe("Test renderStatsCard", () => {
expect(
screen.queryByTestId("prs_merged_percentage"),
).not.toBeInTheDocument();
+ expect(screen.queryByTestId("contributions")).not.toBeInTheDocument();
});
it("should have proper name apostrophe", () => {
@@ -121,6 +123,14 @@ describe("Test renderStatsCard", () => {
expect(screen.queryByTestId("prs_merged_percentage")).toBeDefined();
});
+ it("should show contributions stat when included in show list", () => {
+ document.body.innerHTML = renderStatsCard(stats, {
+ show: ["contributions"],
+ });
+
+ expect(screen.getByTestId("contributions").textContent).toBe("5k");
+ });
+
it("should hide_rank", () => {
document.body.innerHTML = renderStatsCard(stats, { hide_rank: true });