feat: Best Practices (#102)
* feat: best practices * feat: add tests workflow * feat: add best-practices workflow * fix: wrong indentation in workflow
This commit is contained in:
107
.eslintrc
107
.eslintrc
@@ -4,12 +4,9 @@
|
|||||||
"browser": true,
|
"browser": true,
|
||||||
"es6": true,
|
"es6": true,
|
||||||
"node": true,
|
"node": true,
|
||||||
"webextensions": true
|
"webextensions": true,
|
||||||
},
|
},
|
||||||
"ignorePatterns": [
|
"ignorePatterns": ["*.html", "tsconfig.json"],
|
||||||
"*.html",
|
|
||||||
"tsconfig.json"
|
|
||||||
],
|
|
||||||
"extends": [
|
"extends": [
|
||||||
"eslint:recommended",
|
"eslint:recommended",
|
||||||
"plugin:react/recommended",
|
"plugin:react/recommended",
|
||||||
@@ -21,18 +18,14 @@
|
|||||||
"@unocss",
|
"@unocss",
|
||||||
"prettier",
|
"prettier",
|
||||||
],
|
],
|
||||||
"plugins": [
|
"plugins": ["import", "jsdoc", "react-prefer-function-component", "@typescript-eslint", "simple-import-sort"],
|
||||||
"import",
|
|
||||||
"jsdoc",
|
|
||||||
"react-prefer-function-component"
|
|
||||||
],
|
|
||||||
"globals": {
|
"globals": {
|
||||||
"Atomics": "readonly",
|
"Atomics": "readonly",
|
||||||
"SharedArrayBuffer": "readonly",
|
"SharedArrayBuffer": "readonly",
|
||||||
"debugger": true,
|
"debugger": true,
|
||||||
"browser": true,
|
"browser": true,
|
||||||
"context": true,
|
"context": true,
|
||||||
"JSX": true
|
"JSX": true,
|
||||||
},
|
},
|
||||||
"parser": "@typescript-eslint/parser",
|
"parser": "@typescript-eslint/parser",
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
@@ -42,36 +35,33 @@
|
|||||||
"ecmaFeatures": {
|
"ecmaFeatures": {
|
||||||
"jsx": true,
|
"jsx": true,
|
||||||
"modules": true,
|
"modules": true,
|
||||||
"experimentalObjectRestSpread": true
|
"experimentalObjectRestSpread": true,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"react": {
|
"react": {
|
||||||
"version": "detect"
|
"version": "detect",
|
||||||
},
|
},
|
||||||
"jsdoc": {
|
"jsdoc": {
|
||||||
"mode": "typescript"
|
"mode": "typescript",
|
||||||
},
|
},
|
||||||
"import/parsers": {
|
"import/parsers": {
|
||||||
"@typescript-eslint/parser": [
|
"@typescript-eslint/parser": [".ts", ".tsx"],
|
||||||
".ts",
|
|
||||||
".tsx"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"import/resolver": {
|
"import/resolver": {
|
||||||
"typescript": {
|
"typescript": {
|
||||||
"alwaysTryTypes": true,
|
"alwaysTryTypes": true,
|
||||||
"project": "./tsconfig.json"
|
"project": "./tsconfig.json",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
"rules": {
|
"rules": {
|
||||||
"prefer-const": [
|
"prefer-const": [
|
||||||
"off",
|
"off",
|
||||||
{
|
{
|
||||||
"destructuring": "any",
|
"destructuring": "any",
|
||||||
"ignoreReadBeforeAssign": false
|
"ignoreReadBeforeAssign": false,
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
"no-plusplus": "off",
|
"no-plusplus": "off",
|
||||||
"no-inner-declarations": "off",
|
"no-inner-declarations": "off",
|
||||||
@@ -83,20 +73,16 @@
|
|||||||
"no-undef": "off",
|
"no-undef": "off",
|
||||||
"no-return-await": "off",
|
"no-return-await": "off",
|
||||||
"@typescript-eslint/return-await": "off",
|
"@typescript-eslint/return-await": "off",
|
||||||
"@typescript-eslint/no-shadow": [
|
"@typescript-eslint/no-shadow": ["off"],
|
||||||
"off"
|
"@typescript-eslint/no-use-before-define": ["off"],
|
||||||
],
|
|
||||||
"@typescript-eslint/no-use-before-define": [
|
|
||||||
"off"
|
|
||||||
],
|
|
||||||
"class-methods-use-this": "off",
|
"class-methods-use-this": "off",
|
||||||
"react-hooks/exhaustive-deps": "warn",
|
"react-hooks/exhaustive-deps": "warn",
|
||||||
"@typescript-eslint/lines-between-class-members": "off",
|
"@typescript-eslint/lines-between-class-members": "off",
|
||||||
"no-param-reassign": [
|
"no-param-reassign": [
|
||||||
"error",
|
"error",
|
||||||
{
|
{
|
||||||
"props": false
|
"props": false,
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
"no-console": "off",
|
"no-console": "off",
|
||||||
"consistent-return": "off",
|
"consistent-return": "off",
|
||||||
@@ -110,8 +96,8 @@
|
|||||||
"error",
|
"error",
|
||||||
{
|
{
|
||||||
"before": true,
|
"before": true,
|
||||||
"after": true
|
"after": true,
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
"no-continue": "off",
|
"no-continue": "off",
|
||||||
"space-before-blocks": [
|
"space-before-blocks": [
|
||||||
@@ -119,24 +105,22 @@
|
|||||||
{
|
{
|
||||||
"functions": "always",
|
"functions": "always",
|
||||||
"keywords": "always",
|
"keywords": "always",
|
||||||
"classes": "always"
|
"classes": "always",
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
"react/jsx-filename-extension": [
|
"react/jsx-filename-extension": [
|
||||||
1,
|
1,
|
||||||
{
|
{
|
||||||
"extensions": [
|
"extensions": [".tsx"],
|
||||||
".tsx"
|
},
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
"react/no-deprecated": "warn",
|
"react/no-deprecated": "warn",
|
||||||
"react/prop-types": "off",
|
"react/prop-types": "off",
|
||||||
"react-prefer-function-component/react-prefer-function-component": [
|
"react-prefer-function-component/react-prefer-function-component": [
|
||||||
"warn",
|
"warn",
|
||||||
{
|
{
|
||||||
"allowComponentDidCatch": false
|
"allowComponentDidCatch": false,
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
"react/function-component-definition": "off",
|
"react/function-component-definition": "off",
|
||||||
"react/button-has-type": "off",
|
"react/button-has-type": "off",
|
||||||
@@ -154,7 +138,7 @@
|
|||||||
"ArrowFunctionExpression": true,
|
"ArrowFunctionExpression": true,
|
||||||
"ClassDeclaration": true,
|
"ClassDeclaration": true,
|
||||||
"ClassExpression": true,
|
"ClassExpression": true,
|
||||||
"FunctionExpression": true
|
"FunctionExpression": true,
|
||||||
},
|
},
|
||||||
"contexts": [
|
"contexts": [
|
||||||
"MethodDefinition:not([key.name=\"componentDidMount\"]):not([key.name=\"render\"])",
|
"MethodDefinition:not([key.name=\"componentDidMount\"]):not([key.name=\"render\"])",
|
||||||
@@ -169,9 +153,9 @@
|
|||||||
"TSInterfaceDeclaration",
|
"TSInterfaceDeclaration",
|
||||||
"TSMethodSignature",
|
"TSMethodSignature",
|
||||||
"TSModuleDeclaration",
|
"TSModuleDeclaration",
|
||||||
"TSTypeAliasDeclaration"
|
"TSTypeAliasDeclaration",
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
"@typescript-eslint/no-explicit-any": "off",
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
"@typescript-eslint/no-unused-vars": "warn",
|
"@typescript-eslint/no-unused-vars": "warn",
|
||||||
@@ -186,31 +170,36 @@
|
|||||||
{
|
{
|
||||||
"target": "./src/background",
|
"target": "./src/background",
|
||||||
"from": "./src/views",
|
"from": "./src/views",
|
||||||
"message": "You cannot import into the `background` directory from the `views` directory (i.e. content script files) because it will break the build!"
|
"message": "You cannot import into the `background` directory from the `views` directory (i.e. content script files) because it will break the build!",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"target": "./src/views",
|
"target": "./src/views",
|
||||||
"from": "./src/background",
|
"from": "./src/background",
|
||||||
"message": "You cannot import into the `views` directory from the `background` directory (i.e. background script files) because it will break the build!"
|
"message": "You cannot import into the `views` directory from the `background` directory (i.e. background script files) because it will break the build!",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"target": "./src/shared",
|
"target": "./src/shared",
|
||||||
"from": "./",
|
"from": "./",
|
||||||
"except": [
|
"except": ["./src/shared", "./node_modules"],
|
||||||
"./src/shared",
|
"message": "You cannot import into `shared` from an external directory.",
|
||||||
"./node_modules"
|
},
|
||||||
],
|
],
|
||||||
"message": "You cannot import into `shared` from an external directory."
|
},
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
"import/extensions": "off",
|
"import/extensions": "off",
|
||||||
"no-restricted-syntax": [
|
"no-restricted-syntax": [
|
||||||
"error",
|
"error",
|
||||||
"ForInStatement",
|
"ForInStatement",
|
||||||
"LabeledStatement",
|
"LabeledStatement",
|
||||||
"WithStatement"
|
"WithStatement",
|
||||||
]
|
{
|
||||||
}
|
"selector": "TSEnumDeclaration",
|
||||||
|
"message": "Don't declare enums",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"@typescript-eslint/consistent-type-exports": "error",
|
||||||
|
"@typescript-eslint/consistent-type-imports": "error",
|
||||||
|
"simple-import-sort/imports": "error",
|
||||||
|
"simple-import-sort/exports": "error",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
43
.github/workflows/best-practices.yml
vendored
Normal file
43
.github/workflows/best-practices.yml
vendored
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
name: Best Practices
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Setup pnpm
|
||||||
|
uses: pnpm/action-setup@v3
|
||||||
|
with:
|
||||||
|
version: 8
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: pnpm install
|
||||||
|
|
||||||
|
- name: Run ESLint
|
||||||
|
run: pnpm run lint
|
||||||
|
format:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Setup pnpm
|
||||||
|
uses: pnpm/action-setup@v3
|
||||||
|
with:
|
||||||
|
version: 8
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: pnpm install
|
||||||
|
|
||||||
|
- name: Run Prettier
|
||||||
|
run: pnpm run prettier
|
||||||
24
.github/workflows/tests.yml
vendored
Normal file
24
.github/workflows/tests.yml
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
name: Tests
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Setup pnpm
|
||||||
|
uses: pnpm/action-setup@v3
|
||||||
|
with:
|
||||||
|
version: 8
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: pnpm install
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: pnpm test
|
||||||
46
package.json
46
package.json
@@ -9,7 +9,13 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "tsc && vite build",
|
"build": "tsc && vite build",
|
||||||
|
"prettier": "prettier src --check",
|
||||||
|
"prettier:fix": "prettier src --write",
|
||||||
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||||
|
"lint:fix": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0 --fix",
|
||||||
|
"test": "vitest",
|
||||||
|
"test:ui": "vitest --ui",
|
||||||
|
"coverage": "vitest run --coverage",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"devtools": "react-devtools",
|
"devtools": "react-devtools",
|
||||||
"preinstall": "npx only-allow pnpm",
|
"preinstall": "npx only-allow pnpm",
|
||||||
@@ -31,9 +37,7 @@
|
|||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-devtools-core": "^5.0.0",
|
"react-devtools-core": "^5.0.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-icons": "^5.0.1",
|
"sass": "^1.71.1",
|
||||||
"react-window": "^1.8.10",
|
|
||||||
"sass": "^1.70.0",
|
|
||||||
"sql.js": "1.10.2",
|
"sql.js": "1.10.2",
|
||||||
"styled-components": "^6.1.8",
|
"styled-components": "^6.1.8",
|
||||||
"uuid": "^9.0.1"
|
"uuid": "^9.0.1"
|
||||||
@@ -42,22 +46,22 @@
|
|||||||
"@commitlint/cli": "^18.6.1",
|
"@commitlint/cli": "^18.6.1",
|
||||||
"@commitlint/config-conventional": "^18.6.2",
|
"@commitlint/config-conventional": "^18.6.2",
|
||||||
"@crxjs/vite-plugin": "2.0.0-beta.21",
|
"@crxjs/vite-plugin": "2.0.0-beta.21",
|
||||||
"@iconify-json/material-symbols": "^1.1.72",
|
"@iconify-json/material-symbols": "^1.1.73",
|
||||||
"@storybook/addon-designs": "^7.0.9",
|
"@storybook/addon-designs": "^7.0.9",
|
||||||
"@storybook/addon-essentials": "^7.6.13",
|
"@storybook/addon-essentials": "^7.6.17",
|
||||||
"@storybook/addon-links": "^7.6.13",
|
"@storybook/addon-links": "^7.6.17",
|
||||||
"@storybook/blocks": "^7.6.13",
|
"@storybook/blocks": "^7.6.17",
|
||||||
"@storybook/react": "^7.6.13",
|
"@storybook/react": "^7.6.17",
|
||||||
"@storybook/react-vite": "^7.6.13",
|
"@storybook/react-vite": "^7.6.17",
|
||||||
"@storybook/test": "^7.6.13",
|
"@storybook/test": "^7.6.17",
|
||||||
"@svgr/core": "^8.1.0",
|
"@svgr/core": "^8.1.0",
|
||||||
"@svgr/plugin-jsx": "^8.1.0",
|
"@svgr/plugin-jsx": "^8.1.0",
|
||||||
"@types/chrome": "^0.0.260",
|
"@types/chrome": "^0.0.260",
|
||||||
"@types/node": "^20.11.17",
|
"@types/node": "^20.11.19",
|
||||||
"@types/prompts": "^2.4.9",
|
"@types/prompts": "^2.4.9",
|
||||||
"@types/react": "^18.2.55",
|
"@types/react": "^18.2.57",
|
||||||
"@types/react-dom": "^18.2.19",
|
"@types/react-dom": "^18.2.19",
|
||||||
"@types/semver": "^7.5.6",
|
"@types/semver": "^7.5.7",
|
||||||
"@types/uuid": "^9.0.8",
|
"@types/uuid": "^9.0.8",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
||||||
"@typescript-eslint/parser": "^6.21.0",
|
"@typescript-eslint/parser": "^6.21.0",
|
||||||
@@ -69,10 +73,12 @@
|
|||||||
"@unocss/transformer-directives": "^0.58.5",
|
"@unocss/transformer-directives": "^0.58.5",
|
||||||
"@unocss/transformer-variant-group": "^0.58.5",
|
"@unocss/transformer-variant-group": "^0.58.5",
|
||||||
"@vitejs/plugin-react-swc": "^3.6.0",
|
"@vitejs/plugin-react-swc": "^3.6.0",
|
||||||
"chromatic": "^10.9.1",
|
"@vitest/coverage-v8": "^1.3.1",
|
||||||
|
"@vitest/ui": "^1.3.1",
|
||||||
|
"chromatic": "^10.9.6",
|
||||||
"cssnano": "^6.0.3",
|
"cssnano": "^6.0.3",
|
||||||
"cssnano-preset-advanced": "^6.0.3",
|
"cssnano-preset-advanced": "^6.0.3",
|
||||||
"dotenv": "^16.4.1",
|
"dotenv": "^16.4.5",
|
||||||
"es-module-lexer": "^1.4.1",
|
"es-module-lexer": "^1.4.1",
|
||||||
"eslint": "^8.56.0",
|
"eslint": "^8.56.0",
|
||||||
"eslint-config-airbnb": "^19.0.4",
|
"eslint-config-airbnb": "^19.0.4",
|
||||||
@@ -81,12 +87,13 @@
|
|||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-import-resolver-typescript": "^3.6.1",
|
"eslint-import-resolver-typescript": "^3.6.1",
|
||||||
"eslint-plugin-import": "^2.29.1",
|
"eslint-plugin-import": "^2.29.1",
|
||||||
"eslint-plugin-jsdoc": "^48.0.6",
|
"eslint-plugin-jsdoc": "^48.1.0",
|
||||||
"eslint-plugin-prettier": "^5.1.3",
|
"eslint-plugin-prettier": "^5.1.3",
|
||||||
"eslint-plugin-react": "^7.33.2",
|
"eslint-plugin-react": "^7.33.2",
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
"eslint-plugin-react-prefer-function-component": "^3.3.0",
|
"eslint-plugin-react-prefer-function-component": "^3.3.0",
|
||||||
"eslint-plugin-react-refresh": "^0.4.5",
|
"eslint-plugin-react-refresh": "^0.4.5",
|
||||||
|
"eslint-plugin-simple-import-sort": "^12.0.0",
|
||||||
"eslint-plugin-storybook": "^0.6.15",
|
"eslint-plugin-storybook": "^0.6.15",
|
||||||
"husky": "^9.0.11",
|
"husky": "^9.0.11",
|
||||||
"path": "^0.12.7",
|
"path": "^0.12.7",
|
||||||
@@ -94,12 +101,13 @@
|
|||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"react-dev-utils": "^12.0.1",
|
"react-dev-utils": "^12.0.1",
|
||||||
"react-devtools": "^5.0.0",
|
"react-devtools": "^5.0.0",
|
||||||
"storybook": "^7.6.13",
|
"storybook": "^7.6.17",
|
||||||
"typescript": "^5.3.3",
|
"typescript": "^5.3.3",
|
||||||
"unocss": "^0.58.5",
|
"unocss": "^0.58.5",
|
||||||
"unplugin-icons": "^0.18.5",
|
"unplugin-icons": "^0.18.5",
|
||||||
"vite": "^5.1.1",
|
"vite": "^5.1.4",
|
||||||
"vite-plugin-inspect": "^0.8.3"
|
"vite-plugin-inspect": "^0.8.3",
|
||||||
|
"vitest": "^1.3.1"
|
||||||
},
|
},
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"patchedDependencies": {
|
"patchedDependencies": {
|
||||||
|
|||||||
3146
pnpm-lock.yaml
generated
3146
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@ export interface CourseColors {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// calculates luminance of a hex string
|
// calculates luminance of a hex string
|
||||||
function getLuminance(hex: string): number {
|
export function getLuminance(hex: string): number {
|
||||||
let r = parseInt(hex.substring(1, 3), 16);
|
let r = parseInt(hex.substring(1, 3), 16);
|
||||||
let g = parseInt(hex.substring(3, 5), 16);
|
let g = parseInt(hex.substring(3, 5), 16);
|
||||||
let b = parseInt(hex.substring(5, 7), 16);
|
let b = parseInt(hex.substring(5, 7), 16);
|
||||||
|
|||||||
22
src/shared/util/tests/colors.test.ts
Normal file
22
src/shared/util/tests/colors.test.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
|
import { getLuminance } from '../colors';
|
||||||
|
|
||||||
|
describe('getLuminance', () => {
|
||||||
|
it('should return the correct luminance value for a given hex color', () => {
|
||||||
|
// Test case 1: Hex color #FFFFFF (white)
|
||||||
|
expect(getLuminance('#FFFFFF')).toBeCloseTo(1);
|
||||||
|
|
||||||
|
// Test case 2: Hex color #000000 (black)
|
||||||
|
expect(getLuminance('#000000')).toBeCloseTo(0);
|
||||||
|
|
||||||
|
// Test case 3: Hex color #FF0000 (red)
|
||||||
|
expect(getLuminance('#FF0000')).toBeCloseTo(0.2126);
|
||||||
|
|
||||||
|
// Test case 4: Hex color #00FF00 (green)
|
||||||
|
expect(getLuminance('#00FF00')).toBeCloseTo(0.7152);
|
||||||
|
|
||||||
|
// Test case 5: Hex color #0000FF (blue)
|
||||||
|
expect(getLuminance('#0000FF')).toBeCloseTo(0.0722);
|
||||||
|
});
|
||||||
|
});
|
||||||
26
src/shared/util/tests/random.test.ts
Normal file
26
src/shared/util/tests/random.test.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
|
import { generateRandomId } from '../random';
|
||||||
|
|
||||||
|
describe('generateRandomId', () => {
|
||||||
|
it('should generate a random ID with the specified length', () => {
|
||||||
|
// Test case 1: Length 5
|
||||||
|
expect(generateRandomId(5)).toHaveLength(5);
|
||||||
|
|
||||||
|
// Test case 2: Length 10
|
||||||
|
expect(generateRandomId(10)).toHaveLength(10);
|
||||||
|
|
||||||
|
// Test case 3: Length 15
|
||||||
|
expect(generateRandomId(15)).toHaveLength(15);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate a unique ID each time', () => {
|
||||||
|
// Generate 100 IDs and check if they are all unique
|
||||||
|
const ids = new Set<string>();
|
||||||
|
for (let i = 0; i < 100; i += 1) {
|
||||||
|
const id = generateRandomId();
|
||||||
|
expect(ids.has(id)).toBe(false);
|
||||||
|
ids.add(id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
39
src/shared/util/tests/string.test.ts
Normal file
39
src/shared/util/tests/string.test.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
|
import { capitalize } from '../string';
|
||||||
|
|
||||||
|
// TODO: Fix `string.ts` and `string.test.ts` to make the tests pass
|
||||||
|
// `capitalize` is adding an extra space at the end of the word.
|
||||||
|
describe('capitalize', () => {
|
||||||
|
it('should capitalize the first letter of each word', () => {
|
||||||
|
// Debug
|
||||||
|
const word = 'hello world';
|
||||||
|
const capitalized = capitalize(word);
|
||||||
|
console.log(capitalize(word));
|
||||||
|
console.log(capitalized.length);
|
||||||
|
console.log(capitalized.split(''));
|
||||||
|
|
||||||
|
// Test case 1: Single word
|
||||||
|
expect(capitalize('hello')).toBe('Hello');
|
||||||
|
|
||||||
|
// Test case 2: Multiple words
|
||||||
|
expect(capitalize('hello world')).toBe('Hello World');
|
||||||
|
|
||||||
|
// Test case 3: Words with hyphens
|
||||||
|
expect(capitalize('hello-world')).toBe('Hello-World');
|
||||||
|
|
||||||
|
// Test case 4: Words with hyphens and spaces
|
||||||
|
expect(capitalize('hello-world test')).toBe('Hello-World Test');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not change the capitalization of the remaining letters', () => {
|
||||||
|
// Test case 1: All lowercase
|
||||||
|
expect(capitalize('hello')).toBe('Hello');
|
||||||
|
|
||||||
|
// Test case 2: All uppercase
|
||||||
|
expect(capitalize('WORLD')).toBe('WORLD');
|
||||||
|
|
||||||
|
// Test case 3: Mixed case
|
||||||
|
expect(capitalize('HeLLo WoRLd')).toBe('Hello World');
|
||||||
|
});
|
||||||
|
});
|
||||||
14
src/shared/util/tests/time.test.ts
Normal file
14
src/shared/util/tests/time.test.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
|
import { sleep } from '../time';
|
||||||
|
|
||||||
|
describe('sleep', () => {
|
||||||
|
it('should resolve after the specified number of milliseconds', async () => {
|
||||||
|
const start = Date.now();
|
||||||
|
const milliseconds = 1000;
|
||||||
|
await sleep(milliseconds);
|
||||||
|
const end = Date.now();
|
||||||
|
const elapsed = end - start;
|
||||||
|
expect(elapsed).toBeGreaterThanOrEqual(milliseconds);
|
||||||
|
});
|
||||||
|
});
|
||||||
12
src/views/hooks/tests/useFlattenedCourseSchedule.test.ts
Normal file
12
src/views/hooks/tests/useFlattenedCourseSchedule.test.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
|
import { convertMinutesToIndex } from '../useFlattenedCourseSchedule';
|
||||||
|
|
||||||
|
describe('useFlattenedCourseSchedule', () => {
|
||||||
|
it('should convert minutes to index correctly', () => {
|
||||||
|
const minutes = 480; // 8:00 AM
|
||||||
|
const expectedIndex = 2; // (480 - 420) / 30 = 2
|
||||||
|
const result = convertMinutesToIndex(minutes);
|
||||||
|
expect(result).toBe(expectedIndex);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { CalendarCourseCellProps } from 'src/views/components/calendar/CalendarCourseCell/CalendarCourseCell';
|
import type { CalendarCourseCellProps } from 'src/views/components/calendar/CalendarCourseCell/CalendarCourseCell';
|
||||||
|
|
||||||
import useSchedules from './useSchedules';
|
import useSchedules from './useSchedules';
|
||||||
|
|
||||||
const dayToNumber: { [day: string]: number } = {
|
const dayToNumber: { [day: string]: number } = {
|
||||||
@@ -26,7 +27,7 @@ export interface CalendarGridCourse {
|
|||||||
totalColumns?: number;
|
totalColumns?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const convertMinutesToIndex = (minutes: number): number => Math.floor(minutes - 420 / 30);
|
export const convertMinutesToIndex = (minutes: number): number => Math.floor(minutes - 420 / 30);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the active schedule, and convert it to be render-able into a calendar.
|
* Get the active schedule, and convert it to be render-able into a calendar.
|
||||||
|
|||||||
9
vitest.config.ts
Normal file
9
vitest.config.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { defineConfig } from 'vitest/config';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
test: {
|
||||||
|
coverage: {
|
||||||
|
provider: 'v8',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user