fix tests, add CI, fix on mac, extend docs (#25)
* tests: fix float problems and PAT problem * extend CONTRIBUTING.md * extend build documentation, add GH action, fix build on MacOS * fix build on Vercel * update .gitignore and CONTRIBUTING.md * fix all tests * add backend tests to CI * make e2e tests work * add debug log to e2e test * ignore copied tests in CI
This commit is contained in:
@@ -2,12 +2,15 @@
|
||||
|
||||
## Local Development
|
||||
|
||||
To run and test GitHub-Stats-Extended, you need to follow a few simple steps:
|
||||
To set up the project GitHub-Stats-Extended locally, run the following commands:
|
||||
|
||||
1. create [your own deployment](../docs/deploy.md)
|
||||
2. optional: add an SQL database; by using e.g. the ["Nile" integration](https://vercel.com/marketplace/nile) or by manually setting the environment variable `POSTGRES_URL`
|
||||
3. optional: [create your own OAuth App](https://github.com/settings/developers) and set environment variables `OAUTH_REDIRECT_URI`, `OAUTH_CLIENT_ID` and `OAUTH_CLIENT_SECRET` on Vercel accordingly
|
||||
4. optional: in addition to the Vercel project based on the `backend` folder, create a second project based on the `frontend/frontend` folder. No environment variables needed.
|
||||
```bash
|
||||
./vercel-preparation.sh
|
||||
(cd ./backend/ && npm install)
|
||||
(cd ./frontend/frontend/ && yarn install && yarn build-trends)
|
||||
```
|
||||
|
||||
The easiest way to run and test the project is to deploy it to Vercel as described in the [deployment guide](../docs/deploy.md).
|
||||
|
||||
## Themes Contribution
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
name: Basic Build
|
||||
|
||||
on:
|
||||
workflow_dispatch: # Allows you to run this manually from the Actions tab
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
execute:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: latest
|
||||
|
||||
- name: Install Yarn
|
||||
run: npm install -g yarn
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Install and Build
|
||||
run: |
|
||||
chmod +x ./vercel-preparation.sh
|
||||
./vercel-preparation.sh
|
||||
(cd ./backend/ && npm install)
|
||||
(cd ./frontend/frontend/ && yarn install && yarn build-trends)
|
||||
|
||||
- name: Run Backend Tests
|
||||
run: |
|
||||
(cd ./backend/ && npm test)
|
||||
@@ -5,6 +5,7 @@ backend/*.lock
|
||||
backend/coverage
|
||||
backend/benchmarks
|
||||
backend/vercel_token
|
||||
backend-copy
|
||||
frontend/frontend/.env
|
||||
frontend/frontend/src/backend
|
||||
|
||||
|
||||
+11
-2
@@ -4,10 +4,19 @@ export default {
|
||||
testEnvironment: "jsdom",
|
||||
coverageProvider: "v8",
|
||||
setupFiles: ["<rootDir>/tests/setup.jest.js"],
|
||||
testPathIgnorePatterns: ["<rootDir>/node_modules/", "<rootDir>/tests/e2e/"],
|
||||
modulePathIgnorePatterns: ["<rootDir>/node_modules/", "<rootDir>/tests/e2e/"],
|
||||
testPathIgnorePatterns: [
|
||||
"<rootDir>/node_modules/",
|
||||
"<rootDir>/tests/e2e/",
|
||||
"<rootDir>/.vercel/",
|
||||
],
|
||||
modulePathIgnorePatterns: [
|
||||
"<rootDir>/node_modules/",
|
||||
"<rootDir>/tests/e2e/",
|
||||
"<rootDir>/.vercel/",
|
||||
],
|
||||
coveragePathIgnorePatterns: [
|
||||
"<rootDir>/node_modules/",
|
||||
"<rootDir>/tests/E2E/",
|
||||
"<rootDir>/.vercel/",
|
||||
],
|
||||
};
|
||||
|
||||
Generated
+18
-48
@@ -42,7 +42,7 @@
|
||||
"prettier": "^3.7.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22"
|
||||
"node": "24.x"
|
||||
}
|
||||
},
|
||||
"node_modules/@aashutoshrathi/word-wrap": {
|
||||
@@ -3422,18 +3422,6 @@
|
||||
"url": "https://opencollective.com/eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint/node_modules/@eslint/js": {
|
||||
"version": "9.39.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz",
|
||||
"integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://eslint.org/donate"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint/node_modules/escape-string-regexp": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||
@@ -6331,6 +6319,17 @@
|
||||
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/path-to-regexp": {
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz",
|
||||
"integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/pg": {
|
||||
"version": "8.16.2",
|
||||
"resolved": "https://registry.npmjs.org/pg/-/pg-8.16.2.tgz",
|
||||
@@ -6429,17 +6428,6 @@
|
||||
"node": ">= 10.x"
|
||||
}
|
||||
},
|
||||
"node_modules/path-to-regexp": {
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz",
|
||||
"integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
@@ -6491,12 +6479,6 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/platform": {
|
||||
"version": "1.3.6",
|
||||
"resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz",
|
||||
"integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/postgres-array": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
|
||||
@@ -10403,12 +10385,6 @@
|
||||
"optionator": "^0.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@eslint/js": {
|
||||
"version": "9.39.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz",
|
||||
"integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==",
|
||||
"dev": true
|
||||
},
|
||||
"escape-string-regexp": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||
@@ -12555,6 +12531,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"path-to-regexp": {
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz",
|
||||
"integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==",
|
||||
"dev": true
|
||||
},
|
||||
"pg": {
|
||||
"version": "8.16.2",
|
||||
"resolved": "https://registry.npmjs.org/pg/-/pg-8.16.2.tgz",
|
||||
@@ -12622,12 +12604,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"path-to-regexp": {
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz",
|
||||
"integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==",
|
||||
"dev": true
|
||||
},
|
||||
"picocolors": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
@@ -12661,12 +12637,6 @@
|
||||
"find-up": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"platform": {
|
||||
"version": "1.3.6",
|
||||
"resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz",
|
||||
"integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==",
|
||||
"dev": true
|
||||
},
|
||||
"postgres-array": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
|
||||
|
||||
@@ -36,14 +36,13 @@ const retryer = async (fetcher, username, variables) => {
|
||||
);
|
||||
PATs = patNames.map((name) => ({ name, value: process.env[name] }));
|
||||
}
|
||||
const RETRIES = process.env.NODE_ENV === "test" ? 7 : PATs.length;
|
||||
|
||||
if (!RETRIES) {
|
||||
if (!PATs.length) {
|
||||
throw new CustomError("No GitHub API tokens found", CustomError.NO_TOKENS);
|
||||
}
|
||||
const startPAT = getRandomInt(PATs.length);
|
||||
|
||||
for (let retries = 0; retries < RETRIES; retries++) {
|
||||
for (let retries = 0; retries < PATs.length; retries++) {
|
||||
const currentPAT = PATs[(startPAT + retries) % PATs.length];
|
||||
|
||||
try {
|
||||
|
||||
@@ -131,3 +131,17 @@ export const runAndLogStats = async (
|
||||
|
||||
return stats;
|
||||
};
|
||||
|
||||
export function approxNumber(expected, precision = 10) {
|
||||
return {
|
||||
asymmetricMatch(actual) {
|
||||
if (typeof actual !== "number" || typeof expected !== "number")
|
||||
return false;
|
||||
const epsilon = Math.pow(10, -precision);
|
||||
return Math.abs(actual - expected) < epsilon;
|
||||
},
|
||||
toAsymmetricMatcher() {
|
||||
return `≈ ${expected} (precision ${precision})`;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { describe, expect, it } from "@jest/globals";
|
||||
import "@testing-library/jest-dom";
|
||||
import { calculateRank } from "../src/calculateRank.js";
|
||||
import { approxNumber } from "./bench/utils.js";
|
||||
|
||||
describe("Test calculateRank", () => {
|
||||
it("new user gets C rank", () => {
|
||||
@@ -30,7 +31,10 @@ describe("Test calculateRank", () => {
|
||||
stars: 25,
|
||||
followers: 5,
|
||||
}),
|
||||
).toStrictEqual({ level: "B-", percentile: 65.02918514848255 });
|
||||
).toStrictEqual({
|
||||
level: "B-",
|
||||
percentile: approxNumber(65.02918514848255),
|
||||
});
|
||||
});
|
||||
|
||||
it("median user gets B+ rank", () => {
|
||||
@@ -45,7 +49,7 @@ describe("Test calculateRank", () => {
|
||||
stars: 50,
|
||||
followers: 10,
|
||||
}),
|
||||
).toStrictEqual({ level: "B+", percentile: 46.09375 });
|
||||
).toStrictEqual({ level: "B+", percentile: approxNumber(46.09375) });
|
||||
});
|
||||
|
||||
it("average user gets B+ rank (include_all_commits)", () => {
|
||||
@@ -60,7 +64,7 @@ describe("Test calculateRank", () => {
|
||||
stars: 50,
|
||||
followers: 10,
|
||||
}),
|
||||
).toStrictEqual({ level: "B+", percentile: 46.09375 });
|
||||
).toStrictEqual({ level: "B+", percentile: approxNumber(46.09375) });
|
||||
});
|
||||
|
||||
it("advanced user gets A rank", () => {
|
||||
@@ -75,7 +79,10 @@ describe("Test calculateRank", () => {
|
||||
stars: 200,
|
||||
followers: 40,
|
||||
}),
|
||||
).toStrictEqual({ level: "A", percentile: 20.841471354166664 });
|
||||
).toStrictEqual({
|
||||
level: "A",
|
||||
percentile: approxNumber(20.841471354166664),
|
||||
});
|
||||
});
|
||||
|
||||
it("expert user gets A+ rank", () => {
|
||||
@@ -90,7 +97,10 @@ describe("Test calculateRank", () => {
|
||||
stars: 800,
|
||||
followers: 160,
|
||||
}),
|
||||
).toStrictEqual({ level: "A+", percentile: 5.575988339442828 });
|
||||
).toStrictEqual({
|
||||
level: "A+",
|
||||
percentile: approxNumber(5.575988339442828),
|
||||
});
|
||||
});
|
||||
|
||||
it("sindresorhus gets S rank", () => {
|
||||
@@ -105,6 +115,9 @@ describe("Test calculateRank", () => {
|
||||
stars: 600000,
|
||||
followers: 50000,
|
||||
}),
|
||||
).toStrictEqual({ level: "S", percentile: 0.4578556547153667 });
|
||||
).toStrictEqual({
|
||||
level: "S",
|
||||
percentile: approxNumber(0.4578556547153667),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -105,11 +105,10 @@ const GIST_DATA = {
|
||||
const CACHE_BURST_STRING = `v=${new Date().getTime()}`;
|
||||
|
||||
describe("Fetch Cards", () => {
|
||||
let VERCEL_PREVIEW_URL;
|
||||
let VERCEL_PREVIEW_URL = "https://github-stats-extended.vercel.app";
|
||||
|
||||
beforeAll(() => {
|
||||
process.env.NODE_ENV = "development";
|
||||
VERCEL_PREVIEW_URL = process.env.VERCEL_PREVIEW_URL;
|
||||
});
|
||||
|
||||
test("retrieve stats card", async () => {
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/** @jest-environment node */
|
||||
/*
|
||||
Jest must use node for these tests because jsdom doesn't support the structuredClone call in stats.js.
|
||||
https://jestjs.io/docs/configuration#testenvironment-string
|
||||
https://github.com/jsdom/jsdom/issues/3363
|
||||
*/
|
||||
|
||||
import { afterEach, beforeEach, describe, expect, it } from "@jest/globals";
|
||||
import "@testing-library/jest-dom";
|
||||
import axios from "axios";
|
||||
@@ -519,6 +526,11 @@ describe("Test fetchStats", () => {
|
||||
totalStars: 300,
|
||||
totalDiscussionsStarted: 0,
|
||||
totalDiscussionsAnswered: 0,
|
||||
totalIssuesAuthored: 0,
|
||||
totalIssuesCommented: 0,
|
||||
totalPRsAuthored: 0,
|
||||
totalPRsCommented: 0,
|
||||
totalPRsReviewed: 0,
|
||||
rank,
|
||||
});
|
||||
});
|
||||
@@ -552,6 +564,11 @@ describe("Test fetchStats", () => {
|
||||
totalStars: 300,
|
||||
totalDiscussionsStarted: 0,
|
||||
totalDiscussionsAnswered: 0,
|
||||
totalIssuesAuthored: 0,
|
||||
totalIssuesCommented: 0,
|
||||
totalPRsAuthored: 0,
|
||||
totalPRsCommented: 0,
|
||||
totalPRsReviewed: 0,
|
||||
rank,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,6 +3,7 @@ import "@testing-library/jest-dom";
|
||||
import axios from "axios";
|
||||
import MockAdapter from "axios-mock-adapter";
|
||||
import { fetchTopLanguages } from "../src/fetchers/top-languages.js";
|
||||
import { approxNumber } from "./bench/utils.js";
|
||||
|
||||
const mock = new MockAdapter(axios);
|
||||
|
||||
@@ -70,13 +71,13 @@ describe("FetchTopLanguages", () => {
|
||||
color: "#0f0",
|
||||
count: 2,
|
||||
name: "HTML",
|
||||
size: 20.000000000000004,
|
||||
size: approxNumber(20.0),
|
||||
},
|
||||
javascript: {
|
||||
color: "#0ff",
|
||||
count: 2,
|
||||
name: "javascript",
|
||||
size: 20.000000000000004,
|
||||
size: approxNumber(20.0),
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
} from "../src/cards/top-languages.js";
|
||||
|
||||
import { themes } from "../themes/index.js";
|
||||
import { approxNumber } from "./bench/utils.js";
|
||||
|
||||
const langs = {
|
||||
HTML: {
|
||||
@@ -120,89 +121,104 @@ describe("Test renderTopLanguages helper functions", () => {
|
||||
});
|
||||
|
||||
it("degreesToRadians", () => {
|
||||
expect(degreesToRadians(0)).toBe(0);
|
||||
expect(degreesToRadians(90)).toBe(Math.PI / 2);
|
||||
expect(degreesToRadians(180)).toBe(Math.PI);
|
||||
expect(degreesToRadians(270)).toBe((3 * Math.PI) / 2);
|
||||
expect(degreesToRadians(360)).toBe(2 * Math.PI);
|
||||
expect(degreesToRadians(0)).toBeCloseTo(0);
|
||||
expect(degreesToRadians(90)).toBeCloseTo(Math.PI / 2);
|
||||
expect(degreesToRadians(180)).toBeCloseTo(Math.PI);
|
||||
expect(degreesToRadians(270)).toBeCloseTo((3 * Math.PI) / 2);
|
||||
expect(degreesToRadians(360)).toBeCloseTo(2 * Math.PI);
|
||||
});
|
||||
|
||||
it("radiansToDegrees", () => {
|
||||
expect(radiansToDegrees(0)).toBe(0);
|
||||
expect(radiansToDegrees(Math.PI / 2)).toBe(90);
|
||||
expect(radiansToDegrees(Math.PI)).toBe(180);
|
||||
expect(radiansToDegrees((3 * Math.PI) / 2)).toBe(270);
|
||||
expect(radiansToDegrees(2 * Math.PI)).toBe(360);
|
||||
expect(radiansToDegrees(0)).toBeCloseTo(0);
|
||||
expect(radiansToDegrees(Math.PI / 2)).toBeCloseTo(90);
|
||||
expect(radiansToDegrees(Math.PI)).toBeCloseTo(180);
|
||||
expect(radiansToDegrees((3 * Math.PI) / 2)).toBeCloseTo(270);
|
||||
expect(radiansToDegrees(2 * Math.PI)).toBeCloseTo(360);
|
||||
});
|
||||
|
||||
it("polarToCartesian", () => {
|
||||
expect(polarToCartesian(100, 100, 60, 0)).toStrictEqual({ x: 160, y: 100 });
|
||||
expect(polarToCartesian(100, 100, 60, 0)).toStrictEqual({
|
||||
x: approxNumber(160),
|
||||
y: approxNumber(100),
|
||||
});
|
||||
expect(polarToCartesian(100, 100, 60, 45)).toStrictEqual({
|
||||
x: 142.42640687119285,
|
||||
y: 142.42640687119285,
|
||||
x: approxNumber(142.42640687119285),
|
||||
y: approxNumber(142.42640687119285),
|
||||
});
|
||||
expect(polarToCartesian(100, 100, 60, 90)).toStrictEqual({
|
||||
x: 100,
|
||||
y: 160,
|
||||
x: approxNumber(100),
|
||||
y: approxNumber(160),
|
||||
});
|
||||
expect(polarToCartesian(100, 100, 60, 135)).toStrictEqual({
|
||||
x: 57.573593128807154,
|
||||
y: 142.42640687119285,
|
||||
x: approxNumber(57.573593128807154),
|
||||
y: approxNumber(142.42640687119285),
|
||||
});
|
||||
expect(polarToCartesian(100, 100, 60, 180)).toStrictEqual({
|
||||
x: 40,
|
||||
y: 100.00000000000001,
|
||||
x: approxNumber(40),
|
||||
y: approxNumber(100),
|
||||
});
|
||||
expect(polarToCartesian(100, 100, 60, 225)).toStrictEqual({
|
||||
x: 57.57359312880714,
|
||||
y: 57.573593128807154,
|
||||
x: approxNumber(57.57359312880714),
|
||||
y: approxNumber(57.573593128807154),
|
||||
});
|
||||
expect(polarToCartesian(100, 100, 60, 270)).toStrictEqual({
|
||||
x: 99.99999999999999,
|
||||
y: 40,
|
||||
x: approxNumber(100),
|
||||
y: approxNumber(40),
|
||||
});
|
||||
expect(polarToCartesian(100, 100, 60, 315)).toStrictEqual({
|
||||
x: 142.42640687119285,
|
||||
y: 57.57359312880714,
|
||||
x: approxNumber(142.42640687119285),
|
||||
y: approxNumber(57.57359312880714),
|
||||
});
|
||||
expect(polarToCartesian(100, 100, 60, 360)).toStrictEqual({
|
||||
x: 160,
|
||||
y: 99.99999999999999,
|
||||
x: approxNumber(160),
|
||||
y: approxNumber(100),
|
||||
});
|
||||
});
|
||||
|
||||
it("cartesianToPolar", () => {
|
||||
expect(cartesianToPolar(100, 100, 160, 100)).toStrictEqual({
|
||||
radius: 60,
|
||||
angleInDegrees: 0,
|
||||
radius: approxNumber(60),
|
||||
angleInDegrees: approxNumber(0),
|
||||
});
|
||||
expect(
|
||||
cartesianToPolar(100, 100, 142.42640687119285, 142.42640687119285),
|
||||
).toStrictEqual({ radius: 60.00000000000001, angleInDegrees: 45 });
|
||||
).toStrictEqual({
|
||||
radius: approxNumber(60),
|
||||
angleInDegrees: approxNumber(45),
|
||||
});
|
||||
expect(cartesianToPolar(100, 100, 100, 160)).toStrictEqual({
|
||||
radius: 60,
|
||||
angleInDegrees: 90,
|
||||
radius: approxNumber(60),
|
||||
angleInDegrees: approxNumber(90),
|
||||
});
|
||||
expect(
|
||||
cartesianToPolar(100, 100, 57.573593128807154, 142.42640687119285),
|
||||
).toStrictEqual({ radius: 60, angleInDegrees: 135 });
|
||||
).toStrictEqual({
|
||||
radius: approxNumber(60),
|
||||
angleInDegrees: approxNumber(135),
|
||||
});
|
||||
expect(cartesianToPolar(100, 100, 40, 100.00000000000001)).toStrictEqual({
|
||||
radius: 60,
|
||||
angleInDegrees: 180,
|
||||
radius: approxNumber(60),
|
||||
angleInDegrees: approxNumber(180),
|
||||
});
|
||||
expect(
|
||||
cartesianToPolar(100, 100, 57.57359312880714, 57.573593128807154),
|
||||
).toStrictEqual({ radius: 60, angleInDegrees: 225 });
|
||||
).toStrictEqual({
|
||||
radius: approxNumber(60),
|
||||
angleInDegrees: approxNumber(225),
|
||||
});
|
||||
expect(cartesianToPolar(100, 100, 99.99999999999999, 40)).toStrictEqual({
|
||||
radius: 60,
|
||||
angleInDegrees: 270,
|
||||
radius: approxNumber(60),
|
||||
angleInDegrees: approxNumber(270),
|
||||
});
|
||||
expect(
|
||||
cartesianToPolar(100, 100, 142.42640687119285, 57.57359312880714),
|
||||
).toStrictEqual({ radius: 60.00000000000001, angleInDegrees: 315 });
|
||||
).toStrictEqual({
|
||||
radius: approxNumber(60),
|
||||
angleInDegrees: approxNumber(315),
|
||||
});
|
||||
expect(cartesianToPolar(100, 100, 160, 99.99999999999999)).toStrictEqual({
|
||||
radius: 60,
|
||||
angleInDegrees: 360,
|
||||
radius: approxNumber(60),
|
||||
angleInDegrees: approxNumber(360),
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { describe, expect, it, jest } from "@jest/globals";
|
||||
import "@testing-library/jest-dom";
|
||||
import { RETRIES, retryer } from "../src/common/retryer.js";
|
||||
import { retryer } from "../src/common/retryer.js";
|
||||
import { logger } from "../src/common/log.js";
|
||||
|
||||
const fetcher = jest.fn((variables, token) => {
|
||||
@@ -75,7 +75,7 @@ describe("Test Retryer", () => {
|
||||
try {
|
||||
await retryer(fetcherFail, {});
|
||||
} catch (err) {
|
||||
expect(fetcherFail).toHaveBeenCalledTimes(RETRIES + 1);
|
||||
expect(fetcherFail).toHaveBeenCalledTimes(2);
|
||||
// @ts-ignore
|
||||
expect(err.message).toBe("Downtime due to GitHub API rate limiting");
|
||||
}
|
||||
|
||||
@@ -3,3 +3,5 @@
|
||||
import { TextEncoder, TextDecoder } from "util";
|
||||
|
||||
Object.assign(global, { TextDecoder, TextEncoder });
|
||||
process.env.PAT_1 = "dummyPAT1";
|
||||
process.env.PAT_2 = "dummyPAT2";
|
||||
|
||||
@@ -62,6 +62,9 @@ Click on the deploy button to get started!
|
||||

|
||||
11. As `Root directory` select the `backend` folder.
|
||||
12. Click deploy, and you're good to go. See your domains to use the API!
|
||||
13. optional: add an SQL database; by using e.g. the ["Nile" integration](https://vercel.com/marketplace/nile) or by manually setting the environment variable `POSTGRES_URL`
|
||||
14. optional: [create your own OAuth App](https://github.com/settings/developers) and set environment variables `OAUTH_REDIRECT_URI`, `OAUTH_CLIENT_ID` and `OAUTH_CLIENT_SECRET` on Vercel accordingly
|
||||
15. optional: in addition to the Vercel project based on the `backend` folder, create a second project based on the `frontend/frontend` folder. No environment variables needed.
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
+14
-6
@@ -1,7 +1,15 @@
|
||||
# move into the folder of this script
|
||||
cd "$(dirname "$0")" || exit 1
|
||||
#!/bin/bash
|
||||
|
||||
mkdir --parents backend/.vercel/output/functions/api.func/
|
||||
cp --recursive backend/* backend/.vercel/output/functions/api.func/
|
||||
cp --recursive backend/.vercel/output/functions/api.func/_dot_vercel_copy/output/ backend/.vercel/
|
||||
cp --recursive backend frontend/frontend/src/backend/
|
||||
# defensive error handling
|
||||
set -euo pipefail
|
||||
|
||||
# move into the folder of this script
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
mkdir -p backend/.vercel/output/functions/api.func/
|
||||
# copying `backend` to `backend/.vercel/...` directly may cause problems
|
||||
cp -R backend/. backend-copy/
|
||||
# `shopt` includes dot-files in the `mv` operation
|
||||
(shopt -s dotglob && mv backend-copy/* backend/.vercel/output/functions/api.func/)
|
||||
cp -R backend/.vercel/output/functions/api.func/_dot_vercel_copy/output backend/.vercel/
|
||||
cp -R backend frontend/frontend/src/backend/
|
||||
|
||||
Reference in New Issue
Block a user