forked from mirrored/github-readme-stats
Compare commits
41
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6883fec5c7 | ||
|
|
90f1ed40f9 | ||
|
|
5d42af59c0 | ||
|
|
822efbf6cb | ||
|
|
84c9d354b3 | ||
|
|
d56b294ca1 | ||
|
|
6f8bd9fe10 | ||
|
|
f0daab11c8 | ||
|
|
3a07cb02db | ||
|
|
510cf6266c | ||
|
|
406dbc5656 | ||
|
|
b8faef6857 | ||
|
|
986070a597 | ||
|
|
51e7887b95 | ||
|
|
098331069a | ||
|
|
f86059f097 | ||
|
|
aaf710c024 | ||
|
|
d5d8912954 | ||
|
|
6b7e91abb6 | ||
|
|
f8e32f9e0c | ||
|
|
fe1ca87e33 | ||
|
|
9a909ff0a4 | ||
|
|
dbf6560781 | ||
|
|
8886b93a7e | ||
|
|
41d6760abd | ||
|
|
c7d4c29757 | ||
|
|
0de23d3762 | ||
|
|
107f7ca52c | ||
|
|
40735124ed | ||
|
|
f9b9015ec2 | ||
|
|
eacc3cd29c | ||
|
|
c1324b31bf | ||
|
|
dc8852dd6b | ||
|
|
b0bb994ad3 | ||
|
|
192170c111 | ||
|
|
4c2307ab4b | ||
|
|
d5c94b6f57 | ||
|
|
a481021dab | ||
|
|
579cb7d175 | ||
|
|
84610521a2 | ||
|
|
c2c07ea022 |
@@ -0,0 +1,33 @@
|
||||
name: Test Deployment
|
||||
on:
|
||||
deployment_status:
|
||||
|
||||
jobs:
|
||||
e2eTests:
|
||||
if:
|
||||
github.event_name == 'deployment_status' &&
|
||||
github.event.deployment_status.state == 'success'
|
||||
name: Perform 2e2 tests
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [16.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
env:
|
||||
CI: true
|
||||
|
||||
- name: Run end-to-end tests.
|
||||
run: npm run test:e2e
|
||||
env:
|
||||
VERCEL_PREVIEW_URL: ${{ github.event.deployment_status.target_url }}
|
||||
@@ -0,0 +1,31 @@
|
||||
name: Close empty issues and templates
|
||||
on:
|
||||
issues:
|
||||
types:
|
||||
- reopened
|
||||
- opened
|
||||
- edited
|
||||
|
||||
jobs:
|
||||
closeEmptyIssuesAndTemplates:
|
||||
name: Close empty issues
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3 # NOTE: Retrieve issue templates.
|
||||
|
||||
- name: Run empty issues closer action
|
||||
uses: rickstaa/empty-issues-closer-action@v1
|
||||
env:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
close_comment:
|
||||
Closing this issue because it appears to be empty. Please update the
|
||||
issue for it to be reopened.
|
||||
open_comment:
|
||||
Reopening this issue because the author provided more information.
|
||||
check_templates: true
|
||||
template_close_comment:
|
||||
Closing this issue since the issue template was not filled in.
|
||||
Please provide us with more information to have this issue reopened.
|
||||
template_open_comment:
|
||||
Reopening this issue because the author provided more information.
|
||||
@@ -1,5 +1,4 @@
|
||||
name: Generate Theme Readme
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
@@ -8,19 +7,25 @@ on:
|
||||
- "themes/index.js"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
generateThemeDoc:
|
||||
runs-on: ubuntu-latest
|
||||
name: Generate theme doc
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [16.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: setup node
|
||||
uses: actions/setup-node@v1
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: "12.x"
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: npm
|
||||
|
||||
- name: npm install, generate readme
|
||||
run: |
|
||||
npm install
|
||||
npm ci
|
||||
npm run theme-readme-gen
|
||||
env:
|
||||
CI: true
|
||||
|
||||
@@ -6,6 +6,6 @@ jobs:
|
||||
triage:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/labeler@v2
|
||||
- uses: actions/labeler@v4
|
||||
with:
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
|
||||
@@ -1,24 +1,33 @@
|
||||
name: Theme preview
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, synchronize, reopened]
|
||||
types: [opened, edited, reopened, synchronize]
|
||||
branches:
|
||||
- master
|
||||
- theme-preview-script
|
||||
paths:
|
||||
- "themes/index.js"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
previewTheme:
|
||||
name: Install & Preview
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [16.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: npm
|
||||
|
||||
- uses: bahmutov/npm-install@v1
|
||||
with:
|
||||
useLockFile: false
|
||||
|
||||
- run: npm run preview-theme
|
||||
env:
|
||||
CI: true
|
||||
PERSONAL_TOKEN: ${{ secrets.PERSONAL_TOKEN }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
name: Close stale theme pull requests that have the 'invalid' label.
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 */7 * *"
|
||||
|
||||
jobs:
|
||||
closeOldThemePrs:
|
||||
name: Close stale 'invalid' theme PRs
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [16.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: npm
|
||||
|
||||
- uses: bahmutov/npm-install@v1
|
||||
with:
|
||||
useLockFile: false
|
||||
|
||||
- run: npm run close-stale-theme-prs
|
||||
env:
|
||||
STALE_DAYS: 15
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -1,5 +1,4 @@
|
||||
name: Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
@@ -10,20 +9,29 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Perform tests
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [16.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v1
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: "12.x"
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: npm
|
||||
|
||||
- name: Install & Test
|
||||
run: |
|
||||
npm install
|
||||
npm ci
|
||||
npm run test
|
||||
|
||||
- name: Run Prettier
|
||||
run: |
|
||||
npm run format:check
|
||||
|
||||
- name: Code Coverage
|
||||
uses: codecov/codecov-action@v1
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: Update top issues dashboard
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 */7 * *"
|
||||
- cron: "0 0 */3 * *"
|
||||
|
||||
jobs:
|
||||
showAndLabelTopIssues:
|
||||
@@ -13,8 +13,10 @@ jobs:
|
||||
env:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
filter: "1772"
|
||||
label: false
|
||||
dashboard: true
|
||||
dashboard_show_total_reactions: true
|
||||
top_issues: true
|
||||
top_bugs: true
|
||||
top_features: true
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
.vercel
|
||||
.env
|
||||
node_modules
|
||||
package-lock.json
|
||||
*.lock
|
||||
.vscode/
|
||||
.idea/
|
||||
|
||||
+19
-11
@@ -1,22 +1,25 @@
|
||||
require("dotenv").config();
|
||||
const {
|
||||
renderError,
|
||||
parseBoolean,
|
||||
parseArray,
|
||||
import * as dotenv from "dotenv";
|
||||
import { renderStatsCard } from "../src/cards/stats-card.js";
|
||||
import { blacklist } from "../src/common/blacklist.js";
|
||||
import {
|
||||
clampValue,
|
||||
CONSTANTS,
|
||||
} = require("../src/common/utils");
|
||||
const fetchStats = require("../src/fetchers/stats-fetcher");
|
||||
const renderStatsCard = require("../src/cards/stats-card");
|
||||
const blacklist = require("../src/common/blacklist");
|
||||
const { isLocaleAvailable } = require("../src/translations");
|
||||
parseArray,
|
||||
parseBoolean,
|
||||
renderError,
|
||||
} from "../src/common/utils.js";
|
||||
import { fetchStats } from "../src/fetchers/stats-fetcher.js";
|
||||
import { isLocaleAvailable } from "../src/translations.js";
|
||||
|
||||
module.exports = async (req, res) => {
|
||||
dotenv.config();
|
||||
|
||||
export default async (req, res) => {
|
||||
const {
|
||||
username,
|
||||
hide,
|
||||
hide_title,
|
||||
hide_border,
|
||||
card_width,
|
||||
hide_rank,
|
||||
show_icons,
|
||||
count_private,
|
||||
@@ -25,9 +28,11 @@ module.exports = async (req, res) => {
|
||||
title_color,
|
||||
icon_color,
|
||||
text_color,
|
||||
text_bold,
|
||||
bg_color,
|
||||
theme,
|
||||
cache_seconds,
|
||||
exclude_repo,
|
||||
custom_title,
|
||||
locale,
|
||||
disable_animations,
|
||||
@@ -49,6 +54,7 @@ module.exports = async (req, res) => {
|
||||
username,
|
||||
parseBoolean(count_private),
|
||||
parseBoolean(include_all_commits),
|
||||
parseArray(exclude_repo),
|
||||
);
|
||||
|
||||
const cacheSeconds = clampValue(
|
||||
@@ -65,12 +71,14 @@ module.exports = async (req, res) => {
|
||||
show_icons: parseBoolean(show_icons),
|
||||
hide_title: parseBoolean(hide_title),
|
||||
hide_border: parseBoolean(hide_border),
|
||||
card_width: parseInt(card_width, 10),
|
||||
hide_rank: parseBoolean(hide_rank),
|
||||
include_all_commits: parseBoolean(include_all_commits),
|
||||
line_height,
|
||||
title_color,
|
||||
icon_color,
|
||||
text_color,
|
||||
text_bold: parseBoolean(text_bold),
|
||||
bg_color,
|
||||
theme,
|
||||
custom_title,
|
||||
|
||||
+9
-10
@@ -1,16 +1,15 @@
|
||||
require("dotenv").config();
|
||||
const {
|
||||
renderError,
|
||||
parseBoolean,
|
||||
import { renderRepoCard } from "../src/cards/repo-card.js";
|
||||
import { blacklist } from "../src/common/blacklist.js";
|
||||
import {
|
||||
clampValue,
|
||||
CONSTANTS,
|
||||
} = require("../src/common/utils");
|
||||
const fetchRepo = require("../src/fetchers/repo-fetcher");
|
||||
const renderRepoCard = require("../src/cards/repo-card");
|
||||
const blacklist = require("../src/common/blacklist");
|
||||
const { isLocaleAvailable } = require("../src/translations");
|
||||
parseBoolean,
|
||||
renderError,
|
||||
} from "../src/common/utils.js";
|
||||
import { fetchRepo } from "../src/fetchers/repo-fetcher.js";
|
||||
import { isLocaleAvailable } from "../src/translations.js";
|
||||
|
||||
module.exports = async (req, res) => {
|
||||
export default async (req, res) => {
|
||||
const {
|
||||
username,
|
||||
repo,
|
||||
|
||||
+13
-11
@@ -1,17 +1,19 @@
|
||||
require("dotenv").config();
|
||||
const {
|
||||
renderError,
|
||||
import * as dotenv from "dotenv";
|
||||
import { renderTopLanguages } from "../src/cards/top-languages-card.js";
|
||||
import { blacklist } from "../src/common/blacklist.js";
|
||||
import {
|
||||
clampValue,
|
||||
parseBoolean,
|
||||
parseArray,
|
||||
CONSTANTS,
|
||||
} = require("../src/common/utils");
|
||||
const fetchTopLanguages = require("../src/fetchers/top-languages-fetcher");
|
||||
const renderTopLanguages = require("../src/cards/top-languages-card");
|
||||
const blacklist = require("../src/common/blacklist");
|
||||
const { isLocaleAvailable } = require("../src/translations");
|
||||
parseArray,
|
||||
parseBoolean,
|
||||
renderError,
|
||||
} from "../src/common/utils.js";
|
||||
import { fetchTopLanguages } from "../src/fetchers/top-languages-fetcher.js";
|
||||
import { isLocaleAvailable } from "../src/translations.js";
|
||||
|
||||
module.exports = async (req, res) => {
|
||||
dotenv.config();
|
||||
|
||||
export default async (req, res) => {
|
||||
const {
|
||||
username,
|
||||
hide,
|
||||
|
||||
+13
-11
@@ -1,16 +1,18 @@
|
||||
require("dotenv").config();
|
||||
const {
|
||||
renderError,
|
||||
parseBoolean,
|
||||
import * as dotenv from "dotenv";
|
||||
import { renderWakatimeCard } from "../src/cards/wakatime-card.js";
|
||||
import {
|
||||
clampValue,
|
||||
parseArray,
|
||||
CONSTANTS,
|
||||
} = require("../src/common/utils");
|
||||
const { isLocaleAvailable } = require("../src/translations");
|
||||
const { fetchWakatimeStats } = require("../src/fetchers/wakatime-fetcher");
|
||||
const wakatimeCard = require("../src/cards/wakatime-card");
|
||||
parseArray,
|
||||
parseBoolean,
|
||||
renderError,
|
||||
} from "../src/common/utils.js";
|
||||
import { fetchWakatimeStats } from "../src/fetchers/wakatime-fetcher.js";
|
||||
import { isLocaleAvailable } from "../src/translations.js";
|
||||
|
||||
module.exports = async (req, res) => {
|
||||
dotenv.config();
|
||||
|
||||
export default async (req, res) => {
|
||||
const {
|
||||
username,
|
||||
title_color,
|
||||
@@ -56,7 +58,7 @@ module.exports = async (req, res) => {
|
||||
res.setHeader("Cache-Control", `public, max-age=${cacheSeconds}`);
|
||||
|
||||
return res.send(
|
||||
wakatimeCard(stats, {
|
||||
renderWakatimeCard(stats, {
|
||||
custom_title,
|
||||
hide_title: parseBoolean(hide_title),
|
||||
hide_border: parseBoolean(hide_border),
|
||||
|
||||
+10
-1
@@ -1,3 +1,12 @@
|
||||
module.exports = {
|
||||
export default {
|
||||
clearMocks: true,
|
||||
transform: {},
|
||||
testEnvironment: "jsdom",
|
||||
coverageProvider: "v8",
|
||||
testPathIgnorePatterns: ["<rootDir>/node_modules/", "<rootDir>/tests/e2e/"],
|
||||
modulePathIgnorePatterns: ["<rootDir>/node_modules/", "<rootDir>/tests/e2e/"],
|
||||
coveragePathIgnorePatterns: [
|
||||
"<rootDir>/node_modules/",
|
||||
"<rootDir>/tests/E2E/",
|
||||
],
|
||||
};
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
export default {
|
||||
clearMocks: true,
|
||||
transform: {},
|
||||
testEnvironment: "node",
|
||||
coverageProvider: "v8",
|
||||
testMatch: ["<rootDir>/tests/e2e/**/*.test.js"],
|
||||
};
|
||||
Generated
+9398
File diff suppressed because it is too large
Load Diff
+15
-10
@@ -3,29 +3,33 @@
|
||||
"version": "1.0.0",
|
||||
"description": "Dynamically generate stats for your github readmes",
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"test": "jest --coverage",
|
||||
"test:watch": "jest --watch",
|
||||
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js --coverage",
|
||||
"test:watch": "node --experimental-vm-modules node_modules/jest/bin/jest.js --watch",
|
||||
"test:update:snapshot": "node --experimental-vm-modules node_modules/jest/bin/jest.js -u",
|
||||
"test:e2e": "node --experimental-vm-modules node_modules/jest/bin/jest.js --config jest.e2e.config.js",
|
||||
"theme-readme-gen": "node scripts/generate-theme-doc",
|
||||
"preview-theme": "node scripts/preview-theme",
|
||||
"generate-langs-json": "node scripts/generate-langs-json"
|
||||
"close-stale-theme-prs": "node scripts/close-stale-theme-prs",
|
||||
"generate-langs-json": "node scripts/generate-langs-json",
|
||||
"format": "./node_modules/.bin/prettier --write .",
|
||||
"format:check": "./node_modules/.bin/prettier --check ."
|
||||
},
|
||||
"author": "Anurag Hazra",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=13"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@actions/core": "^1.2.4",
|
||||
"@actions/core": "^1.9.1",
|
||||
"@actions/github": "^4.0.0",
|
||||
"@testing-library/dom": "^7.20.0",
|
||||
"@testing-library/jest-dom": "^5.11.0",
|
||||
"@testing-library/dom": "^8.17.1",
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@uppercod/css-to-object": "^1.1.1",
|
||||
"axios-mock-adapter": "^1.18.1",
|
||||
"color-contrast-checker": "^2.1.0",
|
||||
"hjson": "^3.2.2",
|
||||
"husky": "^4.2.5",
|
||||
"jest": "^26.1.0",
|
||||
"jest": "^29.0.3",
|
||||
"jest-environment-jsdom": "^29.0.3",
|
||||
"js-yaml": "^4.1.0",
|
||||
"lodash.snakecase": "^4.1.1",
|
||||
"parse-diff": "^0.7.0",
|
||||
@@ -36,6 +40,7 @@
|
||||
"dotenv": "^8.2.0",
|
||||
"emoji-name-map": "^1.2.8",
|
||||
"github-username-regex": "^1.0.0",
|
||||
"upgrade": "^1.1.0",
|
||||
"word-wrap": "^1.2.3"
|
||||
},
|
||||
"husky": {
|
||||
|
||||
@@ -67,12 +67,12 @@
|
||||
<img src="https://d2wvdrxmr8p0wf.cloudfront.net/static/giveindia.svg" alt="Give india logo" width="200" />
|
||||
</a>
|
||||
|
||||
Are you considering supporting the project by donating? Please DON'T!!
|
||||
Are you considering supporting the project by donating? Please DON'T!!
|
||||
|
||||
Instead, Help India fight the 2nd deadly wave of COVID-19.
|
||||
Thousands of people are dying in India because of a lack of Oxygen & also COVID-related infrastructure.
|
||||
Instead, Help India fight the 2nd deadly wave of COVID-19.
|
||||
Thousands of people are dying in India because of a lack of Oxygen & also COVID-related infrastructure.
|
||||
|
||||
Visit [https://indiafightscorona.giveindia.org](https://indiafightscorona.giveindia.org) and make a small donation to help us fight COVID and overcome this crisis.
|
||||
Visit [https://indiafightscorona.giveindia.org](https://indiafightscorona.giveindia.org) and make a small donation to help us fight COVID and overcome this crisis.
|
||||
A small donation goes a long way. :heart:
|
||||
</p>
|
||||
|
||||
@@ -160,18 +160,18 @@ You can customize the appearance of your `Stats Card` or `Repo Card` however you
|
||||
|
||||
#### Common Options:
|
||||
|
||||
- `title_color` - Card's title color _(hex color)_
|
||||
- `text_color` - Body text color _(hex color)_
|
||||
- `icon_color` - Icons color if available _(hex color)_
|
||||
- `border_color` - Card's border color _(hex color)_. (Does not apply when `hide_border` is enabled)
|
||||
- `bg_color` - Card's background color _(hex color)_ **or** a gradient in the form of _angle,start,end_
|
||||
- `hide_border` - Hides the card's border _(boolean)_
|
||||
- `theme` - name of the theme, choose from [all available themes](./themes/README.md)
|
||||
- `cache_seconds` - set the cache header manually _(min: 7200, max: 86400)_
|
||||
- `locale` - set the language in the card _(e.g. cn, de, es, etc.)_
|
||||
- `border_radius` - Corner rounding on the card_
|
||||
- `title_color` - Card's title color _(hex color)_. Default: `2f80ed`.
|
||||
- `text_color` - Body text color _(hex color)_. Default: `434d58`.
|
||||
- `icon_color` - Icons color if available _(hex color)_. Default: `4c71f2`.
|
||||
- `border_color` - Card's border color _(hex color)_. Default: `e4e2e2` (Does not apply when `hide_border` is enabled).
|
||||
- `bg_color` - Card's background color _(hex color)_ **or** a gradient in the form of _angle,start,end_. Default: `fffefe`
|
||||
- `hide_border` - Hides the card's border _(boolean)_. Default: `false`
|
||||
- `theme` - name of the theme, choose from [all available themes](./themes/README.md). Default: `default` theme.
|
||||
- `cache_seconds` - set the cache header manually _(min: 7200, max: 86400)_. Default: `14400 seconds (4 hours)`.
|
||||
- `locale` - set the language in the card _(e.g. cn, de, es, etc.)_. Default: `en`.
|
||||
- `border_radius` - Corner rounding on the card. Default: `4.5`.
|
||||
|
||||
> Note: The minimum of cache_seconds is currently 4 hours as a temporary fix for PATs exhaustion.
|
||||
> Note: The minimum of cache_seconds is currently 4 hours as a temporary fix for PATs exhaustion.
|
||||
|
||||
##### Gradient in bg_color
|
||||
|
||||
@@ -185,15 +185,21 @@ You can provide multiple comma-separated values in the bg_color option to render
|
||||
|
||||
#### Stats Card Exclusive Options:
|
||||
|
||||
- `hide` - Hides the [specified items](#hiding-individual-stats) from stats _(Comma-separated values)_
|
||||
- `hide_title` - _(boolean)_
|
||||
- `hide_rank` - _(boolean)_ hides the rank and automatically resizes the card width
|
||||
- `show_icons` - _(boolean)_
|
||||
- `include_all_commits` - Count total commits instead of just the current year commits _(boolean)_
|
||||
- `count_private` - Count private commits _(boolean)_
|
||||
- `line_height` - Sets the line-height between text _(number)_
|
||||
- `custom_title` - Sets a custom title for the card
|
||||
- `disable_animations` - Disables all animations in the card _(boolean)_
|
||||
- `hide` - Hides the [specified items](#hiding-individual-stats) from stats _(Comma-separated values)_. Default: `[] (blank array)`.
|
||||
- `hide_title` - _(boolean)_. Default: `false`.
|
||||
- `card_width` - Set the card's width manually _(number)_. Default: `500px (approx.)`.
|
||||
- `hide_rank` - _(boolean)_ hides the rank and automatically resizes the card width. Default: `false`.
|
||||
- `show_icons` - _(boolean)_. Default: `false`.
|
||||
- `include_all_commits` - Count total commits instead of just the current year commits _(boolean)_. Default: `false`.
|
||||
- `count_private` - Count private commits _(boolean)_. Default: `false`.
|
||||
- `line_height` - Sets the line-height between text _(number)_. Default: `25`.
|
||||
- `exclude_repo` - Exclude stars from specified repositories _(Comma-separated values)_. Default: `[] (blank array)`.
|
||||
- `custom_title` - Sets a custom title for the card. Default: `<username> Github Stats`.
|
||||
- `text_bold` - Use bold text _(boolean)_. Default: `true`.
|
||||
- `disable_animations` - Disables all animations in the card _(boolean)_. Default: `false`.
|
||||
|
||||
> Note on `hide_rank`:
|
||||
When hide_rank=`true`, the minimum card width is 270 px + the title length and padding.
|
||||
|
||||
#### Repo Card Exclusive Options:
|
||||
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
/**
|
||||
* @file Script that can be used to close stale theme PRs that have a `invalid` label.
|
||||
*/
|
||||
import * as dotenv from "dotenv";
|
||||
dotenv.config();
|
||||
|
||||
import { debug, setFailed } from "@actions/core";
|
||||
import github from "@actions/github";
|
||||
import { RequestError } from "@octokit/request-error";
|
||||
import { getGithubToken, getRepoInfo } from "./helpers.js";
|
||||
|
||||
// Script parameters
|
||||
const CLOSING_COMMENT = `
|
||||
\rThis PR has been automatically closed due to inactivity. Please feel free to reopen it if you need to continue working on it.\
|
||||
\rThank you for your contributions.
|
||||
`;
|
||||
|
||||
/**
|
||||
* Fetch open PRs from a given repository.
|
||||
* @param user The user name of the repository owner.
|
||||
* @param repo The name of the repository.
|
||||
* @returns The open PRs.
|
||||
*/
|
||||
export const fetchOpenPRs = async (octokit, user, repo) => {
|
||||
const openPRs = [];
|
||||
let hasNextPage = true;
|
||||
let endCursor;
|
||||
while (hasNextPage) {
|
||||
try {
|
||||
const { repository } = await octokit.graphql(
|
||||
`
|
||||
{
|
||||
repository(owner: "${user}", name: "${repo}") {
|
||||
open_prs: pullRequests(${
|
||||
endCursor ? `after: "${endCursor}", ` : ""
|
||||
}
|
||||
first: 100, states: OPEN, orderBy: {field: CREATED_AT, direction: DESC}) {
|
||||
nodes {
|
||||
number
|
||||
commits(last:1){
|
||||
nodes{
|
||||
commit{
|
||||
pushedDate
|
||||
}
|
||||
}
|
||||
}
|
||||
labels(first: 100, orderBy:{field: CREATED_AT, direction: DESC}) {
|
||||
nodes {
|
||||
name
|
||||
}
|
||||
}
|
||||
reviews(first: 1, states: CHANGES_REQUESTED, author: "github-actions[bot]") {
|
||||
nodes {
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
pageInfo {
|
||||
endCursor
|
||||
hasNextPage
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
);
|
||||
openPRs.push(...repository.open_prs.nodes);
|
||||
hasNextPage = repository.open_prs.pageInfo.hasNextPage;
|
||||
endCursor = repository.open_prs.pageInfo.endCursor;
|
||||
} catch (error) {
|
||||
if (error instanceof RequestError) {
|
||||
setFailed(`Could not retrieve top PRs using GraphQl: ${error.message}`);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
return openPRs;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve pull requests that have a given label.
|
||||
* @param pull The pull requests to check.
|
||||
* @param label The label to check for.
|
||||
*/
|
||||
export const pullsWithLabel = (pulls, label) => {
|
||||
return pulls.filter((pr) => {
|
||||
return pr.labels.nodes.some((lab) => lab.name === label);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if PR is stale. Meaning that it hasn't been updated in a given time.
|
||||
* @param {Object} pullRequest request object.
|
||||
* @param {number} days number of days.
|
||||
* @returns Boolean indicating if PR is stale.
|
||||
*/
|
||||
const isStale = (pullRequest, staleDays) => {
|
||||
const lastCommitDate = new Date(
|
||||
pullRequest.commits.nodes[0].commit.pushedDate,
|
||||
);
|
||||
if (pullRequest.reviews.nodes[0]) {
|
||||
const lastReviewDate = new Date(pullRequest.reviews.nodes[0].updatedAt);
|
||||
const lastUpdateDate =
|
||||
lastCommitDate >= lastReviewDate ? lastCommitDate : lastReviewDate;
|
||||
const now = new Date();
|
||||
return now - lastUpdateDate > 1000 * 60 * 60 * 24 * staleDays;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Main function.
|
||||
*/
|
||||
const run = async () => {
|
||||
try {
|
||||
// Create octokit client.
|
||||
const dryRun = process.env.DRY_RUN === "true" || false;
|
||||
const staleDays = process.env.STALE_DAYS || 15;
|
||||
debug("Creating octokit client...");
|
||||
const octokit = github.getOctokit(getGithubToken());
|
||||
const { owner, repo } = getRepoInfo(github.context);
|
||||
|
||||
// Retrieve all theme pull requests.
|
||||
debug("Retrieving all theme pull requests...");
|
||||
const prs = await fetchOpenPRs(octokit, owner, repo);
|
||||
const themePRs = pullsWithLabel(prs, "themes");
|
||||
const invalidThemePRs = pullsWithLabel(themePRs, "invalid");
|
||||
debug("Retrieving stale theme PRs...");
|
||||
const staleThemePRs = invalidThemePRs.filter((pr) =>
|
||||
isStale(pr, staleDays),
|
||||
);
|
||||
const staleThemePRsNumbers = staleThemePRs.map((pr) => pr.number);
|
||||
debug(`Found ${staleThemePRs.length} stale theme PRs`);
|
||||
|
||||
// Loop through all stale invalid theme pull requests and close them.
|
||||
for (const prNumber of staleThemePRsNumbers) {
|
||||
debug(`Closing #${prNumber} because it is stale...`);
|
||||
if (!dryRun) {
|
||||
await octokit.issues.createComment({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: prNumber,
|
||||
body: CLOSING_COMMENT,
|
||||
});
|
||||
await octokit.pulls.update({
|
||||
owner,
|
||||
repo,
|
||||
pull_number: prNumber,
|
||||
state: "closed",
|
||||
});
|
||||
} else {
|
||||
debug("Dry run enabled, skipping...");
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
setFailed(error.message);
|
||||
}
|
||||
};
|
||||
|
||||
run();
|
||||
@@ -1,26 +1,30 @@
|
||||
const fs = require('fs');
|
||||
const jsYaml = require('js-yaml');
|
||||
const axios = require('axios');
|
||||
import axios from "axios";
|
||||
import fs from "fs";
|
||||
import jsYaml from "js-yaml";
|
||||
|
||||
const LANGS_FILEPATH = "./src/common/languageColors.json"
|
||||
const LANGS_FILEPATH = "./src/common/languageColors.json";
|
||||
|
||||
//Retrieve languages from github linguist repository yaml file
|
||||
//@ts-ignore
|
||||
axios.get("https://raw.githubusercontent.com/github/linguist/master/lib/linguist/languages.yml")
|
||||
.then((response) => {
|
||||
axios
|
||||
.get(
|
||||
"https://raw.githubusercontent.com/github/linguist/master/lib/linguist/languages.yml",
|
||||
)
|
||||
.then((response) => {
|
||||
//and convert them to a JS Object
|
||||
const languages = jsYaml.load(response.data);
|
||||
|
||||
//and convert them to a JS Object
|
||||
const languages = jsYaml.load(response.data);
|
||||
const languageColors = {};
|
||||
|
||||
const languageColors = {};
|
||||
//Filter only language colors from the whole file
|
||||
Object.keys(languages).forEach((lang) => {
|
||||
languageColors[lang] = languages[lang].color;
|
||||
});
|
||||
|
||||
//Filter only language colors from the whole file
|
||||
Object.keys(languages).forEach((lang) => {
|
||||
languageColors[lang] = languages[lang].color;
|
||||
//Debug Print
|
||||
//console.dir(languageColors);
|
||||
fs.writeFileSync(
|
||||
LANGS_FILEPATH,
|
||||
JSON.stringify(languageColors, null, " "),
|
||||
);
|
||||
});
|
||||
|
||||
//Debug Print
|
||||
//console.dir(languageColors);
|
||||
fs.writeFileSync(LANGS_FILEPATH, JSON.stringify(languageColors, null, ' '));
|
||||
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const theme = require("../themes/index");
|
||||
const fs = require("fs");
|
||||
import fs from "fs";
|
||||
import { themes } from "../themes/index.js";
|
||||
|
||||
const TARGET_FILE = "./themes/README.md";
|
||||
const REPO_CARD_LINKS_FLAG = "<!-- REPO_CARD_LINKS -->";
|
||||
@@ -54,7 +54,7 @@ const createStatMdLink = (theme) => {
|
||||
};
|
||||
|
||||
const generateLinks = (fn) => {
|
||||
return Object.keys(theme)
|
||||
return Object.keys(themes)
|
||||
.map((name) => fn(name))
|
||||
.join("");
|
||||
};
|
||||
@@ -65,14 +65,14 @@ const createTableItem = ({ link, label, isRepoCard }) => {
|
||||
};
|
||||
const generateTable = ({ isRepoCard }) => {
|
||||
const rows = [];
|
||||
const themes = Object.keys(theme).filter(
|
||||
const themesFiltered = Object.keys(themes).filter(
|
||||
(name) => name !== (!isRepoCard ? "default_repocard" : "default"),
|
||||
);
|
||||
|
||||
for (let i = 0; i < themes.length; i += 3) {
|
||||
const one = themes[i];
|
||||
const two = themes[i + 1];
|
||||
const three = themes[i + 2];
|
||||
for (let i = 0; i < themesFiltered.length; i += 3) {
|
||||
const one = themesFiltered[i];
|
||||
const two = themesFiltered[i + 1];
|
||||
const three = themesFiltered[i + 2];
|
||||
|
||||
let tableItem1 = createTableItem({ link: one, label: one, isRepoCard });
|
||||
let tableItem2 = createTableItem({ link: two, label: two, isRepoCard });
|
||||
@@ -84,7 +84,7 @@ const generateTable = ({ isRepoCard }) => {
|
||||
rows.push(`| ${tableItem1} | ${tableItem2} | ${tableItem3} |`);
|
||||
|
||||
// if it's the last row & the row has no empty space push a new row
|
||||
if (three && i + 3 === themes.length) {
|
||||
if (three && i + 3 === themesFiltered.length) {
|
||||
rows.push(`| [Add your theme][add-theme] | | |`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* @file Contains helper functions used in the scripts.
|
||||
*/
|
||||
|
||||
import { getInput } from "@actions/core";
|
||||
|
||||
// Script variables.
|
||||
const OWNER = "anuraghazra";
|
||||
const REPO = "github-readme-stats";
|
||||
|
||||
/**
|
||||
* Retrieve information about the repository that ran the action.
|
||||
*
|
||||
* @param {Object} context Action context.
|
||||
* @returns {Object} Repository information.
|
||||
*/
|
||||
export const getRepoInfo = (ctx) => {
|
||||
try {
|
||||
return {
|
||||
owner: ctx.repo.owner,
|
||||
repo: ctx.repo.repo,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
owner: OWNER,
|
||||
repo: REPO,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve github token and throw error if it is not found.
|
||||
*
|
||||
* @returns {string} Github token.
|
||||
*/
|
||||
export const getGithubToken = () => {
|
||||
const token = getInput("github_token") || process.env.GITHUB_TOKEN;
|
||||
if (!token) {
|
||||
throw Error("Could not find github token");
|
||||
}
|
||||
return token;
|
||||
};
|
||||
+494
-115
@@ -1,26 +1,81 @@
|
||||
const core = require("@actions/core");
|
||||
const github = require("@actions/github");
|
||||
const parse = require("parse-diff");
|
||||
const Hjson = require("hjson");
|
||||
const snakeCase = require("lodash.snakecase");
|
||||
const ColorContrastChecker = require("color-contrast-checker");
|
||||
/**
|
||||
* @file This script is used to preview the theme on theme PRs.
|
||||
*/
|
||||
import * as dotenv from "dotenv";
|
||||
dotenv.config();
|
||||
|
||||
require("dotenv").config();
|
||||
import { debug, setFailed } from "@actions/core";
|
||||
import github from "@actions/github";
|
||||
import ColorContrastChecker from "color-contrast-checker";
|
||||
import { info } from "console";
|
||||
import Hjson from "hjson";
|
||||
import snakeCase from "lodash.snakecase";
|
||||
import parse from "parse-diff";
|
||||
import { inspect } from "util";
|
||||
import { isValidHexColor } from "../src/common/utils.js";
|
||||
import { themes } from "../themes/index.js";
|
||||
import { getGithubToken, getRepoInfo } from "./helpers.js";
|
||||
|
||||
// Script variables.
|
||||
const COMMENTER = "github-actions[bot]";
|
||||
|
||||
const OWNER = "anuraghazra";
|
||||
const REPO = "github-readme-stats";
|
||||
const COMMENT_TITLE = "Automated Theme Preview";
|
||||
const THEME_PR_FAIL_TEXT = ":x: Theme PR does not adhere to our guidelines.";
|
||||
const THEME_PR_SUCCESS_TEXT =
|
||||
":heavy_check_mark: Theme PR does adhere to our guidelines.";
|
||||
const FAIL_TEXT = `
|
||||
\rUnfortunately, your theme PR does not adhere to our [theme guidelines](https://github.com/anuraghazra/github-readme-stats/blob/master/CONTRIBUTING.md#themes-contribution). Please fix the issues below, and we will review your\
|
||||
\r PR again. This pull request will **automatically close in 15 days** if no changes are made. After this time, you must re-open the PR for it to be reviewed.
|
||||
`;
|
||||
const THEME_CONTRIB_GUIDELINESS = `
|
||||
\rHi, thanks for the theme contribution. Please read our theme [contribution guidelines](https://github.com/anuraghazra/github-readme-stats/blob/master/CONTRIBUTING.md#themes-contribution).
|
||||
\rWe are currently only accepting color combinations from any VSCode theme or themes with good colour combinations to minimize bloating the themes collection.
|
||||
|
||||
\r> Also, note that if this theme is exclusively for your personal use, then instead of adding it to our theme collection, you can use card [customization options](https://github.com/anuraghazra/github-readme-stats#customization).
|
||||
`;
|
||||
const COLOR_PROPS = {
|
||||
title_color: 6,
|
||||
icon_color: 6,
|
||||
text_color: 6,
|
||||
bg_color: 8,
|
||||
border_color: 6,
|
||||
};
|
||||
const ACCEPTED_COLOR_PROPS = Object.keys(COLOR_PROPS);
|
||||
const REQUIRED_COLOR_PROPS = ACCEPTED_COLOR_PROPS.slice(0, 4);
|
||||
const INVALID_REVIEW_COMMENT = (commentUrl) =>
|
||||
`Some themes are invalid. See the [Automated Theme Preview](${commentUrl}) comment above for more information.`;
|
||||
|
||||
/**
|
||||
* Retrieve PR number from the event payload.
|
||||
*
|
||||
* @returns {number} PR number.
|
||||
*/
|
||||
const getPrNumber = () => {
|
||||
if (process.env.MOCK_PR_NUMBER) return process.env.MOCK_PR_NUMBER; // For testing purposes.
|
||||
|
||||
function getPrNumber() {
|
||||
const pullRequest = github.context.payload.pull_request;
|
||||
if (!pullRequest) {
|
||||
return undefined;
|
||||
throw Error("Could not get pull request number from context");
|
||||
}
|
||||
|
||||
return pullRequest.number;
|
||||
}
|
||||
};
|
||||
|
||||
function findCommentPredicate(inputs, comment) {
|
||||
/**
|
||||
* Retrieve the commenting user.
|
||||
* @returns {string} Commenting user.
|
||||
*/
|
||||
const getCommenter = () => {
|
||||
return process.env.COMMENTER ? process.env.COMMENTER : COMMENTER;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns whether the comment is a preview comment.
|
||||
*
|
||||
* @param {Object} inputs Action inputs.
|
||||
* @param {Object} comment Comment object.
|
||||
* @returns {boolean} Whether the comment is a preview comment.
|
||||
*/
|
||||
const isPreviewComment = (inputs, comment) => {
|
||||
return (
|
||||
(inputs.commentAuthor && comment.user
|
||||
? comment.user.login === inputs.commentAuthor
|
||||
@@ -29,160 +84,484 @@ function findCommentPredicate(inputs, comment) {
|
||||
? comment.body.includes(inputs.bodyIncludes)
|
||||
: true)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
async function findComment(octokit, issueNumber) {
|
||||
/**
|
||||
* Find the preview theme comment.
|
||||
*
|
||||
* @param {Object} octokit Octokit instance.
|
||||
* @param {number} issueNumber Issue number.
|
||||
* @param {string} repo Repository name.
|
||||
* @param {string} owner Owner of the repository.
|
||||
* @returns {Object} The Github comment object.
|
||||
*/
|
||||
const findComment = async (octokit, issueNumber, owner, repo, commenter) => {
|
||||
const parameters = {
|
||||
owner: OWNER,
|
||||
repo: REPO,
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
};
|
||||
const inputs = {
|
||||
commentAuthor: OWNER,
|
||||
commentAuthor: commenter,
|
||||
bodyIncludes: COMMENT_TITLE,
|
||||
};
|
||||
|
||||
// Search each page for the comment
|
||||
for await (const { data: comments } of octokit.paginate.iterator(
|
||||
octokit.rest.issues.listComments,
|
||||
parameters,
|
||||
)) {
|
||||
// Search each page for the comment
|
||||
const comment = comments.find((comment) =>
|
||||
findCommentPredicate(inputs, comment),
|
||||
isPreviewComment(inputs, comment),
|
||||
);
|
||||
if (comment) return comment;
|
||||
if (comment) {
|
||||
debug(`Found theme preview comment: ${inspect(comment)}`);
|
||||
return comment;
|
||||
} else {
|
||||
debug(`No theme preview comment found.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
async function upsertComment(octokit, props) {
|
||||
/**
|
||||
* Create or update the preview comment.
|
||||
*
|
||||
* @param {Object} octokit Octokit instance.
|
||||
* @param {Object} props Comment properties.
|
||||
* @return {string} The comment URL.
|
||||
*/
|
||||
const upsertComment = async (octokit, props) => {
|
||||
let resp;
|
||||
if (props.comment_id !== undefined) {
|
||||
await octokit.issues.updateComment(props);
|
||||
resp = await octokit.issues.updateComment(props);
|
||||
} else {
|
||||
await octokit.issues.createComment(props);
|
||||
resp = await octokit.issues.createComment(props);
|
||||
}
|
||||
}
|
||||
return resp.data.html_url;
|
||||
};
|
||||
|
||||
function getWebAimLink(color1, color2) {
|
||||
/**
|
||||
* Adds a review to the pull request.
|
||||
*
|
||||
* @param {Object} octokit Octokit instance.
|
||||
* @param {number} prNumber Pull request number.
|
||||
* @param {string} owner Owner of the repository.
|
||||
* @param {string} repo Repository name.
|
||||
* @param {string} reviewState The review state. Options are (APPROVE, REQUEST_CHANGES, COMMENT, PENDING).
|
||||
* @param {string} reason The reason for the review.
|
||||
*/
|
||||
const addReview = async (
|
||||
octokit,
|
||||
prNumber,
|
||||
owner,
|
||||
repo,
|
||||
reviewState,
|
||||
reason,
|
||||
) => {
|
||||
await octokit.pulls.createReview({
|
||||
owner,
|
||||
repo,
|
||||
pull_number: prNumber,
|
||||
event: reviewState,
|
||||
body: reason,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Add label to pull request.
|
||||
*
|
||||
* @param {Object} octokit Octokit instance.
|
||||
* @param {number} prNumber Pull request number.
|
||||
* @param {string} owner Repository owner.
|
||||
* @param {string} repo Repository name.
|
||||
* @param {string[]} labels Labels to add.
|
||||
*/
|
||||
const addLabel = async (octokit, prNumber, owner, repo, labels) => {
|
||||
await octokit.issues.addLabels({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: prNumber,
|
||||
labels,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove label from the pull request.
|
||||
*
|
||||
* @param {Object} octokit Octokit instance.
|
||||
* @param {number} prNumber Pull request number.
|
||||
* @param {string} owner Repository owner.
|
||||
* @param {string} repo Repository name.
|
||||
* @param {string} label Label to add or remove.
|
||||
*/
|
||||
const removeLabel = async (octokit, prNumber, owner, repo, label) => {
|
||||
await octokit.issues.removeLabel({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: prNumber,
|
||||
name: label,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds or removes a label from the pull request.
|
||||
*
|
||||
* @param {Object} octokit Octokit instance.
|
||||
* @param {number} prNumber Pull request number.
|
||||
* @param {string} owner Repository owner.
|
||||
* @param {string} repo Repository name.
|
||||
* @param {string} label Label to add or remove.
|
||||
* @param {boolean} add Whether to add or remove the label.
|
||||
*/
|
||||
const addRemoveLabel = async (octokit, prNumber, owner, repo, label, add) => {
|
||||
const res = await octokit.pulls.get({
|
||||
owner,
|
||||
repo,
|
||||
pull_number: prNumber,
|
||||
});
|
||||
if (add) {
|
||||
if (!res.data.labels.find((l) => l.name === label)) {
|
||||
await addLabel(octokit, prNumber, owner, repo, [label]);
|
||||
}
|
||||
} else {
|
||||
if (res.data.labels.find((l) => l.name === label)) {
|
||||
await removeLabel(octokit, prNumber, owner, repo, label);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve webAim contrast color check link.
|
||||
*
|
||||
* @param {string} color1 First color.
|
||||
* @param {string} color2 Second color.
|
||||
* @returns {string} WebAim contrast color check link.
|
||||
*/
|
||||
const getWebAimLink = (color1, color2) => {
|
||||
return `https://webaim.org/resources/contrastchecker/?fcolor=${color1}&bcolor=${color2}`;
|
||||
}
|
||||
};
|
||||
|
||||
function getGrsLink(colors) {
|
||||
/**
|
||||
* Retrieves the theme GRS url.
|
||||
*
|
||||
* @param {Object} colors The theme colors.
|
||||
* @returns {string} GRS theme url.
|
||||
*/
|
||||
const getGRSLink = (colors) => {
|
||||
const url = `https://github-readme-stats.vercel.app/api?username=anuraghazra`;
|
||||
const colorString = Object.keys(colors)
|
||||
.map((colorKey) => `${colorKey}=${colors[colorKey]}`)
|
||||
.join("&");
|
||||
|
||||
return `${url}&${colorString}&show_icons=true`;
|
||||
}
|
||||
};
|
||||
|
||||
const themeContribGuidelines = `
|
||||
\rHi, thanks for the theme contribution, please read our theme [contribution guidelines](https://github.com/anuraghazra/github-readme-stats/blob/master/CONTRIBUTING.md#themes-contribution).
|
||||
\rWe are currently only accepting color combinations from any VSCode theme or themes which have good color combination to minimize bloating the themes collection.
|
||||
|
||||
\r> Also note that if this theme is exclusively for your personal use, then instead of adding it to our theme collection you can use card [customization options](https://github.com/anuraghazra/github-readme-stats#customization)
|
||||
`;
|
||||
|
||||
async function run() {
|
||||
/**
|
||||
* Retrieve javascript object from json string.
|
||||
*
|
||||
* @description Wraps the Hjson parse function to fix several known json syntax errors.
|
||||
*
|
||||
* @param {string} json The json to parse.
|
||||
* @returns {Object} Object parsed from the json.
|
||||
*/
|
||||
const parseJSON = (json) => {
|
||||
try {
|
||||
const ccc = new ColorContrastChecker();
|
||||
const warnings = [];
|
||||
const token = core.getInput("token");
|
||||
const octokit = github.getOctokit(token || process.env.PERSONAL_TOKEN);
|
||||
const pullRequestId = getPrNumber();
|
||||
|
||||
if (!pullRequestId) {
|
||||
console.log("PR not found");
|
||||
return;
|
||||
const parsedJson = Hjson.parse(json);
|
||||
if (typeof parsedJson === "object") {
|
||||
return parsedJson;
|
||||
} else {
|
||||
throw new Error("PR diff is not a valid theme JSON object.");
|
||||
}
|
||||
} catch (error) {
|
||||
let parsedJson = json
|
||||
.split(/([\s\r\s]*}[\s\r\s]*,[\s\r\s]*)(?=[\w"-]+:)/)
|
||||
.filter((x) => typeof x !== "string" || !!x.trim());
|
||||
if (parsedJson[0].replace(/\s+/g, "") === "},") {
|
||||
parsedJson[0] = "},";
|
||||
if (!/\s*}\s*,?\s*$/.test(parsedJson[1])) {
|
||||
parsedJson.push(parsedJson.shift());
|
||||
} else {
|
||||
parsedJson.shift();
|
||||
}
|
||||
return Hjson.parse(parsedJson.join(""));
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check whether the theme name is still available.
|
||||
* @param {string} name Theme name.
|
||||
* @returns {boolean} Whether the theme name is available.
|
||||
*/
|
||||
const themeNameAlreadyExists = (name) => {
|
||||
return themes[name] !== undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* Main function.
|
||||
*/
|
||||
export const run = async (prNumber) => {
|
||||
try {
|
||||
const dryRun = process.env.DRY_RUN === "true" || false;
|
||||
debug("Retrieve action information from context...");
|
||||
debug(`Context: ${inspect(github.context)}`);
|
||||
let commentBody = `
|
||||
\r# ${COMMENT_TITLE}
|
||||
\r${THEME_CONTRIB_GUIDELINESS}
|
||||
`;
|
||||
const ccc = new ColorContrastChecker();
|
||||
const octokit = github.getOctokit(getGithubToken());
|
||||
const pullRequestId = prNumber ? prNumber : getPrNumber();
|
||||
const commenter = getCommenter();
|
||||
const { owner, repo } = getRepoInfo(github.context);
|
||||
debug(`Owner: ${owner}`);
|
||||
debug(`Repo: ${repo}`);
|
||||
debug(`Commenter: ${commenter}`);
|
||||
|
||||
// Retrieve the PR diff and preview-theme comment.
|
||||
debug("Retrieve PR diff...");
|
||||
const res = await octokit.pulls.get({
|
||||
owner: OWNER,
|
||||
repo: REPO,
|
||||
owner,
|
||||
repo,
|
||||
pull_number: pullRequestId,
|
||||
mediaType: {
|
||||
format: "diff",
|
||||
},
|
||||
});
|
||||
const comment = await findComment(octokit, pullRequestId);
|
||||
debug("Retrieve preview-theme comment...");
|
||||
const comment = await findComment(
|
||||
octokit,
|
||||
pullRequestId,
|
||||
owner,
|
||||
repo,
|
||||
commenter,
|
||||
);
|
||||
|
||||
// Retrieve theme changes from the PR diff.
|
||||
debug("Retrieve themes...");
|
||||
const diff = parse(res.data);
|
||||
const content = diff
|
||||
.find((file) => file.to === "themes/index.js")
|
||||
.chunks[0].changes.filter((c) => c.type === "add")
|
||||
.map((c) => c.content.replace("+", ""))
|
||||
.join("");
|
||||
|
||||
const themeObject = Hjson.parse(content);
|
||||
const themeName = Object.keys(themeObject)[0];
|
||||
const colors = themeObject[themeName];
|
||||
|
||||
if (themeName !== snakeCase(themeName)) {
|
||||
warnings.push("Theme name isn't in snake_case");
|
||||
const themeObject = parseJSON(content);
|
||||
if (
|
||||
Object.keys(themeObject).every(
|
||||
(key) => typeof themeObject[key] !== "object",
|
||||
)
|
||||
) {
|
||||
throw new Error("PR diff is not a valid theme JSON object.");
|
||||
}
|
||||
|
||||
if (!colors) {
|
||||
await upsertComment({
|
||||
comment_id: comment?.id,
|
||||
owner: OWNER,
|
||||
repo: REPO,
|
||||
issue_number: pullRequestId,
|
||||
body: `
|
||||
\r**${COMMENT_TITLE}**
|
||||
|
||||
\rCannot create theme preview
|
||||
// Loop through themes and create theme preview body.
|
||||
debug("Create theme preview body...");
|
||||
const themeValid = Object.fromEntries(
|
||||
Object.keys(themeObject).map((name) => [name, true]),
|
||||
);
|
||||
let previewBody = "";
|
||||
for (const theme in themeObject) {
|
||||
debug(`Create theme preview for ${theme}...`);
|
||||
const themeName = theme;
|
||||
const colors = themeObject[theme];
|
||||
const warnings = [];
|
||||
const errors = [];
|
||||
|
||||
${themeContribGuidelines}
|
||||
`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const titleColor = colors.title_color;
|
||||
const iconColor = colors.icon_color;
|
||||
const textColor = colors.text_color;
|
||||
const bgColor = colors.bg_color;
|
||||
const url = getGrsLink(colors);
|
||||
|
||||
const colorPairs = {
|
||||
title_color: [titleColor, bgColor],
|
||||
icon_color: [iconColor, bgColor],
|
||||
text_color: [textColor, bgColor],
|
||||
};
|
||||
|
||||
// check color contrast
|
||||
Object.keys(colorPairs).forEach((key) => {
|
||||
const color1 = colorPairs[key][0];
|
||||
const color2 = colorPairs[key][1];
|
||||
if (!ccc.isLevelAA(`#${color1}`, `#${color2}`)) {
|
||||
const permalink = getWebAimLink(color1, color2);
|
||||
warnings.push(
|
||||
`\`${key}\` does not pass [AA contrast ratio](${permalink})`,
|
||||
);
|
||||
// Check if the theme name is valid.
|
||||
debug("Theme preview body: Check if the theme name is valid...");
|
||||
if (themeNameAlreadyExists(themeName)) {
|
||||
warnings.push("Theme name already taken");
|
||||
themeValid[theme] = false;
|
||||
}
|
||||
if (themeName !== snakeCase(themeName)) {
|
||||
warnings.push("Theme name isn't in snake_case");
|
||||
themeValid[theme] = false;
|
||||
}
|
||||
});
|
||||
|
||||
await upsertComment(octokit, {
|
||||
comment_id: comment?.id,
|
||||
issue_number: pullRequestId,
|
||||
owner: OWNER,
|
||||
repo: REPO,
|
||||
body: `
|
||||
\r**${COMMENT_TITLE}**
|
||||
|
||||
\r${warnings.map((warning) => `- :warning: ${warning}\n`).join("")}
|
||||
// Check if the theme colors are valid.
|
||||
debug("Theme preview body: Check if the theme colors are valid...");
|
||||
let invalidColors = false;
|
||||
if (!colors) {
|
||||
warning.push("Theme colors are missing");
|
||||
invalidColors = true;
|
||||
} else {
|
||||
const missingKeys = REQUIRED_COLOR_PROPS.filter(
|
||||
(x) => !Object.keys(colors).includes(x),
|
||||
);
|
||||
const extraKeys = Object.keys(colors).filter(
|
||||
(x) => !ACCEPTED_COLOR_PROPS.includes(x),
|
||||
);
|
||||
if (missingKeys.length > 0 || extraKeys.length > 0) {
|
||||
for (const missingKey of missingKeys) {
|
||||
errors.push(`Theme color properties \`${missingKey}\` are missing`);
|
||||
}
|
||||
|
||||
\ntitle_color: <code>#${titleColor}</code> | icon_color: <code>#${iconColor}</code> | text_color: <code>#${textColor}</code> | bg_color: <code>#${bgColor}</code>
|
||||
|
||||
\r[Preview Link](${url})
|
||||
for (const extraKey of extraKeys) {
|
||||
warnings.push(
|
||||
`Theme color properties \`${extraKey}\` is not supported`,
|
||||
);
|
||||
}
|
||||
invalidColors = true;
|
||||
} else {
|
||||
for (const [colorKey, colorValue] of Object.entries(colors)) {
|
||||
if (colorValue[0] === "#") {
|
||||
errors.push(
|
||||
`Theme color property \`${colorKey}\` should not start with '#'`,
|
||||
);
|
||||
invalidColors = true;
|
||||
} else if (colorValue.length > COLOR_PROPS[colorKey]) {
|
||||
errors.push(
|
||||
`Theme color property \`${colorKey}\` can not be longer than \`${COLOR_PROPS[colorKey]}\` characters`,
|
||||
);
|
||||
invalidColors = true;
|
||||
} else if (!isValidHexColor(colorValue)) {
|
||||
errors.push(
|
||||
`Theme color property \`${colorKey}\` is not a valid hex color: <code>#${colorValue}</code>`,
|
||||
);
|
||||
invalidColors = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (invalidColors) {
|
||||
themeValid[theme] = false;
|
||||
previewBody += `
|
||||
\r### ${
|
||||
themeName.charAt(0).toUpperCase() + themeName.slice(1)
|
||||
} theme preview
|
||||
|
||||
\r${warnings.map((warning) => `- :warning: ${warning}.\n`).join("")}
|
||||
\r${errors.map((error) => `- :x: ${error}.\n`).join("")}
|
||||
|
||||
\r[](${url})
|
||||
\r>:x: Cannot create theme preview.
|
||||
`;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check color contrast.
|
||||
debug("Theme preview body: Check color contrast...");
|
||||
const titleColor = colors.title_color;
|
||||
const iconColor = colors.icon_color;
|
||||
const textColor = colors.text_color;
|
||||
const bgColor = colors.bg_color;
|
||||
const borderColor = colors.border_color;
|
||||
const url = getGRSLink(colors);
|
||||
const colorPairs = {
|
||||
title_color: [titleColor, bgColor],
|
||||
icon_color: [iconColor, bgColor],
|
||||
text_color: [textColor, bgColor],
|
||||
};
|
||||
Object.keys(colorPairs).forEach((item) => {
|
||||
let color1 = colorPairs[item][0];
|
||||
let color2 = colorPairs[item][1];
|
||||
color1 = color1.length === 4 ? color1.slice(0, 3) : color1.slice(0, 6);
|
||||
color2 = color2.length === 4 ? color2.slice(0, 3) : color2.slice(0, 6);
|
||||
if (!ccc.isLevelAA(`#${color1}`, `#${color2}`)) {
|
||||
const permalink = getWebAimLink(color1, color2);
|
||||
warnings.push(
|
||||
`\`${item}\` does not pass [AA contrast ratio](${permalink})`,
|
||||
);
|
||||
themeValid[theme] = false;
|
||||
}
|
||||
});
|
||||
|
||||
// Create theme preview body.
|
||||
debug("Theme preview body: Create theme preview body...");
|
||||
previewBody += `
|
||||
\r### ${
|
||||
themeName.charAt(0).toUpperCase() + themeName.slice(1)
|
||||
} theme preview
|
||||
|
||||
\r${warnings.map((warning) => `- :warning: ${warning}.\n`).join("")}
|
||||
|
||||
\ntitle_color: <code>#${titleColor}</code> | icon_color: <code>#${iconColor}</code> | text_color: <code>#${textColor}</code> | bg_color: <code>#${bgColor}</code>${
|
||||
borderColor ? ` | border_color: <code>#${borderColor}</code>` : ""
|
||||
}
|
||||
|
||||
\r[Preview Link](${url})
|
||||
|
||||
\r[](${url})
|
||||
`;
|
||||
}
|
||||
|
||||
// Create comment body.
|
||||
debug("Create comment body...");
|
||||
commentBody += `
|
||||
\r${
|
||||
Object.values(themeValid).every((value) => value)
|
||||
? THEME_PR_SUCCESS_TEXT
|
||||
: THEME_PR_FAIL_TEXT
|
||||
}
|
||||
\r## Test results
|
||||
\r${Object.entries(themeValid)
|
||||
.map(
|
||||
([key, value]) => `- ${value ? ":heavy_check_mark:" : ":x:"} ${key}`,
|
||||
)
|
||||
.join("\r")}
|
||||
|
||||
\r${
|
||||
Object.values(themeValid).every((value) => value)
|
||||
? "**Result:** :heavy_check_mark: All themes are valid."
|
||||
: "**Result:** :x: Some themes are invalid.\n\n" + FAIL_TEXT
|
||||
}
|
||||
|
||||
${themeContribGuidelines}
|
||||
`,
|
||||
});
|
||||
\r## Details
|
||||
\r${previewBody}
|
||||
`;
|
||||
|
||||
// Create or update theme-preview comment.
|
||||
debug("Create or update theme-preview comment...");
|
||||
let comment_url;
|
||||
if (!dryRun) {
|
||||
comment_url = await upsertComment(octokit, {
|
||||
comment_id: comment?.id,
|
||||
issue_number: pullRequestId,
|
||||
owner,
|
||||
repo,
|
||||
body: commentBody,
|
||||
});
|
||||
} else {
|
||||
info(`DRY_RUN: Comment body: ${commentBody}`);
|
||||
comment_url = "";
|
||||
}
|
||||
|
||||
// Change review state and add/remove `invalid` label based on theme PR validity.
|
||||
debug(
|
||||
"Change review state and add/remove `invalid` label based on whether all themes passed...",
|
||||
);
|
||||
const themesValid = Object.values(themeValid).every((value) => value);
|
||||
const reviewState = themesValid ? "APPROVE" : "REQUEST_CHANGES";
|
||||
const reviewReason = themesValid
|
||||
? undefined
|
||||
: INVALID_REVIEW_COMMENT(comment_url);
|
||||
if (!dryRun) {
|
||||
await addReview(
|
||||
octokit,
|
||||
pullRequestId,
|
||||
owner,
|
||||
repo,
|
||||
reviewState,
|
||||
reviewReason,
|
||||
);
|
||||
await addRemoveLabel(
|
||||
octokit,
|
||||
pullRequestId,
|
||||
owner,
|
||||
repo,
|
||||
"invalid",
|
||||
!themesValid,
|
||||
);
|
||||
} else {
|
||||
info(`DRY_RUN: Review state: ${reviewState}`);
|
||||
info(`DRY_RUN: Review reason: ${reviewReason}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
setFailed(error.message);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
run();
|
||||
if (typeof require !== "undefined" && require.main === module) {
|
||||
run();
|
||||
}
|
||||
|
||||
@@ -11,4 +11,4 @@ git checkout -b $BRANCH_NAME
|
||||
git add --all
|
||||
git commit --message "docs(theme): Auto update theme readme" || exit 0
|
||||
git remote add origin-$BRANCH_NAME https://${PERSONAL_TOKEN}@github.com/${GH_REPO}.git
|
||||
git push --force --quiet --set-upstream origin-$BRANCH_NAME $BRANCH_NAME
|
||||
git push --force --quiet --set-upstream origin-$BRANCH_NAME $BRANCH_NAME
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* @file Apply preview-theme script to all theme pull requests.
|
||||
*/
|
||||
|
||||
import { debug, setFailed } from "@actions/core";
|
||||
import { getGithubToken, getRepoInfo } from "./helpers.js";
|
||||
import { run as previewTheme } from "./preview-theme.js";
|
||||
import github from "@actions/github";
|
||||
import { fetchOpenPRs, pullsWithLabel } from "./close-stale-theme-prs.js";
|
||||
|
||||
/**
|
||||
* Get all pull request that do not contain a given label.
|
||||
* @param pull The pull requests to check.
|
||||
* @param label The label to check for.
|
||||
* @returns Whether the pull request contains the label.
|
||||
*/
|
||||
export const pullsWithoutLabel = (pulls, label) => {
|
||||
return pulls.filter((pr) => {
|
||||
return pr.labels.nodes.some((lab) => lab.name !== label);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Main function.
|
||||
*/
|
||||
const run = async () => {
|
||||
try {
|
||||
// Create octokit client.
|
||||
const dryRun = process.env.DRY_RUN === "true" || false;
|
||||
debug("Creating octokit client...");
|
||||
const octokit = github.getOctokit(getGithubToken());
|
||||
const { owner, repo } = getRepoInfo(github.context);
|
||||
|
||||
// Retrieve all theme pull requests.
|
||||
debug("Retrieving all theme pull requests...");
|
||||
const prs = await fetchOpenPRs(octokit, owner, repo);
|
||||
let themePRs = pullsWithLabel(prs, "themes");
|
||||
themePRs = pullsWithoutLabel(themePRs, "invalid");
|
||||
debug(`Found ${themePRs.length} theme PRs`);
|
||||
|
||||
// Loop through all theme PR and execute the theme-preview script.
|
||||
for (const pr of themePRs) {
|
||||
debug(`Applying theme-preview to #${pr.number}...`);
|
||||
console.log("Pull request number: ", pr.number);
|
||||
if (!dryRun) {
|
||||
await previewTheme(pr.number);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
setFailed(error.message);
|
||||
}
|
||||
};
|
||||
|
||||
run();
|
||||
@@ -57,21 +57,22 @@ function calculateRank({
|
||||
issues * ISSUES_OFFSET +
|
||||
stargazers * STARS_OFFSET +
|
||||
prs * PRS_OFFSET +
|
||||
followers * FOLLOWERS_OFFSET +
|
||||
totalRepos * REPO_OFFSET
|
||||
followers * FOLLOWERS_OFFSET +
|
||||
totalRepos * REPO_OFFSET
|
||||
) / 100;
|
||||
|
||||
const normalizedScore = normalcdf(score, TOTAL_VALUES, ALL_OFFSETS) * 100;
|
||||
|
||||
const level = (() => {
|
||||
if (normalizedScore < RANK_S_VALUE) return "S+";
|
||||
if (normalizedScore >= RANK_S_VALUE && normalizedScore < RANK_DOUBLE_A_VALUE) return "S";
|
||||
if (normalizedScore >= RANK_DOUBLE_A_VALUE && normalizedScore < RANK_A2_VALUE) return "A++";
|
||||
if (normalizedScore >= RANK_A2_VALUE && normalizedScore < RANK_A3_VALUE) return "A+"
|
||||
if (normalizedScore >= RANK_A3_VALUE && normalizedScore < RANK_B_VALUE) return "B+"
|
||||
})()
|
||||
if (normalizedScore < RANK_DOUBLE_A_VALUE) return "S";
|
||||
if (normalizedScore < RANK_A2_VALUE) return "A++";
|
||||
if (normalizedScore < RANK_A3_VALUE) return "A+";
|
||||
return "B+";
|
||||
})();
|
||||
|
||||
return { level, score: normalizedScore };
|
||||
}
|
||||
|
||||
module.exports = calculateRank;
|
||||
export { calculateRank };
|
||||
export default calculateRank;
|
||||
|
||||
+17
-16
@@ -1,17 +1,17 @@
|
||||
// @ts-check
|
||||
const {
|
||||
kFormatter,
|
||||
import { Card } from "../common/Card.js";
|
||||
import { I18n } from "../common/I18n.js";
|
||||
import { icons } from "../common/icons.js";
|
||||
import {
|
||||
encodeHTML,
|
||||
getCardColors,
|
||||
flexLayout,
|
||||
wrapTextMultiline,
|
||||
getCardColors,
|
||||
kFormatter,
|
||||
measureText,
|
||||
parseEmojis,
|
||||
} = require("../common/utils");
|
||||
const I18n = require("../common/I18n");
|
||||
const Card = require("../common/Card");
|
||||
const icons = require("../common/icons");
|
||||
const { repoCardLocales } = require("../translations");
|
||||
wrapTextMultiline,
|
||||
} from "../common/utils.js";
|
||||
import { repoCardLocales } from "../translations.js";
|
||||
|
||||
/**
|
||||
* @param {string} label
|
||||
@@ -67,8 +67,8 @@ const iconWithLabel = (icon, label, testid) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {import('../fetchers/types').RepositoryData} repo
|
||||
* @param {Partial<import("./types").RepoCardOptions>} options
|
||||
* @param {import('../fetchers/types').RepositoryData} repo
|
||||
* @param {Partial<import("./types").RepoCardOptions>} options
|
||||
* @returns {string}
|
||||
*/
|
||||
const renderRepoCard = (repo, options = {}) => {
|
||||
@@ -167,11 +167,11 @@ const renderRepoCard = (repo, options = {}) => {
|
||||
return card.render(`
|
||||
${
|
||||
isTemplate
|
||||
// @ts-ignore
|
||||
? getBadgeSVG(i18n.t("repocard.template"), colors.textColor)
|
||||
? // @ts-ignore
|
||||
getBadgeSVG(i18n.t("repocard.template"), colors.textColor)
|
||||
: isArchived
|
||||
// @ts-ignore
|
||||
? getBadgeSVG(i18n.t("repocard.archived"), colors.textColor)
|
||||
? // @ts-ignore
|
||||
getBadgeSVG(i18n.t("repocard.archived"), colors.textColor)
|
||||
: ""
|
||||
}
|
||||
|
||||
@@ -185,4 +185,5 @@ const renderRepoCard = (repo, options = {}) => {
|
||||
`);
|
||||
};
|
||||
|
||||
module.exports = renderRepoCard;
|
||||
export { renderRepoCard };
|
||||
export default renderRepoCard;
|
||||
|
||||
+77
-44
@@ -1,16 +1,16 @@
|
||||
// @ts-check
|
||||
const I18n = require("../common/I18n");
|
||||
const Card = require("../common/Card");
|
||||
const icons = require("../common/icons");
|
||||
const { getStyles } = require("../getStyles");
|
||||
const { statCardLocales } = require("../translations");
|
||||
const {
|
||||
kFormatter,
|
||||
flexLayout,
|
||||
import { Card } from "../common/Card.js";
|
||||
import { I18n } from "../common/I18n.js";
|
||||
import { icons } from "../common/icons.js";
|
||||
import {
|
||||
clampValue,
|
||||
measureText,
|
||||
flexLayout,
|
||||
getCardColors,
|
||||
} = require("../common/utils");
|
||||
kFormatter,
|
||||
measureText,
|
||||
} from "../common/utils.js";
|
||||
import { getStyles } from "../getStyles.js";
|
||||
import { statCardLocales } from "../translations.js";
|
||||
|
||||
const createTextNode = ({
|
||||
icon,
|
||||
@@ -20,6 +20,7 @@ const createTextNode = ({
|
||||
index,
|
||||
showIcons,
|
||||
shiftValuePos,
|
||||
bold,
|
||||
}) => {
|
||||
const kValue = kFormatter(value);
|
||||
const staggerDelay = (index + 3) * 150;
|
||||
@@ -35,11 +36,13 @@ const createTextNode = ({
|
||||
return `
|
||||
<g class="stagger" style="animation-delay: ${staggerDelay}ms" transform="translate(25, 0)">
|
||||
${iconSvg}
|
||||
<text class="stat bold" ${labelOffset} y="12.5">${label}:</text>
|
||||
<text
|
||||
class="stat"
|
||||
x="${(showIcons ? 140 : 120) + shiftValuePos}"
|
||||
y="12.5"
|
||||
<text class="stat ${
|
||||
bold ? " bold" : "not_bold"
|
||||
}" ${labelOffset} y="12.5">${label}:</text>
|
||||
<text
|
||||
class="stat ${bold ? " bold" : "not_bold"}"
|
||||
x="${(showIcons ? 140 : 120) + shiftValuePos}"
|
||||
y="12.5"
|
||||
data-testid="${id}"
|
||||
>${kValue}</text>
|
||||
</g>
|
||||
@@ -66,12 +69,14 @@ const renderStatsCard = (stats = {}, options = { hide: [] }) => {
|
||||
show_icons = false,
|
||||
hide_title = false,
|
||||
hide_border = false,
|
||||
card_width,
|
||||
hide_rank = false,
|
||||
include_all_commits = false,
|
||||
line_height = 25,
|
||||
title_color,
|
||||
icon_color,
|
||||
text_color,
|
||||
text_bold = true,
|
||||
bg_color,
|
||||
theme = "default",
|
||||
custom_title,
|
||||
@@ -164,6 +169,7 @@ const renderStatsCard = (stats = {}, options = { hide: [] }) => {
|
||||
showIcons: show_icons,
|
||||
shiftValuePos:
|
||||
(!include_all_commits ? 50 : 35) + (isLongLocale ? 50 : 0),
|
||||
bold: text_bold,
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -174,26 +180,6 @@ const renderStatsCard = (stats = {}, options = { hide: [] }) => {
|
||||
hide_rank ? 0 : 150,
|
||||
);
|
||||
|
||||
// Conditionally rendered elements
|
||||
const rankCircle = hide_rank
|
||||
? ""
|
||||
: `<g data-testid="rank-circle"
|
||||
transform="translate(400, ${height / 2 - 50})">
|
||||
<circle class="rank-circle-rim" cx="-10" cy="8" r="40" />
|
||||
<circle class="rank-circle" cx="-10" cy="8" r="40" />
|
||||
<g class="rank-text">
|
||||
<text
|
||||
x="-5"
|
||||
y="3"
|
||||
alignment-baseline="central"
|
||||
dominant-baseline="central"
|
||||
text-anchor="middle"
|
||||
>
|
||||
${rank.level}
|
||||
</text>
|
||||
</g>
|
||||
</g>`;
|
||||
|
||||
// the better user's score the the rank will be closer to zero so
|
||||
// subtracting 100 to get the progress in 100%
|
||||
const progress = 100 - rank.score;
|
||||
@@ -209,13 +195,20 @@ const renderStatsCard = (stats = {}, options = { hide: [] }) => {
|
||||
return measureText(custom_title ? custom_title : i18n.t("statcard.title"));
|
||||
};
|
||||
|
||||
const width = hide_rank
|
||||
? clampValue(
|
||||
50 /* padding */ + calculateTextWidth() * 2,
|
||||
270 /* min */,
|
||||
Infinity,
|
||||
)
|
||||
: 495;
|
||||
/*
|
||||
When hide_rank=true, the minimum card width is 270 px + the title length and padding.
|
||||
When hide_rank=false, the minimum card_width is 340 px + the icon width (if show_icons=true).
|
||||
Numbers are picked by looking at existing dimensions on production.
|
||||
*/
|
||||
const iconWidth = show_icons ? 16 : 0;
|
||||
const minCardWidth = hide_rank
|
||||
? clampValue(50 /* padding */ + calculateTextWidth() * 2, 270, Infinity)
|
||||
: 340 + iconWidth;
|
||||
const defaultCardWidth = hide_rank ? 270 : 495;
|
||||
let width = isNaN(card_width) ? defaultCardWidth : card_width;
|
||||
if (width < minCardWidth) {
|
||||
width = minCardWidth;
|
||||
}
|
||||
|
||||
const card = new Card({
|
||||
customTitle: custom_title,
|
||||
@@ -238,6 +231,45 @@ const renderStatsCard = (stats = {}, options = { hide: [] }) => {
|
||||
|
||||
if (disable_animations) card.disableAnimations();
|
||||
|
||||
/**
|
||||
* Calculates the right rank circle translation values such that the rank circle
|
||||
* keeps respecting the padding.
|
||||
*
|
||||
* width > 450: The default left padding of 50 px will be used.
|
||||
* width < 450: The left and right padding will shrink equally.
|
||||
*
|
||||
* @returns {number} - Rank circle translation value.
|
||||
*/
|
||||
const calculateRankXTranslation = () => {
|
||||
if (width < 450) {
|
||||
return width - 95 + (45 * (450 - 340)) / 110;
|
||||
} else {
|
||||
return width - 95;
|
||||
}
|
||||
};
|
||||
|
||||
// Conditionally rendered elements
|
||||
const rankCircle = hide_rank
|
||||
? ""
|
||||
: `<g data-testid="rank-circle"
|
||||
transform="translate(${calculateRankXTranslation()}, ${
|
||||
height / 2 - 50
|
||||
})">
|
||||
<circle class="rank-circle-rim" cx="-10" cy="8" r="40" />
|
||||
<circle class="rank-circle" cx="-10" cy="8" r="40" />
|
||||
<g class="rank-text">
|
||||
<text
|
||||
x="-5"
|
||||
y="3"
|
||||
alignment-baseline="central"
|
||||
dominant-baseline="central"
|
||||
text-anchor="middle"
|
||||
>
|
||||
${rank.level}
|
||||
</text>
|
||||
</g>
|
||||
</g>`;
|
||||
|
||||
// Accessibility Labels
|
||||
const labels = Object.keys(STATS)
|
||||
.filter((key) => !hide.includes(key))
|
||||
@@ -264,8 +296,9 @@ const renderStatsCard = (stats = {}, options = { hide: [] }) => {
|
||||
gap: lheight,
|
||||
direction: "column",
|
||||
}).join("")}
|
||||
</svg>
|
||||
</svg>
|
||||
`);
|
||||
};
|
||||
|
||||
module.exports = renderStatsCard;
|
||||
export { renderStatsCard };
|
||||
export default renderStatsCard;
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
// @ts-check
|
||||
const Card = require("../common/Card");
|
||||
const I18n = require("../common/I18n");
|
||||
const { langCardLocales } = require("../translations");
|
||||
const { createProgressNode } = require("../common/createProgressNode");
|
||||
const {
|
||||
import { Card } from "../common/Card.js";
|
||||
import { createProgressNode } from "../common/createProgressNode.js";
|
||||
import { I18n } from "../common/I18n.js";
|
||||
import {
|
||||
chunkArray,
|
||||
clampValue,
|
||||
getCardColors,
|
||||
flexLayout,
|
||||
getCardColors,
|
||||
lowercaseTrim,
|
||||
measureText,
|
||||
chunkArray,
|
||||
} = require("../common/utils");
|
||||
} from "../common/utils.js";
|
||||
import { langCardLocales } from "../translations.js";
|
||||
|
||||
const DEFAULT_CARD_WIDTH = 300;
|
||||
const MIN_CARD_WIDTH = 230;
|
||||
@@ -311,5 +311,4 @@ const renderTopLanguages = (topLangs, options = {}) => {
|
||||
`);
|
||||
};
|
||||
|
||||
module.exports = renderTopLanguages;
|
||||
module.exports.MIN_CARD_WIDTH = MIN_CARD_WIDTH;
|
||||
export { renderTopLanguages, MIN_CARD_WIDTH };
|
||||
|
||||
Vendored
+2
-1
@@ -1,4 +1,4 @@
|
||||
type ThemeNames = keyof typeof import("../../themes");
|
||||
type ThemeNames = keyof typeof import("../../themes/index.js");
|
||||
|
||||
export type CommonOptions = {
|
||||
title_color: string;
|
||||
@@ -16,6 +16,7 @@ export type StatCardOptions = CommonOptions & {
|
||||
show_icons: boolean;
|
||||
hide_title: boolean;
|
||||
hide_border: boolean;
|
||||
card_width: number;
|
||||
hide_rank: boolean;
|
||||
include_all_commits: boolean;
|
||||
line_height: number | string;
|
||||
|
||||
+21
-11
@@ -1,16 +1,26 @@
|
||||
// @ts-check
|
||||
const Card = require("../common/Card");
|
||||
const I18n = require("../common/I18n");
|
||||
const { getStyles } = require("../getStyles");
|
||||
const { wakatimeCardLocales } = require("../translations");
|
||||
const languageColors = require("../common/languageColors.json");
|
||||
const { createProgressNode } = require("../common/createProgressNode");
|
||||
const {
|
||||
import { Card } from "../common/Card.js";
|
||||
import { createProgressNode } from "../common/createProgressNode.js";
|
||||
import { I18n } from "../common/I18n.js";
|
||||
import {
|
||||
clampValue,
|
||||
getCardColors,
|
||||
flexLayout,
|
||||
getCardColors,
|
||||
lowercaseTrim,
|
||||
} = require("../common/utils");
|
||||
} from "../common/utils.js";
|
||||
import { getStyles } from "../getStyles.js";
|
||||
import { wakatimeCardLocales } from "../translations.js";
|
||||
|
||||
/** Import language colors.
|
||||
*
|
||||
* @description Here we use the workaround found in
|
||||
* https://stackoverflow.com/questions/66726365/how-should-i-import-json-in-node
|
||||
* since vercel is using v16.14.0 which does not yet support json imports without the
|
||||
* --experimental-json-modules flag.
|
||||
*/
|
||||
import { createRequire } from "module";
|
||||
const require = createRequire(import.meta.url);
|
||||
const languageColors = require("../common/languageColors.json"); // now works
|
||||
|
||||
/**
|
||||
* @param {{color: string, text: string}} param0
|
||||
@@ -314,5 +324,5 @@ const renderWakatimeCard = (stats = {}, options = { hide: [] }) => {
|
||||
`);
|
||||
};
|
||||
|
||||
module.exports = renderWakatimeCard;
|
||||
exports.createProgressNode = createProgressNode;
|
||||
export { renderWakatimeCard };
|
||||
export default renderWakatimeCard;
|
||||
|
||||
+5
-4
@@ -1,5 +1,5 @@
|
||||
const { getAnimations } = require("../getStyles");
|
||||
const { flexLayout, encodeHTML } = require("../common/utils");
|
||||
import { getAnimations } from "../getStyles.js";
|
||||
import { encodeHTML, flexLayout } from "./utils.js";
|
||||
|
||||
class Card {
|
||||
/**
|
||||
@@ -10,7 +10,7 @@ class Card {
|
||||
* @param {string?=} args.customTitle
|
||||
* @param {string?=} args.defaultTitle
|
||||
* @param {string?=} args.titlePrefixIcon
|
||||
* @param {ReturnType<import('../common/utils').getCardColors>?=} args.colors
|
||||
* @param {ReturnType<import('../common/utils.js').getCardColors>?=} args.colors
|
||||
*/
|
||||
constructor({
|
||||
width = 100,
|
||||
@@ -216,4 +216,5 @@ class Card {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Card;
|
||||
export { Card };
|
||||
export default Card;
|
||||
|
||||
+2
-1
@@ -18,4 +18,5 @@ class I18n {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = I18n;
|
||||
export { I18n };
|
||||
export default I18n;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
const blacklist = ["renovate-bot", "technote-space", "sw-yx"];
|
||||
|
||||
module.exports = blacklist;
|
||||
export { blacklist };
|
||||
export default blacklist;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const { clampValue } = require("../common/utils");
|
||||
import { clampValue } from "./utils.js";
|
||||
|
||||
const createProgressNode = ({
|
||||
x,
|
||||
@@ -16,7 +16,7 @@ const createProgressNode = ({
|
||||
<rect
|
||||
height="8"
|
||||
fill="${color}"
|
||||
rx="5" ry="5" x="0" y="0"
|
||||
rx="5" ry="5" x="0" y="0"
|
||||
data-testid="lang-progress"
|
||||
width="${progressPercentage}%"
|
||||
>
|
||||
@@ -25,4 +25,5 @@ const createProgressNode = ({
|
||||
`;
|
||||
};
|
||||
|
||||
exports.createProgressNode = createProgressNode;
|
||||
export { createProgressNode };
|
||||
export default createProgressNode;
|
||||
|
||||
+2
-1
@@ -8,4 +8,5 @@ const icons = {
|
||||
fork: `<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>`,
|
||||
};
|
||||
|
||||
module.exports = icons;
|
||||
export { icons };
|
||||
export default icons;
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
"ATS": "#1ac620",
|
||||
"ActionScript": "#882B0F",
|
||||
"Ada": "#02f88c",
|
||||
"Adblock Filter List": "#800000",
|
||||
"Adobe Font Metrics": "#fa0f00",
|
||||
"Agda": "#315665",
|
||||
"Alloy": "#64C800",
|
||||
@@ -22,6 +23,7 @@
|
||||
"Altium Designer": "#A89663",
|
||||
"AngelScript": "#C7D7DC",
|
||||
"Ant Build System": "#A9157E",
|
||||
"Antlers": "#ff269e",
|
||||
"ApacheConf": "#d12127",
|
||||
"Apex": "#1797c0",
|
||||
"Apollo Guidance Computer": "#0B3D91",
|
||||
@@ -41,8 +43,10 @@
|
||||
"Ballerina": "#FF5000",
|
||||
"Batchfile": "#C1F12E",
|
||||
"Beef": "#a52f4e",
|
||||
"Berry": "#15A13C",
|
||||
"BibTeX": "#778899",
|
||||
"Bicep": "#519aba",
|
||||
"Bikeshed": "#5562ac",
|
||||
"Bison": "#6A463F",
|
||||
"BitBake": "#00bce4",
|
||||
"Blade": "#f7523f",
|
||||
@@ -52,11 +56,13 @@
|
||||
"Boo": "#d4bec1",
|
||||
"Boogie": "#c80fa0",
|
||||
"Brainfuck": "#2F2530",
|
||||
"BrighterScript": "#66AABB",
|
||||
"Brightscript": "#662D91",
|
||||
"Browserslist": "#ffd539",
|
||||
"C": "#555555",
|
||||
"C#": "#178600",
|
||||
"C++": "#f34b7d",
|
||||
"CAP CDS": "#0092d1",
|
||||
"CLIPS": "#00A300",
|
||||
"CMake": "#DA3434",
|
||||
"COLLADA": "#F1A42B",
|
||||
@@ -66,7 +72,9 @@
|
||||
"CUE": "#5886E1",
|
||||
"CWeb": "#00007a",
|
||||
"Cabal Config": "#483465",
|
||||
"Cadence": "#00ef8b",
|
||||
"Cairo": "#ff4a48",
|
||||
"CameLIGO": "#3be133",
|
||||
"Cap'n Proto": "#c42727",
|
||||
"Ceylon": "#dfa535",
|
||||
"Chapel": "#8dc63f",
|
||||
@@ -120,6 +128,7 @@
|
||||
"Eiffel": "#4d6977",
|
||||
"Elixir": "#6e4a7e",
|
||||
"Elm": "#60B5CC",
|
||||
"Elvish": "#55BB55",
|
||||
"Emacs Lisp": "#c065db",
|
||||
"EmberScript": "#FFF4F3",
|
||||
"Erlang": "#B83998",
|
||||
@@ -153,6 +162,9 @@
|
||||
"GSC": "#FF6800",
|
||||
"Game Maker Language": "#71b417",
|
||||
"Gemfile.lock": "#701516",
|
||||
"Gemini": "#ff6900",
|
||||
"Genero": "#63408e",
|
||||
"Genero Forms": "#d8df39",
|
||||
"Genie": "#fb855d",
|
||||
"Genshi": "#951531",
|
||||
"Gentoo Ebuild": "#9400ff",
|
||||
@@ -161,6 +173,7 @@
|
||||
"Gherkin": "#5B2063",
|
||||
"Git Attributes": "#F44D27",
|
||||
"Git Config": "#F44D27",
|
||||
"Git Revision List": "#F44D27",
|
||||
"Gleam": "#ffaff3",
|
||||
"Glyph": "#c1ac7f",
|
||||
"Gnuplot": "#f0a9f0",
|
||||
@@ -178,6 +191,7 @@
|
||||
"Groovy Server Pages": "#4298b8",
|
||||
"HAProxy": "#106da9",
|
||||
"HLSL": "#aace60",
|
||||
"HOCON": "#9ff8ee",
|
||||
"HTML": "#e34c26",
|
||||
"HTML+ECR": "#2e1052",
|
||||
"HTML+EEX": "#6e4a7e",
|
||||
@@ -222,6 +236,7 @@
|
||||
"JavaScript": "#f1e05a",
|
||||
"JavaScript+ERB": "#f1e05a",
|
||||
"Jest Snapshot": "#15c213",
|
||||
"JetBrains MPS": "#21D789",
|
||||
"Jinja": "#a52a22",
|
||||
"Jison": "#56b3cb",
|
||||
"Jison Lex": "#56b3cb",
|
||||
@@ -246,6 +261,7 @@
|
||||
"Latte": "#f2a542",
|
||||
"Less": "#1d365d",
|
||||
"Lex": "#DBCA00",
|
||||
"LigoLANG": "#0e74ff",
|
||||
"LilyPond": "#9ccc7c",
|
||||
"Liquid": "#67b8de",
|
||||
"Literate Agda": "#315665",
|
||||
@@ -278,13 +294,16 @@
|
||||
"Modelica": "#de1d31",
|
||||
"Modula-2": "#10253f",
|
||||
"Modula-3": "#223388",
|
||||
"Monkey C": "#8D6747",
|
||||
"MoonScript": "#ff4585",
|
||||
"Motoko": "#fbb03b",
|
||||
"Motorola 68K Assembly": "#005daa",
|
||||
"Move": "#4a137a",
|
||||
"Mustache": "#724b3b",
|
||||
"NCL": "#28431f",
|
||||
"NPM Config": "#cb3837",
|
||||
"NWScript": "#111522",
|
||||
"Nasal": "#1d2c4e",
|
||||
"Nearley": "#990000",
|
||||
"Nemerle": "#3d3c6e",
|
||||
"NetLinx": "#0aa0ff",
|
||||
@@ -332,6 +351,7 @@
|
||||
"PigLatin": "#fcd7de",
|
||||
"Pike": "#005390",
|
||||
"PogoScript": "#d80074",
|
||||
"Portugol": "#f8bd00",
|
||||
"PostCSS": "#dc3a0c",
|
||||
"PostScript": "#da291c",
|
||||
"PowerBuilder": "#8f0f8d",
|
||||
@@ -366,6 +386,7 @@
|
||||
"Rascal": "#fffaa0",
|
||||
"ReScript": "#ed5051",
|
||||
"Reason": "#ff5847",
|
||||
"ReasonLIGO": "#ff5847",
|
||||
"Rebol": "#358a5b",
|
||||
"Record Jar": "#0673ba",
|
||||
"Red": "#f50000",
|
||||
@@ -377,6 +398,7 @@
|
||||
"Roff": "#ecdebe",
|
||||
"Roff Manpage": "#ecdebe",
|
||||
"Rouge": "#cc0088",
|
||||
"RouterOS Script": "#DE3941",
|
||||
"Ruby": "#701516",
|
||||
"Rust": "#dea584",
|
||||
"SAS": "#B34936",
|
||||
@@ -386,6 +408,7 @@
|
||||
"SQL": "#e38c00",
|
||||
"SQLPL": "#e38c00",
|
||||
"SRecode Template": "#348a34",
|
||||
"STL": "#373b5e",
|
||||
"SVG": "#ff9900",
|
||||
"SaltStack": "#646464",
|
||||
"Sass": "#a53b70",
|
||||
@@ -425,8 +448,9 @@
|
||||
"TOML": "#9c4221",
|
||||
"TSQL": "#e38c00",
|
||||
"TSV": "#237346",
|
||||
"TSX": "#2b7489",
|
||||
"TSX": "#3178c6",
|
||||
"TXL": "#0178b8",
|
||||
"Talon": "#333333",
|
||||
"Tcl": "#e4cc98",
|
||||
"TeX": "#3D6117",
|
||||
"Terra": "#00004c",
|
||||
@@ -435,7 +459,7 @@
|
||||
"Thrift": "#D12127",
|
||||
"Turing": "#cf142b",
|
||||
"Twig": "#c1d026",
|
||||
"TypeScript": "#2b7489",
|
||||
"TypeScript": "#3178c6",
|
||||
"Unified Parallel C": "#4e3617",
|
||||
"Unity3D Asset": "#222c37",
|
||||
"Uno": "#9933cc",
|
||||
@@ -446,8 +470,9 @@
|
||||
"VBScript": "#15dcdc",
|
||||
"VCL": "#148AA8",
|
||||
"VHDL": "#adb2cb",
|
||||
"Vala": "#fbe5cd",
|
||||
"Vala": "#a56de2",
|
||||
"Valve Data Format": "#f26025",
|
||||
"Velocity Template Language": "#507cff",
|
||||
"Verilog": "#b2b7f8",
|
||||
"Vim Help File": "#199f4b",
|
||||
"Vim Script": "#199f4b",
|
||||
@@ -458,11 +483,13 @@
|
||||
"Vyper": "#2980b9",
|
||||
"Web Ontology Language": "#5b70bd",
|
||||
"WebAssembly": "#04133b",
|
||||
"Whiley": "#d5c397",
|
||||
"Wikitext": "#fc5757",
|
||||
"Windows Registry Entries": "#52d5ff",
|
||||
"Witcher Script": "#ff0000",
|
||||
"Wollok": "#a23738",
|
||||
"World of Warcraft Addon Data": "#f7e43f",
|
||||
"Wren": "#383838",
|
||||
"X10": "#4B6BEF",
|
||||
"XC": "#99DA07",
|
||||
"XML": "#0060ac",
|
||||
@@ -476,6 +503,7 @@
|
||||
"YARA": "#220000",
|
||||
"YASnippet": "#32AB90",
|
||||
"Yacc": "#4B6C4B",
|
||||
"Yul": "#794932",
|
||||
"ZAP": "#0d665e",
|
||||
"ZIL": "#dc75e5",
|
||||
"ZenScript": "#00BCD1",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const { logger, CustomError } = require("../common/utils");
|
||||
import { CustomError, logger } from "./utils.js";
|
||||
|
||||
const retryer = async (fetcher, variables, retries = 0) => {
|
||||
if (retries > 7) {
|
||||
@@ -40,4 +40,5 @@ const retryer = async (fetcher, variables, retries = 0) => {
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = retryer;
|
||||
export { retryer };
|
||||
export default retryer;
|
||||
|
||||
+5
-5
@@ -1,8 +1,8 @@
|
||||
// @ts-check
|
||||
const axios = require("axios");
|
||||
const wrap = require("word-wrap");
|
||||
const themes = require("../../themes");
|
||||
const toEmoji = require("emoji-name-map");
|
||||
import axios from "axios";
|
||||
import toEmoji from "emoji-name-map";
|
||||
import wrap from "word-wrap";
|
||||
import { themes } from "../../themes/index.js";
|
||||
|
||||
/**
|
||||
* @param {string} message
|
||||
@@ -370,7 +370,7 @@ function parseEmojis(str) {
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export {
|
||||
renderError,
|
||||
kFormatter,
|
||||
encodeHTML,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @ts-check
|
||||
const retryer = require("../common/retryer");
|
||||
const { request, MissingParamError } = require("../common/utils");
|
||||
import { retryer } from "../common/retryer.js";
|
||||
import { MissingParamError, request } from "../common/utils.js";
|
||||
|
||||
/**
|
||||
* @param {import('Axios').AxiosRequestHeaders} variables
|
||||
@@ -97,4 +97,5 @@ async function fetchRepo(username, reponame) {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = fetchRepo;
|
||||
export { fetchRepo };
|
||||
export default fetchRepo;
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
// @ts-check
|
||||
const axios = require("axios").default;
|
||||
const githubUsernameRegex = require("github-username-regex");
|
||||
|
||||
const retryer = require("../common/retryer");
|
||||
const calculateRank = require("../calculateRank");
|
||||
const {
|
||||
request,
|
||||
logger,
|
||||
import axios from "axios";
|
||||
import * as dotenv from "dotenv";
|
||||
import githubUsernameRegex from "github-username-regex";
|
||||
import { calculateRank } from "../calculateRank.js";
|
||||
import { retryer } from "../common/retryer.js";
|
||||
import {
|
||||
CustomError,
|
||||
logger,
|
||||
MissingParamError,
|
||||
} = require("../common/utils");
|
||||
request,
|
||||
} from "../common/utils.js";
|
||||
|
||||
require("dotenv").config();
|
||||
dotenv.config();
|
||||
|
||||
/**
|
||||
* @param {import('axios').AxiosRequestHeaders} variables
|
||||
@@ -47,6 +47,7 @@ const fetcher = (variables, token) => {
|
||||
repositories(first: 100, ownerAffiliations: OWNER, orderBy: {direction: DESC, field: STARGAZERS}) {
|
||||
totalCount
|
||||
nodes {
|
||||
name
|
||||
stargazers {
|
||||
totalCount
|
||||
}
|
||||
@@ -86,15 +87,16 @@ const totalCommitsFetcher = async (username) => {
|
||||
|
||||
try {
|
||||
let res = await retryer(fetchTotalCommits, { login: username });
|
||||
if (res.data.total_count) {
|
||||
let total_count = res.data.total_count;
|
||||
if (!!total_count && !isNaN(total_count)) {
|
||||
return res.data.total_count;
|
||||
}
|
||||
} catch (err) {
|
||||
logger.log(err);
|
||||
// just return 0 if there is something wrong so that
|
||||
// we don't break the whole app
|
||||
return 0;
|
||||
}
|
||||
// just return 0 if there is something wrong so that
|
||||
// we don't break the whole app
|
||||
return 0;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -107,6 +109,7 @@ async function fetchStats(
|
||||
username,
|
||||
count_private = false,
|
||||
include_all_commits = false,
|
||||
exclude_repo = [],
|
||||
) {
|
||||
if (!username) throw new MissingParamError(["username"]);
|
||||
|
||||
@@ -132,6 +135,15 @@ async function fetchStats(
|
||||
|
||||
const user = res.data.data.user;
|
||||
|
||||
// populate repoToHide map for quick lookup
|
||||
// while filtering out
|
||||
let repoToHide = {};
|
||||
if (exclude_repo) {
|
||||
exclude_repo.forEach((repoName) => {
|
||||
repoToHide[repoName] = true;
|
||||
});
|
||||
}
|
||||
|
||||
stats.name = user.name || user.login;
|
||||
stats.totalIssues = user.openIssues.totalCount + user.closedIssues.totalCount;
|
||||
|
||||
@@ -153,9 +165,14 @@ async function fetchStats(
|
||||
stats.totalPRs = user.pullRequests.totalCount;
|
||||
stats.contributedTo = user.repositoriesContributedTo.totalCount;
|
||||
|
||||
stats.totalStars = user.repositories.nodes.reduce((prev, curr) => {
|
||||
return prev + curr.stargazers.totalCount;
|
||||
}, 0);
|
||||
// Retrieve stars while filtering out repositories to be hidden
|
||||
stats.totalStars = user.repositories.nodes
|
||||
.filter((data) => {
|
||||
return !repoToHide[data.name];
|
||||
})
|
||||
.reduce((prev, curr) => {
|
||||
return prev + curr.stargazers.totalCount;
|
||||
}, 0);
|
||||
|
||||
stats.rank = calculateRank({
|
||||
totalCommits: stats.totalCommits,
|
||||
@@ -170,4 +187,5 @@ async function fetchStats(
|
||||
return stats;
|
||||
}
|
||||
|
||||
module.exports = fetchStats;
|
||||
export { fetchStats };
|
||||
export default fetchStats;
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// @ts-check
|
||||
const { request, logger, MissingParamError } = require("../common/utils");
|
||||
const retryer = require("../common/retryer");
|
||||
require("dotenv").config();
|
||||
import * as dotenv from "dotenv";
|
||||
import { retryer } from "../common/retryer.js";
|
||||
import { logger, MissingParamError, request } from "../common/utils.js";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
/**
|
||||
* @param {import('Axios').AxiosRequestHeaders} variables
|
||||
@@ -104,4 +106,5 @@ async function fetchTopLanguages(username, exclude_repo = []) {
|
||||
return topLangs;
|
||||
}
|
||||
|
||||
module.exports = fetchTopLanguages;
|
||||
export { fetchTopLanguages };
|
||||
export default fetchTopLanguages;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const axios = require("axios");
|
||||
const { MissingParamError } = require("../common/utils");
|
||||
import axios from "axios";
|
||||
import { MissingParamError } from "../common/utils.js";
|
||||
|
||||
/**
|
||||
* @param {{username: string, api_domain: string, range: string}} props
|
||||
@@ -7,7 +7,7 @@ const { MissingParamError } = require("../common/utils");
|
||||
*/
|
||||
const fetchWakatimeStats = async ({ username, api_domain, range }) => {
|
||||
if (!username) throw new MissingParamError(["username"]);
|
||||
|
||||
|
||||
try {
|
||||
const { data } = await axios.get(
|
||||
`https://${
|
||||
@@ -26,6 +26,5 @@ const fetchWakatimeStats = async ({ username, api_domain, range }) => {
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
fetchWakatimeStats,
|
||||
};
|
||||
export { fetchWakatimeStats };
|
||||
export default fetchWakatimeStats;
|
||||
|
||||
+4
-3
@@ -81,16 +81,17 @@ const getStyles = ({
|
||||
animation: fadeInAnimation 0.3s ease-in-out forwards;
|
||||
}
|
||||
.rank-text {
|
||||
font: 800 24px 'Segoe UI', Ubuntu, Sans-Serif; fill: ${textColor};
|
||||
font: 800 24px 'Segoe UI', Ubuntu, Sans-Serif; fill: ${textColor};
|
||||
animation: scaleInAnimation 0.3s ease-in-out forwards;
|
||||
}
|
||||
|
||||
.not_bold { font-weight: 400 }
|
||||
.bold { font-weight: 700 }
|
||||
.icon {
|
||||
fill: ${iconColor};
|
||||
display: ${!!show_icons ? "block" : "none"};
|
||||
}
|
||||
|
||||
|
||||
.rank-circle-rim {
|
||||
stroke: ${titleColor};
|
||||
fill: none;
|
||||
@@ -112,4 +113,4 @@ const getStyles = ({
|
||||
`;
|
||||
};
|
||||
|
||||
module.exports = { getStyles, getAnimations };
|
||||
export { getStyles, getAnimations };
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
const { encodeHTML } = require("./common/utils");
|
||||
import { encodeHTML } from "./common/utils.js";
|
||||
|
||||
const statCardLocales = ({ name, apostrophe }) => {
|
||||
const encodedName = encodeHTML(name);
|
||||
@@ -358,7 +358,7 @@ function isLocaleAvailable(locale) {
|
||||
return availableLocales.includes(locale.toLowerCase());
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export {
|
||||
isLocaleAvailable,
|
||||
availableLocales,
|
||||
statCardLocales,
|
||||
|
||||
@@ -1,179 +1,20 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Test Render Wakatime Card should render correctly 1`] = `
|
||||
"
|
||||
<svg
|
||||
width=\\"495\\"
|
||||
height=\\"150\\"
|
||||
viewBox=\\"0 0 495 150\\"
|
||||
fill=\\"none\\"
|
||||
xmlns=\\"http://www.w3.org/2000/svg\\"
|
||||
role=\\"img\\"
|
||||
aria-labelledby=\\"descId\\"
|
||||
>
|
||||
<title id=\\"titleId\\"></title>
|
||||
<desc id=\\"descId\\"></desc>
|
||||
<style>
|
||||
.header {
|
||||
font: 600 18px 'Segoe UI', Ubuntu, Sans-Serif;
|
||||
fill: #2f80ed;
|
||||
animation: fadeInAnimation 0.8s ease-in-out forwards;
|
||||
}
|
||||
@supports(-moz-appearance: auto) {
|
||||
/* Selector detects Firefox */
|
||||
.header { font-size: 15.5px; }
|
||||
}
|
||||
|
||||
|
||||
.stat {
|
||||
font: 600 14px 'Segoe UI', Ubuntu, \\"Helvetica Neue\\", Sans-Serif; fill: #434d58;
|
||||
}
|
||||
@supports(-moz-appearance: auto) {
|
||||
/* Selector detects Firefox */
|
||||
.stat { font-size:12px; }
|
||||
}
|
||||
.stagger {
|
||||
opacity: 0;
|
||||
animation: fadeInAnimation 0.3s ease-in-out forwards;
|
||||
}
|
||||
.rank-text {
|
||||
font: 800 24px 'Segoe UI', Ubuntu, Sans-Serif; fill: #434d58;
|
||||
animation: scaleInAnimation 0.3s ease-in-out forwards;
|
||||
}
|
||||
|
||||
.bold { font-weight: 700 }
|
||||
.icon {
|
||||
fill: #4c71f2;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.rank-circle-rim {
|
||||
stroke: #2f80ed;
|
||||
fill: none;
|
||||
stroke-width: 6;
|
||||
opacity: 0.2;
|
||||
}
|
||||
.rank-circle {
|
||||
stroke: #2f80ed;
|
||||
stroke-dasharray: 250;
|
||||
fill: none;
|
||||
stroke-width: 6;
|
||||
stroke-linecap: round;
|
||||
opacity: 0.8;
|
||||
transform-origin: -10px 8px;
|
||||
transform: rotate(-90deg);
|
||||
animation: rankAnimation 1s forwards ease-in-out;
|
||||
}
|
||||
|
||||
|
||||
.lang-name { font: 400 11px 'Segoe UI', Ubuntu, Sans-Serif; fill: #434d58 }
|
||||
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
<rect
|
||||
data-testid=\\"card-bg\\"
|
||||
x=\\"0.5\\"
|
||||
y=\\"0.5\\"
|
||||
rx=\\"4.5\\"
|
||||
height=\\"99%\\"
|
||||
stroke=\\"#e4e2e2\\"
|
||||
width=\\"494\\"
|
||||
fill=\\"#fffefe\\"
|
||||
stroke-opacity=\\"1\\"
|
||||
/>
|
||||
|
||||
|
||||
<g
|
||||
data-testid=\\"card-title\\"
|
||||
transform=\\"translate(25, 35)\\"
|
||||
>
|
||||
<g transform=\\"translate(0, 0)\\">
|
||||
<text
|
||||
x=\\"0\\"
|
||||
y=\\"0\\"
|
||||
class=\\"header\\"
|
||||
data-testid=\\"header\\"
|
||||
>Wakatime Stats</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
|
||||
<g
|
||||
data-testid=\\"main-card-body\\"
|
||||
transform=\\"translate(0, 55)\\"
|
||||
>
|
||||
|
||||
<svg x=\\"0\\" y=\\"0\\" width=\\"100%\\">
|
||||
<g transform=\\"translate(0, 0)\\">
|
||||
<g class=\\"stagger\\" style=\\"animation-delay: NaNms\\" transform=\\"translate(25, 0)\\">
|
||||
<text class=\\"stat bold\\" y=\\"12.5\\" data-testid=\\"Other\\">Other:</text>
|
||||
<text
|
||||
class=\\"stat\\"
|
||||
x=\\"350\\"
|
||||
y=\\"12.5\\"
|
||||
>19 mins</text>
|
||||
|
||||
<svg width=\\"220\\" x=\\"110\\" y=\\"4\\">
|
||||
<rect rx=\\"5\\" ry=\\"5\\" x=\\"0\\" y=\\"0\\" width=\\"220\\" height=\\"8\\" fill=\\"#434d58\\"></rect>
|
||||
<rect
|
||||
height=\\"8\\"
|
||||
fill=\\"#2f80ed\\"
|
||||
rx=\\"5\\" ry=\\"5\\" x=\\"0\\" y=\\"0\\"
|
||||
data-testid=\\"lang-progress\\"
|
||||
width=\\"2%\\"
|
||||
>
|
||||
</rect>
|
||||
</svg>
|
||||
|
||||
</g>
|
||||
</g><g transform=\\"translate(0, 25)\\">
|
||||
<g class=\\"stagger\\" style=\\"animation-delay: NaNms\\" transform=\\"translate(25, 0)\\">
|
||||
<text class=\\"stat bold\\" y=\\"12.5\\" data-testid=\\"TypeScript\\">TypeScript:</text>
|
||||
<text
|
||||
class=\\"stat\\"
|
||||
x=\\"350\\"
|
||||
y=\\"12.5\\"
|
||||
>1 min</text>
|
||||
|
||||
<svg width=\\"220\\" x=\\"110\\" y=\\"4\\">
|
||||
<rect rx=\\"5\\" ry=\\"5\\" x=\\"0\\" y=\\"0\\" width=\\"220\\" height=\\"8\\" fill=\\"#434d58\\"></rect>
|
||||
<rect
|
||||
height=\\"8\\"
|
||||
fill=\\"#2f80ed\\"
|
||||
rx=\\"5\\" ry=\\"5\\" x=\\"0\\" y=\\"0\\"
|
||||
data-testid=\\"lang-progress\\"
|
||||
width=\\"2%\\"
|
||||
>
|
||||
</rect>
|
||||
</svg>
|
||||
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
</g>
|
||||
</svg>
|
||||
"
|
||||
`;
|
||||
exports[`Test Render Wakatime Card should render correctly 1`] = `[Function]`;
|
||||
|
||||
exports[`Test Render Wakatime Card should render correctly with compact layout 1`] = `
|
||||
"
|
||||
<svg
|
||||
width=\\"495\\"
|
||||
height=\\"115\\"
|
||||
viewBox=\\"0 0 495 115\\"
|
||||
fill=\\"none\\"
|
||||
xmlns=\\"http://www.w3.org/2000/svg\\"
|
||||
role=\\"img\\"
|
||||
aria-labelledby=\\"descId\\"
|
||||
width="495"
|
||||
height="115"
|
||||
viewBox="0 0 495 115"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
role="img"
|
||||
aria-labelledby="descId"
|
||||
>
|
||||
<title id=\\"titleId\\"></title>
|
||||
<desc id=\\"descId\\"></desc>
|
||||
<title id="titleId"></title>
|
||||
<desc id="descId"></desc>
|
||||
<style>
|
||||
.header {
|
||||
font: 600 18px 'Segoe UI', Ubuntu, Sans-Serif;
|
||||
@@ -187,7 +28,7 @@ exports[`Test Render Wakatime Card should render correctly with compact layout 1
|
||||
|
||||
|
||||
.stat {
|
||||
font: 600 14px 'Segoe UI', Ubuntu, \\"Helvetica Neue\\", Sans-Serif; fill: #434d58;
|
||||
font: 600 14px 'Segoe UI', Ubuntu, "Helvetica Neue", Sans-Serif; fill: #434d58;
|
||||
}
|
||||
@supports(-moz-appearance: auto) {
|
||||
/* Selector detects Firefox */
|
||||
@@ -198,16 +39,17 @@ exports[`Test Render Wakatime Card should render correctly with compact layout 1
|
||||
animation: fadeInAnimation 0.3s ease-in-out forwards;
|
||||
}
|
||||
.rank-text {
|
||||
font: 800 24px 'Segoe UI', Ubuntu, Sans-Serif; fill: #434d58;
|
||||
font: 800 24px 'Segoe UI', Ubuntu, Sans-Serif; fill: #434d58;
|
||||
animation: scaleInAnimation 0.3s ease-in-out forwards;
|
||||
}
|
||||
|
||||
.not_bold { font-weight: 400 }
|
||||
.bold { font-weight: 700 }
|
||||
.icon {
|
||||
fill: #4c71f2;
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
.rank-circle-rim {
|
||||
stroke: #2f80ed;
|
||||
fill: none;
|
||||
@@ -237,75 +79,75 @@ exports[`Test Render Wakatime Card should render correctly with compact layout 1
|
||||
|
||||
|
||||
<rect
|
||||
data-testid=\\"card-bg\\"
|
||||
x=\\"0.5\\"
|
||||
y=\\"0.5\\"
|
||||
rx=\\"4.5\\"
|
||||
height=\\"99%\\"
|
||||
stroke=\\"#e4e2e2\\"
|
||||
width=\\"494\\"
|
||||
fill=\\"#fffefe\\"
|
||||
stroke-opacity=\\"1\\"
|
||||
data-testid="card-bg"
|
||||
x="0.5"
|
||||
y="0.5"
|
||||
rx="4.5"
|
||||
height="99%"
|
||||
stroke="#e4e2e2"
|
||||
width="494"
|
||||
fill="#fffefe"
|
||||
stroke-opacity="1"
|
||||
/>
|
||||
|
||||
|
||||
<g
|
||||
data-testid=\\"card-title\\"
|
||||
transform=\\"translate(25, 35)\\"
|
||||
data-testid="card-title"
|
||||
transform="translate(25, 35)"
|
||||
>
|
||||
<g transform=\\"translate(0, 0)\\">
|
||||
<g transform="translate(0, 0)">
|
||||
<text
|
||||
x=\\"0\\"
|
||||
y=\\"0\\"
|
||||
class=\\"header\\"
|
||||
data-testid=\\"header\\"
|
||||
x="0"
|
||||
y="0"
|
||||
class="header"
|
||||
data-testid="header"
|
||||
>Wakatime Stats</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
|
||||
<g
|
||||
data-testid=\\"main-card-body\\"
|
||||
transform=\\"translate(0, 55)\\"
|
||||
data-testid="main-card-body"
|
||||
transform="translate(0, 55)"
|
||||
>
|
||||
|
||||
<svg x=\\"0\\" y=\\"0\\" width=\\"100%\\">
|
||||
<svg x="0" y="0" width="100%">
|
||||
|
||||
<mask id=\\"rect-mask\\">
|
||||
<rect x=\\"25\\" y=\\"0\\" width=\\"440\\" height=\\"8\\" fill=\\"white\\" rx=\\"5\\" />
|
||||
<mask id="rect-mask">
|
||||
<rect x="25" y="0" width="440" height="8" fill="white" rx="5" />
|
||||
</mask>
|
||||
|
||||
<rect
|
||||
mask=\\"url(#rect-mask)\\"
|
||||
data-testid=\\"lang-progress\\"
|
||||
x=\\"0\\"
|
||||
y=\\"0\\"
|
||||
width=\\"6.6495\\"
|
||||
height=\\"8\\"
|
||||
fill=\\"#858585\\"
|
||||
mask="url(#rect-mask)"
|
||||
data-testid="lang-progress"
|
||||
x="0"
|
||||
y="0"
|
||||
width="6.6495"
|
||||
height="8"
|
||||
fill="#858585"
|
||||
/>
|
||||
|
||||
<rect
|
||||
mask=\\"url(#rect-mask)\\"
|
||||
data-testid=\\"lang-progress\\"
|
||||
x=\\"6.6495\\"
|
||||
y=\\"0\\"
|
||||
width=\\"0.465\\"
|
||||
height=\\"8\\"
|
||||
fill=\\"#2b7489\\"
|
||||
mask="url(#rect-mask)"
|
||||
data-testid="lang-progress"
|
||||
x="6.6495"
|
||||
y="0"
|
||||
width="0.465"
|
||||
height="8"
|
||||
fill="#3178c6"
|
||||
/>
|
||||
|
||||
|
||||
<g transform=\\"translate(25, 25)\\">
|
||||
<circle cx=\\"5\\" cy=\\"6\\" r=\\"5\\" fill=\\"#858585\\" />
|
||||
<text data-testid=\\"lang-name\\" x=\\"15\\" y=\\"10\\" class='lang-name'>
|
||||
<g transform="translate(25, 25)">
|
||||
<circle cx="5" cy="6" r="5" fill="#858585" />
|
||||
<text data-testid="lang-name" x="15" y="10" class='lang-name'>
|
||||
Other - 19 mins
|
||||
</text>
|
||||
</g>
|
||||
|
||||
<g transform=\\"translate(230, 25)\\">
|
||||
<circle cx=\\"5\\" cy=\\"6\\" r=\\"5\\" fill=\\"#2b7489\\" />
|
||||
<text data-testid=\\"lang-name\\" x=\\"15\\" y=\\"10\\" class='lang-name'>
|
||||
<g transform="translate(230, 25)">
|
||||
<circle cx="5" cy="6" r="5" fill="#3178c6" />
|
||||
<text data-testid="lang-name" x="15" y="10" class='lang-name'>
|
||||
TypeScript - 1 min
|
||||
</text>
|
||||
</g>
|
||||
|
||||
+7
-7
@@ -1,10 +1,10 @@
|
||||
require("@testing-library/jest-dom");
|
||||
const axios = require("axios");
|
||||
const MockAdapter = require("axios-mock-adapter");
|
||||
const api = require("../api/index");
|
||||
const renderStatsCard = require("../src/cards/stats-card");
|
||||
const { renderError, CONSTANTS } = require("../src/common/utils");
|
||||
const calculateRank = require("../src/calculateRank");
|
||||
import { jest } from "@jest/globals";
|
||||
import axios from "axios";
|
||||
import MockAdapter from "axios-mock-adapter";
|
||||
import api from "../api/index.js";
|
||||
import { calculateRank } from "../src/calculateRank.js";
|
||||
import { renderStatsCard } from "../src/cards/stats-card.js";
|
||||
import { CONSTANTS, renderError } from "../src/common/utils.js";
|
||||
|
||||
const stats = {
|
||||
name: "Anurag Hazra",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
require("@testing-library/jest-dom");
|
||||
const calculateRank = require("../src/calculateRank");
|
||||
import "@testing-library/jest-dom";
|
||||
import { calculateRank } from "../src/calculateRank.js";
|
||||
|
||||
describe("Test calculateRank", () => {
|
||||
it("should calculate rank correctly", () => {
|
||||
|
||||
+6
-6
@@ -1,9 +1,9 @@
|
||||
require("@testing-library/jest-dom");
|
||||
const cssToObject = require("@uppercod/css-to-object").cssToObject;
|
||||
const Card = require("../src/common/Card");
|
||||
const icons = require("../src/common/icons");
|
||||
const { getCardColors } = require("../src/common/utils");
|
||||
const { queryByTestId } = require("@testing-library/dom");
|
||||
import { queryByTestId } from "@testing-library/dom";
|
||||
import "@testing-library/jest-dom";
|
||||
import { cssToObject } from "@uppercod/css-to-object";
|
||||
import { Card } from "../src/common/Card.js";
|
||||
import { icons } from "../src/common/icons.js";
|
||||
import { getCardColors } from "../src/common/utils.js";
|
||||
|
||||
describe("Card", () => {
|
||||
it("should hide border", () => {
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
/**
|
||||
* @file Contains end-to-end tests for the Vercel preview instance.
|
||||
*/
|
||||
import dotenv from "dotenv";
|
||||
dotenv.config();
|
||||
|
||||
import { describe } from "@jest/globals";
|
||||
import axios from "axios";
|
||||
import { renderRepoCard } from "../../src/cards/repo-card.js";
|
||||
import { renderStatsCard } from "../../src/cards/stats-card.js";
|
||||
import { renderTopLanguages } from "../../src/cards/top-languages-card.js";
|
||||
import { renderWakatimeCard } from "../../src/cards/wakatime-card.js";
|
||||
|
||||
// Script variables
|
||||
const REPO = "dummy-cra";
|
||||
const USER = "grsdummy";
|
||||
const STATS_DATA = {
|
||||
name: "grsdummy",
|
||||
totalPRs: 2,
|
||||
totalCommits: 2,
|
||||
totalIssues: 1,
|
||||
totalStars: 1,
|
||||
contributedTo: 2,
|
||||
rank: {
|
||||
level: "A+",
|
||||
score: 51.01013099671447,
|
||||
},
|
||||
};
|
||||
const LANGS_DATA = {
|
||||
TypeScript: {
|
||||
color: "#3178c6",
|
||||
name: "TypeScript",
|
||||
size: 2049,
|
||||
},
|
||||
HTML: {
|
||||
color: "#e34c26",
|
||||
name: "HTML",
|
||||
size: 1721,
|
||||
},
|
||||
CSS: {
|
||||
color: "#563d7c",
|
||||
name: "CSS",
|
||||
size: 930,
|
||||
},
|
||||
Python: {
|
||||
color: "#3572A5",
|
||||
name: "Python",
|
||||
size: 671,
|
||||
},
|
||||
};
|
||||
const WAKATIME_DATA = {
|
||||
human_readable_range: "last week",
|
||||
is_already_updating: false,
|
||||
is_coding_activity_visible: false,
|
||||
is_including_today: false,
|
||||
is_other_usage_visible: false,
|
||||
is_stuck: false,
|
||||
is_up_to_date: false,
|
||||
is_up_to_date_pending_future: false,
|
||||
percent_calculated: 0,
|
||||
range: "last_7_days",
|
||||
status: "pending_update",
|
||||
timeout: 15,
|
||||
username: "grsdummy",
|
||||
writes_only: false,
|
||||
};
|
||||
const REPOSITORY_DATA = {
|
||||
name: "dummy-cra",
|
||||
nameWithOwner: "grsdummy/dummy-cra",
|
||||
isPrivate: false,
|
||||
isArchived: false,
|
||||
isTemplate: false,
|
||||
stargazers: {
|
||||
totalCount: 1,
|
||||
},
|
||||
description: "Dummy create react app.",
|
||||
primaryLanguage: {
|
||||
color: "#3178c6",
|
||||
id: "MDg6TGFuZ3VhZ2UyODc=",
|
||||
name: "TypeScript",
|
||||
},
|
||||
forkCount: 0,
|
||||
starCount: 1,
|
||||
};
|
||||
|
||||
describe("Fetch Cards", () => {
|
||||
let VERCEL_PREVIEW_URL;
|
||||
|
||||
beforeAll(() => {
|
||||
process.env.NODE_ENV = "development";
|
||||
VERCEL_PREVIEW_URL = process.env.VERCEL_PREVIEW_URL;
|
||||
});
|
||||
|
||||
test("retrieve stats card", async () => {
|
||||
expect(VERCEL_PREVIEW_URL).toBeDefined();
|
||||
|
||||
// Check if the Vercel preview instance stats card function is up and running.
|
||||
await expect(
|
||||
axios.get(`${VERCEL_PREVIEW_URL}/api?username=${USER}`),
|
||||
).resolves.not.toThrow();
|
||||
|
||||
// Get local stats card.
|
||||
const localStatsCardSVG = renderStatsCard(STATS_DATA);
|
||||
|
||||
// Get the Vercel preview stats card response.
|
||||
const serverStatsSvg = await axios.get(
|
||||
`${VERCEL_PREVIEW_URL}/api?username=${USER}`,
|
||||
);
|
||||
|
||||
// Check if stats card from deployment matches the stats card from local.
|
||||
expect(serverStatsSvg.data).toEqual(localStatsCardSVG);
|
||||
});
|
||||
|
||||
test("retrieve language card", async () => {
|
||||
expect(VERCEL_PREVIEW_URL).toBeDefined();
|
||||
|
||||
// Check if the Vercel preview instance language card function is up and running.
|
||||
await expect(
|
||||
axios.get(`${VERCEL_PREVIEW_URL}/api/top-langs/?username=${USER}`),
|
||||
).resolves.not.toThrow();
|
||||
|
||||
// Get local language card.
|
||||
const localLanguageCardSVG = renderTopLanguages(LANGS_DATA);
|
||||
|
||||
// Get the Vercel preview language card response.
|
||||
const severLanguageSVG = await axios.get(
|
||||
`${VERCEL_PREVIEW_URL}/api/top-langs/?username=${USER}`,
|
||||
);
|
||||
|
||||
// Check if language card from deployment matches the local language card.
|
||||
expect(severLanguageSVG.data).toEqual(localLanguageCardSVG);
|
||||
});
|
||||
|
||||
test("retrieve WakaTime card", async () => {
|
||||
expect(VERCEL_PREVIEW_URL).toBeDefined();
|
||||
|
||||
// Check if the Vercel preview instance WakaTime function is up and running.
|
||||
await expect(
|
||||
axios.get(`${VERCEL_PREVIEW_URL}/api/wakatime?username=${USER}`),
|
||||
).resolves.not.toThrow();
|
||||
|
||||
// Get local WakaTime card.
|
||||
const localWakaCardSVG = renderWakatimeCard(WAKATIME_DATA);
|
||||
|
||||
// Get the Vercel preview WakaTime card response.
|
||||
const serverWakaTimeSvg = await axios.get(
|
||||
`${VERCEL_PREVIEW_URL}/api/wakatime?username=${USER}`,
|
||||
);
|
||||
|
||||
// Check if WakaTime card from deployment matches the local WakaTime card.
|
||||
expect(serverWakaTimeSvg.data).toEqual(localWakaCardSVG);
|
||||
});
|
||||
|
||||
test("retrieve repo card", async () => {
|
||||
expect(VERCEL_PREVIEW_URL).toBeDefined();
|
||||
|
||||
// Check if the Vercel preview instance Repo function is up and running.
|
||||
await expect(
|
||||
axios.get(`${VERCEL_PREVIEW_URL}/api/pin/?username=${USER}&repo=${REPO}`),
|
||||
).resolves.not.toThrow();
|
||||
|
||||
// Get local repo card.
|
||||
const localRepoCardSVG = renderRepoCard(REPOSITORY_DATA);
|
||||
|
||||
// Get the Vercel preview repo card response.
|
||||
const serverRepoSvg = await axios.get(
|
||||
`${VERCEL_PREVIEW_URL}/api/pin/?username=${USER}&repo=${REPO}`,
|
||||
);
|
||||
|
||||
// Check if Repo card from deployment matches the local Repo card.
|
||||
expect(serverRepoSvg.data).toEqual(localRepoCardSVG);
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
require("@testing-library/jest-dom");
|
||||
const axios = require("axios");
|
||||
const MockAdapter = require("axios-mock-adapter");
|
||||
const fetchRepo = require("../src/fetchers/repo-fetcher");
|
||||
import "@testing-library/jest-dom";
|
||||
import axios from "axios";
|
||||
import MockAdapter from "axios-mock-adapter";
|
||||
import { fetchRepo } from "../src/fetchers/repo-fetcher.js";
|
||||
|
||||
const data_repo = {
|
||||
repository: {
|
||||
|
||||
+38
-10
@@ -1,8 +1,8 @@
|
||||
require("@testing-library/jest-dom");
|
||||
const axios = require("axios");
|
||||
const MockAdapter = require("axios-mock-adapter");
|
||||
const fetchStats = require("../src/fetchers/stats-fetcher");
|
||||
const calculateRank = require("../src/calculateRank");
|
||||
import "@testing-library/jest-dom";
|
||||
import axios from "axios";
|
||||
import MockAdapter from "axios-mock-adapter";
|
||||
import { calculateRank } from "../src/calculateRank.js";
|
||||
import { fetchStats } from "../src/fetchers/stats-fetcher.js";
|
||||
|
||||
const data = {
|
||||
data: {
|
||||
@@ -20,11 +20,11 @@ const data = {
|
||||
repositories: {
|
||||
totalCount: 5,
|
||||
nodes: [
|
||||
{ stargazers: { totalCount: 100 } },
|
||||
{ stargazers: { totalCount: 100 } },
|
||||
{ stargazers: { totalCount: 100 } },
|
||||
{ stargazers: { totalCount: 50 } },
|
||||
{ stargazers: { totalCount: 50 } },
|
||||
{ name: "test-repo-1", stargazers: { totalCount: 100 } },
|
||||
{ name: "test-repo-2", stargazers: { totalCount: 100 } },
|
||||
{ name: "test-repo-3", stargazers: { totalCount: 100 } },
|
||||
{ name: "test-repo-4", stargazers: { totalCount: 50 } },
|
||||
{ name: "test-repo-5", stargazers: { totalCount: 50 } },
|
||||
],
|
||||
},
|
||||
},
|
||||
@@ -134,4 +134,32 @@ describe("Test fetchStats", () => {
|
||||
rank,
|
||||
});
|
||||
});
|
||||
|
||||
it("should exclude stars of the `test-repo-1` repository", async () => {
|
||||
mock.onPost("https://api.github.com/graphql").reply(200, data);
|
||||
mock
|
||||
.onGet("https://api.github.com/search/commits?q=author:anuraghazra")
|
||||
.reply(200, { total_count: 1000 });
|
||||
|
||||
let stats = await fetchStats("anuraghazra", true, true, ["test-repo-1"]);
|
||||
const rank = calculateRank({
|
||||
totalCommits: 1050,
|
||||
totalRepos: 5,
|
||||
followers: 100,
|
||||
contributions: 61,
|
||||
stargazers: 300,
|
||||
prs: 300,
|
||||
issues: 200,
|
||||
});
|
||||
|
||||
expect(stats).toStrictEqual({
|
||||
contributedTo: 61,
|
||||
name: "Anurag Hazra",
|
||||
totalCommits: 1050,
|
||||
totalIssues: 200,
|
||||
totalPRs: 300,
|
||||
totalStars: 300,
|
||||
rank,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
require("@testing-library/jest-dom");
|
||||
const axios = require("axios");
|
||||
const MockAdapter = require("axios-mock-adapter");
|
||||
const fetchTopLanguages = require("../src/fetchers/top-languages-fetcher");
|
||||
import "@testing-library/jest-dom";
|
||||
import axios from "axios";
|
||||
import MockAdapter from "axios-mock-adapter";
|
||||
import { fetchTopLanguages } from "../src/fetchers/top-languages-fetcher.js";
|
||||
|
||||
const mock = new MockAdapter(axios);
|
||||
|
||||
@@ -81,7 +81,7 @@ describe("FetchTopLanguages", () => {
|
||||
it("should fetch correct language data while excluding the 'test-repo-1' repository", async () => {
|
||||
mock.onPost("https://api.github.com/graphql").reply(200, data_langs);
|
||||
|
||||
let repo = await fetchTopLanguages("anuraghazra", exclude_repo=["test-repo-1"]);
|
||||
let repo = await fetchTopLanguages("anuraghazra", ["test-repo-1"]);
|
||||
expect(repo).toStrictEqual({
|
||||
HTML: {
|
||||
color: "#0f0",
|
||||
|
||||
+17
-17
@@ -1,7 +1,7 @@
|
||||
require("@testing-library/jest-dom");
|
||||
const axios = require("axios");
|
||||
const MockAdapter = require("axios-mock-adapter");
|
||||
const { fetchWakatimeStats } = require("../src/fetchers/wakatime-fetcher");
|
||||
import "@testing-library/jest-dom";
|
||||
import axios from "axios";
|
||||
import MockAdapter from "axios-mock-adapter";
|
||||
import { fetchWakatimeStats } from "../src/fetchers/wakatime-fetcher.js";
|
||||
const mock = new MockAdapter(axios);
|
||||
|
||||
afterEach(() => {
|
||||
@@ -111,9 +111,9 @@ describe("Wakatime fetcher", () => {
|
||||
|
||||
const repo = await fetchWakatimeStats({ username });
|
||||
expect(repo).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"categories": Array [
|
||||
Object {
|
||||
{
|
||||
"categories": [
|
||||
{
|
||||
"digital": "22:40",
|
||||
"hours": 22,
|
||||
"minutes": 40,
|
||||
@@ -127,8 +127,8 @@ describe("Wakatime fetcher", () => {
|
||||
"daily_average_including_other_language": 16329,
|
||||
"days_including_holidays": 7,
|
||||
"days_minus_holidays": 5,
|
||||
"editors": Array [
|
||||
Object {
|
||||
"editors": [
|
||||
{
|
||||
"digital": "22:40",
|
||||
"hours": 22,
|
||||
"minutes": 40,
|
||||
@@ -150,8 +150,8 @@ describe("Wakatime fetcher", () => {
|
||||
"is_other_usage_visible": true,
|
||||
"is_stuck": false,
|
||||
"is_up_to_date": true,
|
||||
"languages": Array [
|
||||
Object {
|
||||
"languages": [
|
||||
{
|
||||
"digital": "0:19",
|
||||
"hours": 0,
|
||||
"minutes": 19,
|
||||
@@ -160,7 +160,7 @@ describe("Wakatime fetcher", () => {
|
||||
"text": "19 mins",
|
||||
"total_seconds": 1170.434361,
|
||||
},
|
||||
Object {
|
||||
{
|
||||
"digital": "0:01",
|
||||
"hours": 0,
|
||||
"minutes": 1,
|
||||
@@ -169,7 +169,7 @@ describe("Wakatime fetcher", () => {
|
||||
"text": "1 min",
|
||||
"total_seconds": 83.293809,
|
||||
},
|
||||
Object {
|
||||
{
|
||||
"digital": "0:00",
|
||||
"hours": 0,
|
||||
"minutes": 0,
|
||||
@@ -179,8 +179,8 @@ describe("Wakatime fetcher", () => {
|
||||
"total_seconds": 54.975151,
|
||||
},
|
||||
],
|
||||
"operating_systems": Array [
|
||||
Object {
|
||||
"operating_systems": [
|
||||
{
|
||||
"digital": "22:40",
|
||||
"hours": 22,
|
||||
"minutes": 40,
|
||||
@@ -207,9 +207,9 @@ describe("Wakatime fetcher", () => {
|
||||
mock.onGet(/\/https:\/\/wakatime\.com\/api/).reply(404, wakaTimeData);
|
||||
|
||||
await expect(fetchWakatimeStats("noone")).rejects.toThrow(
|
||||
"Missing params \"username\" make sure you pass the parameters in URL",
|
||||
'Missing params "username" make sure you pass the parameters in URL',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
module.exports = { wakaTimeData };
|
||||
export { wakaTimeData };
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const { flexLayout } = require("../src/common/utils");
|
||||
import { flexLayout } from "../src/common/utils.js";
|
||||
|
||||
describe("flexLayout", () => {
|
||||
it("should work with row & col layouts", () => {
|
||||
|
||||
+7
-6
@@ -1,9 +1,10 @@
|
||||
require("@testing-library/jest-dom");
|
||||
const axios = require("axios");
|
||||
const MockAdapter = require("axios-mock-adapter");
|
||||
const pin = require("../api/pin");
|
||||
const renderRepoCard = require("../src/cards/repo-card");
|
||||
const { renderError } = require("../src/common/utils");
|
||||
import { jest } from "@jest/globals";
|
||||
import "@testing-library/jest-dom";
|
||||
import axios from "axios";
|
||||
import MockAdapter from "axios-mock-adapter";
|
||||
import pin from "../api/pin.js";
|
||||
import { renderRepoCard } from "../src/cards/repo-card.js";
|
||||
import { renderError } from "../src/common/utils.js";
|
||||
|
||||
const data_repo = {
|
||||
repository: {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
require("@testing-library/jest-dom");
|
||||
const cssToObject = require("@uppercod/css-to-object").cssToObject;
|
||||
const renderRepoCard = require("../src/cards/repo-card");
|
||||
import { queryByTestId } from "@testing-library/dom";
|
||||
import "@testing-library/jest-dom";
|
||||
import { cssToObject } from "@uppercod/css-to-object";
|
||||
import { renderRepoCard } from "../src/cards/repo-card.js";
|
||||
|
||||
const { queryByTestId } = require("@testing-library/dom");
|
||||
const themes = require("../themes");
|
||||
import { themes } from "../themes/index.js";
|
||||
|
||||
const data_repo = {
|
||||
repository: {
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
require("@testing-library/jest-dom");
|
||||
const cssToObject = require("@uppercod/css-to-object").cssToObject;
|
||||
const renderStatsCard = require("../src/cards/stats-card");
|
||||
|
||||
const {
|
||||
import {
|
||||
getByTestId,
|
||||
queryByTestId,
|
||||
queryAllByTestId,
|
||||
} = require("@testing-library/dom");
|
||||
const themes = require("../themes");
|
||||
queryByTestId,
|
||||
} from "@testing-library/dom";
|
||||
import { cssToObject } from "@uppercod/css-to-object";
|
||||
import { renderStatsCard } from "../src/cards/stats-card.js";
|
||||
// adds special assertions like toHaveTextContent
|
||||
import "@testing-library/jest-dom";
|
||||
|
||||
import { themes } from "../themes/index.js";
|
||||
|
||||
describe("Test renderStatsCard", () => {
|
||||
const stats = {
|
||||
@@ -75,6 +76,52 @@ describe("Test renderStatsCard", () => {
|
||||
expect(queryByTestId(document.body, "rank-circle")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render with custom width set", () => {
|
||||
document.body.innerHTML = renderStatsCard(stats);
|
||||
expect(document.querySelector("svg")).toHaveAttribute("width", "495");
|
||||
|
||||
document.body.innerHTML = renderStatsCard(stats, { card_width: 400 });
|
||||
expect(document.querySelector("svg")).toHaveAttribute("width", "400");
|
||||
});
|
||||
|
||||
it("should render with custom width set and limit minimum width", () => {
|
||||
document.body.innerHTML = renderStatsCard(stats, { card_width: 1 });
|
||||
expect(document.querySelector("svg")).toHaveAttribute("width", "340");
|
||||
|
||||
document.body.innerHTML = renderStatsCard(stats, {
|
||||
card_width: 1,
|
||||
hide_rank: true,
|
||||
});
|
||||
expect(document.querySelector("svg")).toHaveAttribute(
|
||||
"width",
|
||||
"305.81250000000006",
|
||||
);
|
||||
|
||||
document.body.innerHTML = renderStatsCard(stats, {
|
||||
card_width: 1,
|
||||
hide_rank: true,
|
||||
show_icons: true,
|
||||
});
|
||||
expect(document.querySelector("svg")).toHaveAttribute(
|
||||
"width",
|
||||
"305.81250000000006",
|
||||
);
|
||||
|
||||
document.body.innerHTML = renderStatsCard(stats, {
|
||||
card_width: 1,
|
||||
hide_rank: false,
|
||||
show_icons: true,
|
||||
});
|
||||
expect(document.querySelector("svg")).toHaveAttribute("width", "356");
|
||||
|
||||
document.body.innerHTML = renderStatsCard(stats, {
|
||||
card_width: 1,
|
||||
hide_rank: false,
|
||||
show_icons: false,
|
||||
});
|
||||
expect(document.querySelector("svg")).toHaveAttribute("width", "340");
|
||||
});
|
||||
|
||||
it("should render default colors properly", () => {
|
||||
document.body.innerHTML = renderStatsCard(stats);
|
||||
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
require("@testing-library/jest-dom");
|
||||
const cssToObject = require("@uppercod/css-to-object").cssToObject;
|
||||
const renderTopLanguages = require("../src/cards/top-languages-card");
|
||||
import { queryAllByTestId, queryByTestId } from "@testing-library/dom";
|
||||
import { cssToObject } from "@uppercod/css-to-object";
|
||||
import {
|
||||
MIN_CARD_WIDTH,
|
||||
renderTopLanguages,
|
||||
} from "../src/cards/top-languages-card.js";
|
||||
// adds special assertions like toHaveTextContent
|
||||
import "@testing-library/jest-dom";
|
||||
|
||||
const { queryByTestId, queryAllByTestId } = require("@testing-library/dom");
|
||||
const themes = require("../themes");
|
||||
import { themes } from "../themes/index.js";
|
||||
|
||||
describe("Test renderTopLanguages", () => {
|
||||
const langs = {
|
||||
@@ -108,13 +112,13 @@ describe("Test renderTopLanguages", () => {
|
||||
|
||||
expect(document.querySelector("svg")).toHaveAttribute(
|
||||
"width",
|
||||
renderTopLanguages.MIN_CARD_WIDTH.toString(),
|
||||
MIN_CARD_WIDTH.toString(),
|
||||
);
|
||||
|
||||
document.body.innerHTML = renderTopLanguages(langs, { card_width: 100 });
|
||||
expect(document.querySelector("svg")).toHaveAttribute(
|
||||
"width",
|
||||
renderTopLanguages.MIN_CARD_WIDTH.toString(),
|
||||
MIN_CARD_WIDTH.toString(),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -247,7 +251,7 @@ describe("Test renderTopLanguages", () => {
|
||||
});
|
||||
|
||||
it("should render langs with specified langs_count", async () => {
|
||||
options = {
|
||||
const options = {
|
||||
langs_count: 1,
|
||||
};
|
||||
document.body.innerHTML = renderTopLanguages(langs, { ...options });
|
||||
@@ -257,7 +261,7 @@ describe("Test renderTopLanguages", () => {
|
||||
});
|
||||
|
||||
it("should render langs with specified langs_count even when hide is set", async () => {
|
||||
options = {
|
||||
const options = {
|
||||
hide: ["HTML"],
|
||||
langs_count: 2,
|
||||
};
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
require("@testing-library/jest-dom");
|
||||
const { queryByTestId } = require("@testing-library/dom");
|
||||
|
||||
const renderWakatimeCard = require("../src/cards/wakatime-card");
|
||||
const { wakaTimeData } = require("./fetchWakatime.test");
|
||||
import { queryByTestId } from "@testing-library/dom";
|
||||
import "@testing-library/jest-dom";
|
||||
import { renderWakatimeCard } from "../src/cards/wakatime-card.js";
|
||||
import { getCardColors } from "../src/common/utils.js";
|
||||
import { wakaTimeData } from "./fetchWakatime.test.js";
|
||||
|
||||
describe("Test Render Wakatime Card", () => {
|
||||
it("should render correctly", () => {
|
||||
const card = renderWakatimeCard(wakaTimeData.data);
|
||||
|
||||
expect(card).toMatchSnapshot();
|
||||
expect(getCardColors).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should render correctly with compact layout", () => {
|
||||
@@ -48,10 +47,15 @@ describe("Test Render Wakatime Card", () => {
|
||||
});
|
||||
|
||||
it('should show "no coding activitiy this week" message when there hasn not been activity', () => {
|
||||
document.body.innerHTML = renderWakatimeCard({
|
||||
...wakaTimeData.data,
|
||||
languages: undefined
|
||||
}, {});
|
||||
expect(document.querySelector(".stat").textContent).toBe("No coding activity this week")
|
||||
})
|
||||
document.body.innerHTML = renderWakatimeCard(
|
||||
{
|
||||
...wakaTimeData.data,
|
||||
languages: undefined,
|
||||
},
|
||||
{},
|
||||
);
|
||||
expect(document.querySelector(".stat").textContent).toBe(
|
||||
"No coding activity this week",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
require("@testing-library/jest-dom");
|
||||
const retryer = require("../src/common/retryer");
|
||||
const { logger } = require("../src/common/utils");
|
||||
import { jest } from "@jest/globals";
|
||||
import "@testing-library/jest-dom";
|
||||
import { retryer } from "../src/common/retryer.js";
|
||||
import { logger } from "../src/common/utils.js";
|
||||
|
||||
const fetcher = jest.fn((variables, token) => {
|
||||
logger.log(variables, token);
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
require("@testing-library/jest-dom");
|
||||
const axios = require("axios");
|
||||
const MockAdapter = require("axios-mock-adapter");
|
||||
const topLangs = require("../api/top-langs");
|
||||
const renderTopLanguages = require("../src/cards/top-languages-card");
|
||||
const { renderError } = require("../src/common/utils");
|
||||
import { jest } from "@jest/globals";
|
||||
import "@testing-library/jest-dom";
|
||||
import axios from "axios";
|
||||
import MockAdapter from "axios-mock-adapter";
|
||||
import topLangs from "../api/top-langs.js";
|
||||
import { renderTopLanguages } from "../src/cards/top-languages-card.js";
|
||||
import { renderError } from "../src/common/utils.js";
|
||||
|
||||
const data_langs = {
|
||||
data: {
|
||||
|
||||
+9
-9
@@ -1,14 +1,12 @@
|
||||
require("@testing-library/jest-dom");
|
||||
const {
|
||||
kFormatter,
|
||||
import { queryByTestId } from "@testing-library/dom";
|
||||
import "@testing-library/jest-dom";
|
||||
import {
|
||||
encodeHTML,
|
||||
renderError,
|
||||
flexLayout,
|
||||
getCardColors,
|
||||
kFormatter,
|
||||
renderError,
|
||||
wrapTextMultiline,
|
||||
} = require("../src/common/utils");
|
||||
|
||||
const { queryByTestId } = require("@testing-library/dom");
|
||||
} from "../src/common/utils.js";
|
||||
|
||||
describe("Test utils.js", () => {
|
||||
it("should test kFormatter", () => {
|
||||
@@ -32,7 +30,9 @@ describe("Test utils.js", () => {
|
||||
expect(
|
||||
queryByTestId(document.body, "message").children[0],
|
||||
).toHaveTextContent(/Something went wrong/gim);
|
||||
expect(queryByTestId(document.body, "message").children[1]).toBeEmpty(2);
|
||||
expect(
|
||||
queryByTestId(document.body, "message").children[1],
|
||||
).toBeEmptyDOMElement(2);
|
||||
|
||||
// Secondary message
|
||||
document.body.innerHTML = renderError(
|
||||
|
||||
+44
-40
@@ -16,26 +16,27 @@ Use `?theme=THEME_NAME` parameter like so :-
|
||||
|
||||
| | | |
|
||||
| :--: | :--: | :--: |
|
||||
| `default` ![default][default] | `dark` ![dark][dark] | `radical` ![radical][radical] |
|
||||
| `merko` ![merko][merko] | `gruvbox` ![gruvbox][gruvbox] | `gruvbox_light` ![gruvbox_light][gruvbox_light] |
|
||||
| `tokyonight` ![tokyonight][tokyonight] | `onedark` ![onedark][onedark] | `cobalt` ![cobalt][cobalt] |
|
||||
| `synthwave` ![synthwave][synthwave] | `highcontrast` ![highcontrast][highcontrast] | `dracula` ![dracula][dracula] |
|
||||
| `prussian` ![prussian][prussian] | `monokai` ![monokai][monokai] | `vue` ![vue][vue] |
|
||||
| `vue-dark` ![vue-dark][vue-dark] | `shades-of-purple` ![shades-of-purple][shades-of-purple] | `nightowl` ![nightowl][nightowl] |
|
||||
| `buefy` ![buefy][buefy] | `blue-green` ![blue-green][blue-green] | `algolia` ![algolia][algolia] |
|
||||
| `great-gatsby` ![great-gatsby][great-gatsby] | `darcula` ![darcula][darcula] | `bear` ![bear][bear] |
|
||||
| `solarized-dark` ![solarized-dark][solarized-dark] | `solarized-light` ![solarized-light][solarized-light] | `chartreuse-dark` ![chartreuse-dark][chartreuse-dark] |
|
||||
| `nord` ![nord][nord] | `gotham` ![gotham][gotham] | `material-palenight` ![material-palenight][material-palenight] |
|
||||
| `graywhite` ![graywhite][graywhite] | `vision-friendly-dark` ![vision-friendly-dark][vision-friendly-dark] | `ayu-mirage` ![ayu-mirage][ayu-mirage] |
|
||||
| `midnight-purple` ![midnight-purple][midnight-purple] | `calm` ![calm][calm] | `flag-india` ![flag-india][flag-india] |
|
||||
| `omni` ![omni][omni] | `react` ![react][react] | `jolly` ![jolly][jolly] |
|
||||
| `maroongold` ![maroongold][maroongold] | `yeblu` ![yeblu][yeblu] | `blueberry` ![blueberry][blueberry] |
|
||||
| `slateorange` ![slateorange][slateorange] | `kacho_ga` ![kacho_ga][kacho_ga] | `outrun` ![outrun][outrun] |
|
||||
| `ocean_dark` ![ocean_dark][ocean_dark] | `city_lights` ![city_lights][city_lights] | `github_dark` ![github_dark][github_dark] |
|
||||
| `discord_old_blurple` ![discord_old_blurple][discord_old_blurple] | `aura_dark` ![aura_dark][aura_dark] | `panda` ![panda][panda] |
|
||||
| `noctis_minimus` ![noctis_minimus][noctis_minimus] | `cobalt2` ![cobalt2][cobalt2] | `swift` ![swift][swift] |
|
||||
| `aura` ![aura][aura] | `apprentice` ![apprentice][apprentice] | `moltack` ![moltack][moltack] |
|
||||
| `codeSTACKr` ![codeSTACKr][codeSTACKr] | `rose_pine` ![rose_pine][rose_pine] | [Add your theme][add-theme] |
|
||||
| `default` ![default][default] | `transparent` ![transparent][transparent] | `dark` ![dark][dark] |
|
||||
| `radical` ![radical][radical] | `merko` ![merko][merko] | `gruvbox` ![gruvbox][gruvbox] |
|
||||
| `gruvbox_light` ![gruvbox_light][gruvbox_light] | `tokyonight` ![tokyonight][tokyonight] | `onedark` ![onedark][onedark] |
|
||||
| `cobalt` ![cobalt][cobalt] | `synthwave` ![synthwave][synthwave] | `highcontrast` ![highcontrast][highcontrast] |
|
||||
| `dracula` ![dracula][dracula] | `prussian` ![prussian][prussian] | `monokai` ![monokai][monokai] |
|
||||
| `vue` ![vue][vue] | `vue-dark` ![vue-dark][vue-dark] | `shades-of-purple` ![shades-of-purple][shades-of-purple] |
|
||||
| `nightowl` ![nightowl][nightowl] | `buefy` ![buefy][buefy] | `blue-green` ![blue-green][blue-green] |
|
||||
| `algolia` ![algolia][algolia] | `great-gatsby` ![great-gatsby][great-gatsby] | `darcula` ![darcula][darcula] |
|
||||
| `bear` ![bear][bear] | `solarized-dark` ![solarized-dark][solarized-dark] | `solarized-light` ![solarized-light][solarized-light] |
|
||||
| `chartreuse-dark` ![chartreuse-dark][chartreuse-dark] | `nord` ![nord][nord] | `gotham` ![gotham][gotham] |
|
||||
| `material-palenight` ![material-palenight][material-palenight] | `graywhite` ![graywhite][graywhite] | `vision-friendly-dark` ![vision-friendly-dark][vision-friendly-dark] |
|
||||
| `ayu-mirage` ![ayu-mirage][ayu-mirage] | `midnight-purple` ![midnight-purple][midnight-purple] | `calm` ![calm][calm] |
|
||||
| `flag-india` ![flag-india][flag-india] | `omni` ![omni][omni] | `react` ![react][react] |
|
||||
| `jolly` ![jolly][jolly] | `maroongold` ![maroongold][maroongold] | `yeblu` ![yeblu][yeblu] |
|
||||
| `blueberry` ![blueberry][blueberry] | `slateorange` ![slateorange][slateorange] | `kacho_ga` ![kacho_ga][kacho_ga] |
|
||||
| `outrun` ![outrun][outrun] | `ocean_dark` ![ocean_dark][ocean_dark] | `city_lights` ![city_lights][city_lights] |
|
||||
| `github_dark` ![github_dark][github_dark] | `discord_old_blurple` ![discord_old_blurple][discord_old_blurple] | `aura_dark` ![aura_dark][aura_dark] |
|
||||
| `panda` ![panda][panda] | `noctis_minimus` ![noctis_minimus][noctis_minimus] | `cobalt2` ![cobalt2][cobalt2] |
|
||||
| `swift` ![swift][swift] | `aura` ![aura][aura] | `apprentice` ![apprentice][apprentice] |
|
||||
| `moltack` ![moltack][moltack] | `codeSTACKr` ![codeSTACKr][codeSTACKr] | `rose_pine` ![rose_pine][rose_pine] |
|
||||
| [Add your theme][add-theme] | | |
|
||||
|
||||
## Repo Card
|
||||
|
||||
@@ -43,30 +44,32 @@ Use `?theme=THEME_NAME` parameter like so :-
|
||||
|
||||
| | | |
|
||||
| :--: | :--: | :--: |
|
||||
| `default_repocard` ![default_repocard][default_repocard_repo] | `dark` ![dark][dark_repo] | `radical` ![radical][radical_repo] |
|
||||
| `merko` ![merko][merko_repo] | `gruvbox` ![gruvbox][gruvbox_repo] | `gruvbox_light` ![gruvbox_light][gruvbox_light_repo] |
|
||||
| `tokyonight` ![tokyonight][tokyonight_repo] | `onedark` ![onedark][onedark_repo] | `cobalt` ![cobalt][cobalt_repo] |
|
||||
| `synthwave` ![synthwave][synthwave_repo] | `highcontrast` ![highcontrast][highcontrast_repo] | `dracula` ![dracula][dracula_repo] |
|
||||
| `prussian` ![prussian][prussian_repo] | `monokai` ![monokai][monokai_repo] | `vue` ![vue][vue_repo] |
|
||||
| `vue-dark` ![vue-dark][vue-dark_repo] | `shades-of-purple` ![shades-of-purple][shades-of-purple_repo] | `nightowl` ![nightowl][nightowl_repo] |
|
||||
| `buefy` ![buefy][buefy_repo] | `blue-green` ![blue-green][blue-green_repo] | `algolia` ![algolia][algolia_repo] |
|
||||
| `great-gatsby` ![great-gatsby][great-gatsby_repo] | `darcula` ![darcula][darcula_repo] | `bear` ![bear][bear_repo] |
|
||||
| `solarized-dark` ![solarized-dark][solarized-dark_repo] | `solarized-light` ![solarized-light][solarized-light_repo] | `chartreuse-dark` ![chartreuse-dark][chartreuse-dark_repo] |
|
||||
| `nord` ![nord][nord_repo] | `gotham` ![gotham][gotham_repo] | `material-palenight` ![material-palenight][material-palenight_repo] |
|
||||
| `graywhite` ![graywhite][graywhite_repo] | `vision-friendly-dark` ![vision-friendly-dark][vision-friendly-dark_repo] | `ayu-mirage` ![ayu-mirage][ayu-mirage_repo] |
|
||||
| `midnight-purple` ![midnight-purple][midnight-purple_repo] | `calm` ![calm][calm_repo] | `flag-india` ![flag-india][flag-india_repo] |
|
||||
| `omni` ![omni][omni_repo] | `react` ![react][react_repo] | `jolly` ![jolly][jolly_repo] |
|
||||
| `maroongold` ![maroongold][maroongold_repo] | `yeblu` ![yeblu][yeblu_repo] | `blueberry` ![blueberry][blueberry_repo] |
|
||||
| `slateorange` ![slateorange][slateorange_repo] | `kacho_ga` ![kacho_ga][kacho_ga_repo] | `outrun` ![outrun][outrun_repo] |
|
||||
| `ocean_dark` ![ocean_dark][ocean_dark_repo] | `city_lights` ![city_lights][city_lights_repo] | `github_dark` ![github_dark][github_dark_repo] |
|
||||
| `discord_old_blurple` ![discord_old_blurple][discord_old_blurple_repo] | `aura_dark` ![aura_dark][aura_dark_repo] | `panda` ![panda][panda_repo] |
|
||||
| `noctis_minimus` ![noctis_minimus][noctis_minimus_repo] | `cobalt2` ![cobalt2][cobalt2_repo] | `swift` ![swift][swift_repo] |
|
||||
| `aura` ![aura][aura_repo] | `apprentice` ![apprentice][apprentice_repo] | `moltack` ![moltack][moltack_repo] |
|
||||
| `codeSTACKr` ![codeSTACKr][codeSTACKr_repo] | `rose_pine` ![rose_pine][rose_pine_repo] | [Add your theme][add-theme] |
|
||||
| `default_repocard` ![default_repocard][default_repocard_repo] | `transparent` ![transparent][transparent_repo] | `dark` ![dark][dark_repo] |
|
||||
| `radical` ![radical][radical_repo] | `merko` ![merko][merko_repo] | `gruvbox` ![gruvbox][gruvbox_repo] |
|
||||
| `gruvbox_light` ![gruvbox_light][gruvbox_light_repo] | `tokyonight` ![tokyonight][tokyonight_repo] | `onedark` ![onedark][onedark_repo] |
|
||||
| `cobalt` ![cobalt][cobalt_repo] | `synthwave` ![synthwave][synthwave_repo] | `highcontrast` ![highcontrast][highcontrast_repo] |
|
||||
| `dracula` ![dracula][dracula_repo] | `prussian` ![prussian][prussian_repo] | `monokai` ![monokai][monokai_repo] |
|
||||
| `vue` ![vue][vue_repo] | `vue-dark` ![vue-dark][vue-dark_repo] | `shades-of-purple` ![shades-of-purple][shades-of-purple_repo] |
|
||||
| `nightowl` ![nightowl][nightowl_repo] | `buefy` ![buefy][buefy_repo] | `blue-green` ![blue-green][blue-green_repo] |
|
||||
| `algolia` ![algolia][algolia_repo] | `great-gatsby` ![great-gatsby][great-gatsby_repo] | `darcula` ![darcula][darcula_repo] |
|
||||
| `bear` ![bear][bear_repo] | `solarized-dark` ![solarized-dark][solarized-dark_repo] | `solarized-light` ![solarized-light][solarized-light_repo] |
|
||||
| `chartreuse-dark` ![chartreuse-dark][chartreuse-dark_repo] | `nord` ![nord][nord_repo] | `gotham` ![gotham][gotham_repo] |
|
||||
| `material-palenight` ![material-palenight][material-palenight_repo] | `graywhite` ![graywhite][graywhite_repo] | `vision-friendly-dark` ![vision-friendly-dark][vision-friendly-dark_repo] |
|
||||
| `ayu-mirage` ![ayu-mirage][ayu-mirage_repo] | `midnight-purple` ![midnight-purple][midnight-purple_repo] | `calm` ![calm][calm_repo] |
|
||||
| `flag-india` ![flag-india][flag-india_repo] | `omni` ![omni][omni_repo] | `react` ![react][react_repo] |
|
||||
| `jolly` ![jolly][jolly_repo] | `maroongold` ![maroongold][maroongold_repo] | `yeblu` ![yeblu][yeblu_repo] |
|
||||
| `blueberry` ![blueberry][blueberry_repo] | `slateorange` ![slateorange][slateorange_repo] | `kacho_ga` ![kacho_ga][kacho_ga_repo] |
|
||||
| `outrun` ![outrun][outrun_repo] | `ocean_dark` ![ocean_dark][ocean_dark_repo] | `city_lights` ![city_lights][city_lights_repo] |
|
||||
| `github_dark` ![github_dark][github_dark_repo] | `discord_old_blurple` ![discord_old_blurple][discord_old_blurple_repo] | `aura_dark` ![aura_dark][aura_dark_repo] |
|
||||
| `panda` ![panda][panda_repo] | `noctis_minimus` ![noctis_minimus][noctis_minimus_repo] | `cobalt2` ![cobalt2][cobalt2_repo] |
|
||||
| `swift` ![swift][swift_repo] | `aura` ![aura][aura_repo] | `apprentice` ![apprentice][apprentice_repo] |
|
||||
| `moltack` ![moltack][moltack_repo] | `codeSTACKr` ![codeSTACKr][codeSTACKr_repo] | `rose_pine` ![rose_pine][rose_pine_repo] |
|
||||
| [Add your theme][add-theme] | | |
|
||||
|
||||
|
||||
[default]: https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true&hide=contribs,prs&cache_seconds=86400&theme=default
|
||||
[default_repocard]: https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true&hide=contribs,prs&cache_seconds=86400&theme=default_repocard
|
||||
[transparent]: https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true&hide=contribs,prs&cache_seconds=86400&theme=transparent
|
||||
[dark]: https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true&hide=contribs,prs&cache_seconds=86400&theme=dark
|
||||
[radical]: https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true&hide=contribs,prs&cache_seconds=86400&theme=radical
|
||||
[merko]: https://github-readme-stats.vercel.app/api?username=anuraghazra&show_icons=true&hide=contribs,prs&cache_seconds=86400&theme=merko
|
||||
@@ -129,6 +132,7 @@ Use `?theme=THEME_NAME` parameter like so :-
|
||||
|
||||
[default_repo]: https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&cache_seconds=86400&theme=default
|
||||
[default_repocard_repo]: https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&cache_seconds=86400&theme=default_repocard
|
||||
[transparent_repo]: https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&cache_seconds=86400&theme=transparent
|
||||
[dark_repo]: https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&cache_seconds=86400&theme=dark
|
||||
[radical_repo]: https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&cache_seconds=86400&theme=radical
|
||||
[merko_repo]: https://github-readme-stats.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&cache_seconds=86400&theme=merko
|
||||
|
||||
+10
-4
@@ -1,4 +1,4 @@
|
||||
const themes = {
|
||||
export const themes = {
|
||||
default: {
|
||||
title_color: "2f80ed",
|
||||
icon_color: "4c71f2",
|
||||
@@ -12,6 +12,12 @@ const themes = {
|
||||
text_color: "434d58",
|
||||
bg_color: "fffefe",
|
||||
},
|
||||
transparent: {
|
||||
title_color: "006AFF",
|
||||
icon_color: "0579C3",
|
||||
text_color: "417E87",
|
||||
bg_color: "ffffff00",
|
||||
},
|
||||
dark: {
|
||||
title_color: "fff",
|
||||
icon_color: "79ff97",
|
||||
@@ -355,12 +361,12 @@ const themes = {
|
||||
bg_color: "09131B",
|
||||
border_color: "0c1a25",
|
||||
},
|
||||
"rose_pine":{
|
||||
rose_pine: {
|
||||
title_color: "9ccfd8",
|
||||
icon_color: "ebbcba",
|
||||
text_color: "e0def4",
|
||||
bg_color: "191724",
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = themes;
|
||||
export default themes;
|
||||
|
||||
Reference in New Issue
Block a user