diff --git a/.gitignore b/.gitignore index fd3dbb5..f1bc6b7 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,6 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts + +# coverage +.coverage \ No newline at end of file diff --git a/eslint.config.mjs b/eslint.config.mjs index 6597839..e74d731 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -24,5 +24,11 @@ export default [ rules: { "@typescript-eslint/consistent-type-assertions": "off" } + }, + { + files: ["test/**/*.spec.ts"], + rules: { + "max-nested-callbacks": "off" + } } ] \ No newline at end of file diff --git a/package.json b/package.json index e8f91f0..87091dd 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ "dev": "next dev", "build": "next build", "start": "next start", - "lint": "eslint src --max-warnings 0" + "lint": "eslint src test --max-warnings 0", + "test": "vitest run --coverage" }, "dependencies": { "@fortawesome/fontawesome-svg-core": "6.6.0", @@ -25,9 +26,12 @@ "@types/node": "22.8.4", "@types/react": "18.3.12", "@types/react-dom": "18.3.1", + "@vitest/coverage-istanbul": "2.1.4", "eslint": "9.13.0", + "jsdom": "25.0.1", "postcss": "8.4.47", "tailwindcss": "3.4.14", - "typescript": "5.6.3" + "typescript": "5.6.3", + "vitest": "2.1.4" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2ddfdab..7f1e355 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -22,10 +22,10 @@ importers: version: 0.2.2(@fortawesome/fontawesome-svg-core@6.6.0)(react@18.3.1) next: specifier: 15.0.2 - version: 15.0.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 15.0.2(@babel/core@7.26.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) next-plausible: specifier: 3.12.2 - version: 3.12.2(next@15.0.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 3.12.2(next@15.0.2(@babel/core@7.26.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: specifier: 18.3.1 version: 18.3.1 @@ -35,10 +35,16 @@ importers: devDependencies: '@nhcarrigan/eslint-config': specifier: 5.0.0-rc2 - version: 5.0.0-rc2(@typescript-eslint/utils@8.12.2(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.13.0(jiti@1.21.6))(playwright@1.48.2)(prettier@3.3.3)(react@18.3.1)(typescript@5.6.3)(vitest@2.1.4(@types/node@22.8.4)) + version: 5.0.0-rc2(@typescript-eslint/utils@8.12.2(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.13.0(jiti@1.21.6))(playwright@1.48.2)(prettier@3.3.3)(react@18.3.1)(typescript@5.6.3)(vitest@2.1.4(@types/node@22.8.4)(jsdom@25.0.1)) '@nhcarrigan/typescript-config': specifier: 4.0.0 version: 4.0.0(typescript@5.6.3) + '@testing-library/dom': + specifier: 10.4.0 + version: 10.4.0 + '@testing-library/react': + specifier: 16.0.1 + version: 16.0.1(@testing-library/dom@10.4.0)(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@types/node': specifier: 22.8.4 version: 22.8.4 @@ -48,9 +54,18 @@ importers: '@types/react-dom': specifier: 18.3.1 version: 18.3.1 + '@vitejs/plugin-react': + specifier: 4.3.3 + version: 4.3.3(vite@5.4.10(@types/node@22.8.4)) + '@vitest/coverage-istanbul': + specifier: 2.1.4 + version: 2.1.4(vitest@2.1.4(@types/node@22.8.4)(jsdom@25.0.1)) eslint: specifier: 9.13.0 version: 9.13.0(jiti@1.21.6) + jsdom: + specifier: 25.0.1 + version: 25.0.1 postcss: specifier: 8.4.47 version: 8.4.47 @@ -60,6 +75,9 @@ importers: typescript: specifier: 5.6.3 version: 5.6.3 + vitest: + specifier: 2.1.4 + version: 2.1.4(@types/node@22.8.4)(jsdom@25.0.1) packages: @@ -67,14 +85,93 @@ packages: resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + '@babel/code-frame@7.26.2': resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} engines: {node: '>=6.9.0'} + '@babel/compat-data@7.26.2': + resolution: {integrity: sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.26.0': + resolution: {integrity: sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.26.2': + resolution: {integrity: sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.25.9': + resolution: {integrity: sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.25.9': + resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.26.0': + resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-plugin-utils@7.25.9': + resolution: {integrity: sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.25.9': + resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.25.9': resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@7.25.9': + resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.26.0': + resolution: {integrity: sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.26.2': + resolution: {integrity: sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-transform-react-jsx-self@7.25.9': + resolution: {integrity: sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.25.9': + resolution: {integrity: sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/runtime@7.26.0': + resolution: {integrity: sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.25.9': + resolution: {integrity: sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.25.9': + resolution: {integrity: sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.26.0': + resolution: {integrity: sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==} + engines: {node: '>=6.9.0'} + '@emnapi/runtime@1.3.1': resolution: {integrity: sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==} @@ -409,6 +506,10 @@ packages: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} + '@istanbuljs/schema@0.1.3': + resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} + engines: {node: '>=8'} + '@jridgewell/gen-mapping@0.3.5': resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} engines: {node: '>=6.0.0'} @@ -619,6 +720,40 @@ packages: '@swc/helpers@0.5.13': resolution: {integrity: sha512-UoKGxQ3r5kYI9dALKJapMmuK+1zWM/H17Z1+iwnNmzcJRnfFuevZs375TA5rW31pu4BS4NoSy1fRsexDXfWn5w==} + '@testing-library/dom@10.4.0': + resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==} + engines: {node: '>=18'} + + '@testing-library/react@16.0.1': + resolution: {integrity: sha512-dSmwJVtJXmku+iocRhWOUFbrERC76TX2Mnf0ATODz8brzAZrMBbzLwQixlBSanZxR6LddK3eiwpSFZgDET1URg==} + engines: {node: '>=18'} + peerDependencies: + '@testing-library/dom': ^10.0.0 + '@types/react': ^18.0.0 + '@types/react-dom': ^18.0.0 + react: ^18.0.0 + react-dom: ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@types/aria-query@5.0.4': + resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.6.8': + resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.20.6': + resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} + '@types/estree@1.0.6': resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} @@ -754,6 +889,17 @@ packages: resolution: {integrity: sha512-b1tx0orFCCh/THWPQa2ZwWzvOeyzzp36vkJYOpVg0u8UVOIsfVrnuC9FqAw9gRKn+rG2VmWQ/zDJZzkxUnj/XQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@vitejs/plugin-react@4.3.3': + resolution: {integrity: sha512-NooDe9GpHGqNns1i8XDERg0Vsg5SSYRhRxxyTGogUdkdNt47jal+fbuYi+Yfq6pzRCKXyoPcWisfxE6RIM3GKA==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 + + '@vitest/coverage-istanbul@2.1.4': + resolution: {integrity: sha512-NLmfjzXnRSmLF/h4hYkzjvd7hZ85DRZzPUqXu0McPFCMczDfNmOjMoM3KaxjFaEmOc1YzX9HHbU/Rr9VO+35ow==} + peerDependencies: + vitest: 2.1.4 + '@vitest/eslint-plugin@1.1.4': resolution: {integrity: sha512-kudjgefmJJ7xQ2WfbUU6pZbm7Ou4gLYRaao/8Ynide3G0QhVKHd978sDyWX4KOH0CCMH9cyrGAkFd55eGzJ48Q==} peerDependencies: @@ -813,6 +959,10 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + agent-base@7.1.1: + resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==} + engines: {node: '>= 14'} + ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} @@ -828,6 +978,10 @@ packages: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + ansi-styles@6.2.1: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} engines: {node: '>=12'} @@ -849,6 +1003,9 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + array-buffer-byte-length@1.0.1: resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} engines: {node: '>= 0.4'} @@ -889,6 +1046,9 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + available-typed-arrays@1.0.7: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} @@ -983,6 +1143,10 @@ packages: resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} engines: {node: '>=12.5.0'} + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + commander@4.1.1: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} @@ -994,6 +1158,9 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + core-js-compat@3.38.1: resolution: {integrity: sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw==} @@ -1006,9 +1173,17 @@ packages: engines: {node: '>=4'} hasBin: true + cssstyle@4.1.0: + resolution: {integrity: sha512-h66W1URKpBS5YMI/V8PyXvTMFT8SupJ1IzoIV8IeBC/ji8WVmrO8dGlTi+2dh6whmdk6BiKJLD/ZBkhWbcg6nA==} + engines: {node: '>=18'} + csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + data-urls@5.0.0: + resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} + engines: {node: '>=18'} + data-view-buffer@1.0.1: resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} engines: {node: '>= 0.4'} @@ -1047,6 +1222,9 @@ packages: supports-color: optional: true + decimal.js@10.4.3: + resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} + deep-eql@5.0.2: resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} engines: {node: '>=6'} @@ -1062,6 +1240,14 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + detect-libc@2.0.3: resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} engines: {node: '>=8'} @@ -1080,6 +1266,9 @@ packages: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} + dom-accessibility-api@0.5.16: + resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} @@ -1092,6 +1281,10 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} @@ -1344,6 +1537,10 @@ packages: resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} engines: {node: '>=14'} + form-data@4.0.1: + resolution: {integrity: sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==} + engines: {node: '>= 6'} + fsevents@2.3.2: resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -1364,6 +1561,10 @@ packages: functions-have-names@1.2.3: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + get-intrinsic@1.2.4: resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} engines: {node: '>= 0.4'} @@ -1384,6 +1585,10 @@ packages: resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} hasBin: true + globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + globals@13.24.0: resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} engines: {node: '>=8'} @@ -1439,6 +1644,25 @@ packages: hosted-git-info@2.8.9: resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} + html-encoding-sniffer@4.0.0: + resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} + engines: {node: '>=18'} + + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + https-proxy-agent@7.0.5: + resolution: {integrity: sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==} + engines: {node: '>= 14'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -1539,6 +1763,9 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + is-regex@1.1.4: resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} engines: {node: '>= 0.4'} @@ -1580,6 +1807,26 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + istanbul-lib-instrument@6.0.3: + resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} + engines: {node: '>=10'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-lib-source-maps@5.0.6: + resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} + engines: {node: '>=10'} + + istanbul-reports@3.1.7: + resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} + engines: {node: '>=8'} + iterator.prototype@1.1.2: resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==} @@ -1601,6 +1848,15 @@ packages: resolution: {integrity: sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==} engines: {node: '>=12.0.0'} + jsdom@25.0.1: + resolution: {integrity: sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==} + engines: {node: '>=18'} + peerDependencies: + canvas: ^2.11.2 + peerDependenciesMeta: + canvas: + optional: true + jsesc@0.5.0: resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==} hasBin: true @@ -1626,6 +1882,11 @@ packages: resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} hasBin: true + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + jsx-ast-utils@3.3.5: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} @@ -1669,9 +1930,23 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + lz-string@1.5.0: + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + hasBin: true + magic-string@0.30.12: resolution: {integrity: sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==} + magicast@0.3.5: + resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} + + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -1680,6 +1955,14 @@ packages: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + min-indent@1.0.1: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} engines: {node: '>=4'} @@ -1753,6 +2036,9 @@ packages: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} + nwsapi@2.2.13: + resolution: {integrity: sha512-cTGB9ptp9dY9A5VbMSe7fQBcl/tt22Vcqdq8+eN93rblOuE0aCFu4aZ2vMwct/2t+lFnosm8RkQW1I0Omb1UtQ==} + object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -1828,6 +2114,9 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} + parse5@7.2.1: + resolution: {integrity: sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -1949,6 +2238,10 @@ packages: engines: {node: '>=14'} hasBin: true + pretty-format@27.5.1: + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} @@ -1967,6 +2260,13 @@ packages: react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + react-is@17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + + react-refresh@0.14.2: + resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} + engines: {node: '>=0.10.0'} + react@18.3.1: resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} engines: {node: '>=0.10.0'} @@ -1990,6 +2290,9 @@ packages: resolution: {integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==} engines: {node: '>= 0.4'} + regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + regexp-tree@0.1.27: resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==} hasBin: true @@ -2027,6 +2330,9 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + rrweb-cssom@0.7.1: + resolution: {integrity: sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==} + run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -2038,6 +2344,13 @@ packages: resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} engines: {node: '>= 0.4'} + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + scheduler@0.23.2: resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} @@ -2196,6 +2509,9 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + synckit@0.9.2: resolution: {integrity: sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==} engines: {node: ^14.18.0 || >=16.0.0} @@ -2205,6 +2521,10 @@ packages: engines: {node: '>=14.0.0'} hasBin: true + test-exclude@7.0.1: + resolution: {integrity: sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==} + engines: {node: '>=18'} + text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} @@ -2233,10 +2553,25 @@ packages: resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} engines: {node: '>=14.0.0'} + tldts-core@6.1.57: + resolution: {integrity: sha512-lXnRhuQpx3zU9EONF9F7HfcRLvN1uRYUBIiKL+C/gehC/77XTU+Jye6ui86GA3rU6FjlJ0triD1Tkjt2F/2lEg==} + + tldts@6.1.57: + resolution: {integrity: sha512-Oy7yDXK8meJl8vPMOldzA+MtueAJ5BrH4l4HXwZuj2AtfoQbLjmTJmjNWPUcAo+E/ibHn7QlqMS0BOcXJFJyHQ==} + hasBin: true + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + tough-cookie@5.0.0: + resolution: {integrity: sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==} + engines: {node: '>=16'} + + tr46@5.0.0: + resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==} + engines: {node: '>=18'} + ts-api-utils@1.3.0: resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} engines: {node: '>=16'} @@ -2371,6 +2706,26 @@ packages: jsdom: optional: true + w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} + + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + + whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + + whatwg-url@14.0.0: + resolution: {integrity: sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==} + engines: {node: '>=18'} + which-boxed-primitive@1.0.2: resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} @@ -2408,6 +2763,28 @@ packages: resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} engines: {node: '>=12'} + ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xml-name-validator@5.0.0: + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} + + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yaml@2.5.0: resolution: {integrity: sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw==} engines: {node: '>= 14'} @@ -2421,14 +2798,125 @@ snapshots: '@alloc/quick-lru@5.2.0': {} + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + '@babel/code-frame@7.26.2': dependencies: '@babel/helper-validator-identifier': 7.25.9 js-tokens: 4.0.0 picocolors: 1.1.0 + '@babel/compat-data@7.26.2': {} + + '@babel/core@7.26.0': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.26.2 + '@babel/generator': 7.26.2 + '@babel/helper-compilation-targets': 7.25.9 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) + '@babel/helpers': 7.26.0 + '@babel/parser': 7.26.2 + '@babel/template': 7.25.9 + '@babel/traverse': 7.25.9 + '@babel/types': 7.26.0 + convert-source-map: 2.0.0 + debug: 4.3.7 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.26.2': + dependencies: + '@babel/parser': 7.26.2 + '@babel/types': 7.26.0 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 3.0.2 + + '@babel/helper-compilation-targets@7.25.9': + dependencies: + '@babel/compat-data': 7.26.2 + '@babel/helper-validator-option': 7.25.9 + browserslist: 4.24.2 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-module-imports@7.25.9': + dependencies: + '@babel/traverse': 7.25.9 + '@babel/types': 7.26.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-module-imports': 7.25.9 + '@babel/helper-validator-identifier': 7.25.9 + '@babel/traverse': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/helper-plugin-utils@7.25.9': {} + + '@babel/helper-string-parser@7.25.9': {} + '@babel/helper-validator-identifier@7.25.9': {} + '@babel/helper-validator-option@7.25.9': {} + + '@babel/helpers@7.26.0': + dependencies: + '@babel/template': 7.25.9 + '@babel/types': 7.26.0 + + '@babel/parser@7.26.2': + dependencies: + '@babel/types': 7.26.0 + + '@babel/plugin-transform-react-jsx-self@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-react-jsx-source@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/runtime@7.26.0': + dependencies: + regenerator-runtime: 0.14.1 + + '@babel/template@7.25.9': + dependencies: + '@babel/code-frame': 7.26.2 + '@babel/parser': 7.26.2 + '@babel/types': 7.26.0 + + '@babel/traverse@7.25.9': + dependencies: + '@babel/code-frame': 7.26.2 + '@babel/generator': 7.26.2 + '@babel/parser': 7.26.2 + '@babel/template': 7.25.9 + '@babel/types': 7.26.0 + debug: 4.3.7 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.26.0': + dependencies: + '@babel/helper-string-parser': 7.25.9 + '@babel/helper-validator-identifier': 7.25.9 + '@emnapi/runtime@1.3.1': dependencies: tslib: 2.7.0 @@ -2667,6 +3155,8 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 + '@istanbuljs/schema@0.1.3': {} + '@jridgewell/gen-mapping@0.3.5': dependencies: '@jridgewell/set-array': 1.2.1 @@ -2710,7 +3200,7 @@ snapshots: '@next/swc-win32-x64-msvc@15.0.2': optional: true - '@nhcarrigan/eslint-config@5.0.0-rc2(@typescript-eslint/utils@8.12.2(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.13.0(jiti@1.21.6))(playwright@1.48.2)(prettier@3.3.3)(react@18.3.1)(typescript@5.6.3)(vitest@2.1.4(@types/node@22.8.4))': + '@nhcarrigan/eslint-config@5.0.0-rc2(@typescript-eslint/utils@8.12.2(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.13.0(jiti@1.21.6))(playwright@1.48.2)(prettier@3.3.3)(react@18.3.1)(typescript@5.6.3)(vitest@2.1.4(@types/node@22.8.4)(jsdom@25.0.1))': dependencies: '@eslint/compat': 1.1.1 '@eslint/eslintrc': 3.1.0 @@ -2718,7 +3208,7 @@ snapshots: '@stylistic/eslint-plugin': 2.8.0(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3) '@typescript-eslint/eslint-plugin': 8.7.0(@typescript-eslint/parser@8.7.0(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3) '@typescript-eslint/parser': 8.7.0(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3) - '@vitest/eslint-plugin': 1.1.4(@typescript-eslint/utils@8.12.2(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3)(vitest@2.1.4(@types/node@22.8.4)) + '@vitest/eslint-plugin': 1.1.4(@typescript-eslint/utils@8.12.2(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3)(vitest@2.1.4(@types/node@22.8.4)(jsdom@25.0.1)) eslint: 9.13.0(jiti@1.21.6) eslint-config-prettier: 9.1.0(eslint@9.13.0(jiti@1.21.6)) eslint-plugin-deprecation: 3.0.0(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3) @@ -2733,7 +3223,7 @@ snapshots: playwright: 1.48.2 react: 18.3.1 typescript: 5.6.3 - vitest: 2.1.4(@types/node@22.8.4) + vitest: 2.1.4(@types/node@22.8.4)(jsdom@25.0.1) transitivePeerDependencies: - '@types/eslint' - '@typescript-eslint/utils' @@ -2838,6 +3328,50 @@ snapshots: dependencies: tslib: 2.7.0 + '@testing-library/dom@10.4.0': + dependencies: + '@babel/code-frame': 7.26.2 + '@babel/runtime': 7.26.0 + '@types/aria-query': 5.0.4 + aria-query: 5.3.0 + chalk: 4.1.2 + dom-accessibility-api: 0.5.16 + lz-string: 1.5.0 + pretty-format: 27.5.1 + + '@testing-library/react@16.0.1(@testing-library/dom@10.4.0)(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.26.0 + '@testing-library/dom': 10.4.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.12 + '@types/react-dom': 18.3.1 + + '@types/aria-query@5.0.4': {} + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.26.2 + '@babel/types': 7.26.0 + '@types/babel__generator': 7.6.8 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.20.6 + + '@types/babel__generator@7.6.8': + dependencies: + '@babel/types': 7.26.0 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.26.2 + '@babel/types': 7.26.0 + + '@types/babel__traverse@7.20.6': + dependencies: + '@babel/types': 7.26.0 + '@types/estree@1.0.6': {} '@types/json-schema@7.0.15': {} @@ -3018,13 +3552,40 @@ snapshots: '@typescript-eslint/types': 8.7.0 eslint-visitor-keys: 3.4.3 - '@vitest/eslint-plugin@1.1.4(@typescript-eslint/utils@8.12.2(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3)(vitest@2.1.4(@types/node@22.8.4))': + '@vitejs/plugin-react@4.3.3(vite@5.4.10(@types/node@22.8.4))': + dependencies: + '@babel/core': 7.26.0 + '@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.0) + '@types/babel__core': 7.20.5 + react-refresh: 0.14.2 + vite: 5.4.10(@types/node@22.8.4) + transitivePeerDependencies: + - supports-color + + '@vitest/coverage-istanbul@2.1.4(vitest@2.1.4(@types/node@22.8.4)(jsdom@25.0.1))': + dependencies: + '@istanbuljs/schema': 0.1.3 + debug: 4.3.7 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-instrument: 6.0.3 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 5.0.6 + istanbul-reports: 3.1.7 + magicast: 0.3.5 + test-exclude: 7.0.1 + tinyrainbow: 1.2.0 + vitest: 2.1.4(@types/node@22.8.4)(jsdom@25.0.1) + transitivePeerDependencies: + - supports-color + + '@vitest/eslint-plugin@1.1.4(@typescript-eslint/utils@8.12.2(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3)(vitest@2.1.4(@types/node@22.8.4)(jsdom@25.0.1))': dependencies: eslint: 9.13.0(jiti@1.21.6) optionalDependencies: '@typescript-eslint/utils': 8.12.2(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3) typescript: 5.6.3 - vitest: 2.1.4(@types/node@22.8.4) + vitest: 2.1.4(@types/node@22.8.4)(jsdom@25.0.1) '@vitest/expect@2.1.4': dependencies: @@ -3078,6 +3639,12 @@ snapshots: acorn@8.14.0: {} + agent-base@7.1.1: + dependencies: + debug: 4.3.7 + transitivePeerDependencies: + - supports-color + ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 @@ -3093,6 +3660,8 @@ snapshots: dependencies: color-convert: 2.0.1 + ansi-styles@5.2.0: {} + ansi-styles@6.2.1: {} any-promise@1.3.0: {} @@ -3108,6 +3677,10 @@ snapshots: argparse@2.0.1: {} + aria-query@5.3.0: + dependencies: + dequal: 2.0.3 + array-buffer-byte-length@1.0.1: dependencies: call-bind: 1.0.7 @@ -3177,6 +3750,8 @@ snapshots: assertion-error@2.0.1: {} + asynckit@0.4.0: {} + available-typed-arrays@1.0.7: dependencies: possible-typed-array-names: 1.0.0 @@ -3280,12 +3855,18 @@ snapshots: color-string: 1.9.1 optional: true + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + commander@4.1.1: {} comment-parser@1.4.1: {} concat-map@0.0.1: {} + convert-source-map@2.0.0: {} + core-js-compat@3.38.1: dependencies: browserslist: 4.24.2 @@ -3298,8 +3879,17 @@ snapshots: cssesc@3.0.0: {} + cssstyle@4.1.0: + dependencies: + rrweb-cssom: 0.7.1 + csstype@3.1.3: {} + data-urls@5.0.0: + dependencies: + whatwg-mimetype: 4.0.0 + whatwg-url: 14.0.0 + data-view-buffer@1.0.1: dependencies: call-bind: 1.0.7 @@ -3330,6 +3920,8 @@ snapshots: dependencies: ms: 2.1.3 + decimal.js@10.4.3: {} + deep-eql@5.0.2: {} deep-is@0.1.4: {} @@ -3346,6 +3938,10 @@ snapshots: has-property-descriptors: 1.0.2 object-keys: 1.1.1 + delayed-stream@1.0.0: {} + + dequal@2.0.3: {} + detect-libc@2.0.3: optional: true @@ -3361,6 +3957,8 @@ snapshots: dependencies: esutils: 2.0.3 + dom-accessibility-api@0.5.16: {} + eastasianwidth@0.2.0: {} electron-to-chromium@1.5.49: {} @@ -3369,6 +3967,8 @@ snapshots: emoji-regex@9.2.2: {} + entities@4.5.0: {} + error-ex@1.3.2: dependencies: is-arrayish: 0.2.1 @@ -3776,6 +4376,12 @@ snapshots: cross-spawn: 7.0.3 signal-exit: 4.1.0 + form-data@4.0.1: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + fsevents@2.3.2: optional: true @@ -3793,6 +4399,8 @@ snapshots: functions-have-names@1.2.3: {} + gensync@1.0.0-beta.2: {} + get-intrinsic@1.2.4: dependencies: es-errors: 1.3.0 @@ -3824,6 +4432,8 @@ snapshots: package-json-from-dist: 1.0.0 path-scurry: 1.11.1 + globals@11.12.0: {} + globals@13.24.0: dependencies: type-fest: 0.20.2 @@ -3874,6 +4484,30 @@ snapshots: hosted-git-info@2.8.9: {} + html-encoding-sniffer@4.0.0: + dependencies: + whatwg-encoding: 3.1.1 + + html-escaper@2.0.2: {} + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.1 + debug: 4.3.7 + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.5: + dependencies: + agent-base: 7.1.1 + debug: 4.3.7 + transitivePeerDependencies: + - supports-color + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + ignore@5.3.2: {} import-fresh@3.3.0: @@ -3962,6 +4596,8 @@ snapshots: is-number@7.0.0: {} + is-potential-custom-element-name@1.0.1: {} + is-regex@1.1.4: dependencies: call-bind: 1.0.7 @@ -4000,6 +4636,37 @@ snapshots: isexe@2.0.0: {} + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-instrument@6.0.3: + dependencies: + '@babel/core': 7.26.0 + '@babel/parser': 7.26.2 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 7.6.3 + transitivePeerDependencies: + - supports-color + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-lib-source-maps@5.0.6: + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + debug: 4.3.7 + istanbul-lib-coverage: 3.2.2 + transitivePeerDependencies: + - supports-color + + istanbul-reports@3.1.7: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + iterator.prototype@1.1.2: dependencies: define-properties: 1.2.1 @@ -4024,6 +4691,34 @@ snapshots: jsdoc-type-pratt-parser@4.1.0: {} + jsdom@25.0.1: + dependencies: + cssstyle: 4.1.0 + data-urls: 5.0.0 + decimal.js: 10.4.3 + form-data: 4.0.1 + html-encoding-sniffer: 4.0.0 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.5 + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.13 + parse5: 7.2.1 + rrweb-cssom: 0.7.1 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 5.0.0 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 3.1.1 + whatwg-mimetype: 4.0.0 + whatwg-url: 14.0.0 + ws: 8.18.0 + xml-name-validator: 5.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + jsesc@0.5.0: {} jsesc@3.0.2: {} @@ -4040,6 +4735,8 @@ snapshots: dependencies: minimist: 1.2.8 + json5@2.2.3: {} + jsx-ast-utils@3.3.5: dependencies: array-includes: 3.1.8 @@ -4080,10 +4777,26 @@ snapshots: lru-cache@10.4.3: {} + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + lz-string@1.5.0: {} + magic-string@0.30.12: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 + magicast@0.3.5: + dependencies: + '@babel/parser': 7.26.2 + '@babel/types': 7.26.0 + source-map-js: 1.2.1 + + make-dir@4.0.0: + dependencies: + semver: 7.6.3 + merge2@1.4.1: {} micromatch@4.0.8: @@ -4091,6 +4804,12 @@ snapshots: braces: 3.0.3 picomatch: 2.3.1 + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + min-indent@1.0.1: {} minimatch@3.1.2: @@ -4119,13 +4838,13 @@ snapshots: natural-compare@1.4.0: {} - next-plausible@3.12.2(next@15.0.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + next-plausible@3.12.2(next@15.0.2(@babel/core@7.26.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - next: 15.0.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next: 15.0.2(@babel/core@7.26.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - next@15.0.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + next@15.0.2(@babel/core@7.26.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@next/env': 15.0.2 '@swc/counter': 0.1.3 @@ -4135,7 +4854,7 @@ snapshots: postcss: 8.4.31 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - styled-jsx: 5.1.6(react@18.3.1) + styled-jsx: 5.1.6(@babel/core@7.26.0)(react@18.3.1) optionalDependencies: '@next/swc-darwin-arm64': 15.0.2 '@next/swc-darwin-x64': 15.0.2 @@ -4161,6 +4880,8 @@ snapshots: normalize-path@3.0.0: {} + nwsapi@2.2.13: {} + object-assign@4.1.1: {} object-hash@3.0.0: {} @@ -4246,6 +4967,10 @@ snapshots: json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 + parse5@7.2.1: + dependencies: + entities: 4.5.0 + path-exists@4.0.0: {} path-key@3.1.1: {} @@ -4336,6 +5061,12 @@ snapshots: prettier@3.3.3: {} + pretty-format@27.5.1: + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + prop-types@15.8.1: dependencies: loose-envify: 1.4.0 @@ -4354,6 +5085,10 @@ snapshots: react-is@16.13.1: {} + react-is@17.0.2: {} + + react-refresh@0.14.2: {} + react@18.3.1: dependencies: loose-envify: 1.4.0 @@ -4389,6 +5124,8 @@ snapshots: globalthis: 1.0.4 which-builtin-type: 1.1.4 + regenerator-runtime@0.14.1: {} + regexp-tree@0.1.27: {} regexp.prototype.flags@1.5.2: @@ -4444,6 +5181,8 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.24.3 fsevents: 2.3.3 + rrweb-cssom@0.7.1: {} + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 @@ -4461,6 +5200,12 @@ snapshots: es-errors: 1.3.0 is-regex: 1.1.4 + safer-buffer@2.1.2: {} + + saxes@6.0.0: + dependencies: + xmlchars: 2.2.0 + scheduler@0.23.2: dependencies: loose-envify: 1.4.0 @@ -4634,10 +5379,12 @@ snapshots: strip-json-comments@3.1.1: {} - styled-jsx@5.1.6(react@18.3.1): + styled-jsx@5.1.6(@babel/core@7.26.0)(react@18.3.1): dependencies: client-only: 0.0.1 react: 18.3.1 + optionalDependencies: + '@babel/core': 7.26.0 sucrase@3.35.0: dependencies: @@ -4655,6 +5402,8 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + symbol-tree@3.2.4: {} + synckit@0.9.2: dependencies: '@pkgr/core': 0.1.1 @@ -4687,6 +5436,12 @@ snapshots: transitivePeerDependencies: - ts-node + test-exclude@7.0.1: + dependencies: + '@istanbuljs/schema': 0.1.3 + glob: 10.4.5 + minimatch: 9.0.5 + text-table@0.2.0: {} thenify-all@1.6.0: @@ -4707,10 +5462,24 @@ snapshots: tinyspy@3.0.2: {} + tldts-core@6.1.57: {} + + tldts@6.1.57: + dependencies: + tldts-core: 6.1.57 + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 + tough-cookie@5.0.0: + dependencies: + tldts: 6.1.57 + + tr46@5.0.0: + dependencies: + punycode: 2.3.1 + ts-api-utils@1.3.0(typescript@5.6.3): dependencies: typescript: 5.6.3 @@ -4822,7 +5591,7 @@ snapshots: '@types/node': 22.8.4 fsevents: 2.3.3 - vitest@2.1.4(@types/node@22.8.4): + vitest@2.1.4(@types/node@22.8.4)(jsdom@25.0.1): dependencies: '@vitest/expect': 2.1.4 '@vitest/mocker': 2.1.4(vite@5.4.10(@types/node@22.8.4)) @@ -4846,6 +5615,7 @@ snapshots: why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.8.4 + jsdom: 25.0.1 transitivePeerDependencies: - less - lightningcss @@ -4857,6 +5627,23 @@ snapshots: - supports-color - terser + w3c-xmlserializer@5.0.0: + dependencies: + xml-name-validator: 5.0.0 + + webidl-conversions@7.0.0: {} + + whatwg-encoding@3.1.1: + dependencies: + iconv-lite: 0.6.3 + + whatwg-mimetype@4.0.0: {} + + whatwg-url@14.0.0: + dependencies: + tr46: 5.0.0 + webidl-conversions: 7.0.0 + which-boxed-primitive@1.0.2: dependencies: is-bigint: 1.0.4 @@ -4918,6 +5705,14 @@ snapshots: string-width: 5.1.2 strip-ansi: 7.1.0 + ws@8.18.0: {} + + xml-name-validator@5.0.0: {} + + xmlchars@2.2.0: {} + + yallist@3.1.1: {} + yaml@2.5.0: {} yocto-queue@0.1.0: {} diff --git a/src/app/about/page.tsx b/src/app/about/page.tsx index 92e2635..bed2512 100644 --- a/src/app/about/page.tsx +++ b/src/app/about/page.tsx @@ -57,7 +57,8 @@ const About = (): JSX.Element => { satisfaction, with an emphasis on making resources accessible to newcomers in the field.`} -
  • +
  • {`Mentorship and Advocacy:`} {` Implemented programs to support aspiring developers, particularly those from diff --git a/src/config/Jobs.ts b/src/config/Jobs.ts index c6c340e..b107807 100644 --- a/src/config/Jobs.ts +++ b/src/config/Jobs.ts @@ -118,7 +118,7 @@ Additionally, I guided and mentored other developers in the design and coding of
  • `, - end: new Date("June 4 2024"), + end: new Date("June 5 2024"), link: "https://deepgram.com", logo: "deepgram.jpeg", start: new Date("July 5 2023"), diff --git a/src/config/NavItems.ts b/src/config/NavItems.ts index 5f25551..16eb52e 100644 --- a/src/config/NavItems.ts +++ b/src/config/NavItems.ts @@ -20,7 +20,6 @@ export const NavItems = [ { href: "/polycule", text: "Polycule" }, { href: "/activity", text: "Activity" }, { href: "/art", text: "Art" }, - { href: "https://nhcarrigan.creator-spring.com/", text: "Merch" }, { href: "/manifesto", text: "Transfemme Manifesto" }, ].sort((a, b) => { return a.text.localeCompare(b.text); diff --git a/src/config/Socials.ts b/src/config/Socials.ts index d9627c6..fa98480 100644 --- a/src/config/Socials.ts +++ b/src/config/Socials.ts @@ -42,12 +42,11 @@ import { faComputer, faEnvelope, faGamepad, - faGift, faHashtag, faMoneyBill, - faUniversity, } from "@fortawesome/free-solid-svg-icons"; import { Codeberg } from "../icons/Codeberg"; +import { Coursera } from "../icons/Coursera"; import { Fiverr } from "../icons/Fiverr"; import { Gather } from "../icons/Gather"; import { Gog } from "../icons/Gog"; @@ -58,7 +57,9 @@ import { Pixiv } from "../icons/Pixiv"; import { Polywork } from "../icons/Polywork"; import { Saylor } from "../icons/Saylor"; import { TeeSpring } from "../icons/TeeSpring"; +import { Throne } from "../icons/Throne"; import { TreeNation } from "../icons/TreeNation"; +import { Udemy } from "../icons/Udemy"; import { VRoid } from "../icons/VRoid"; import type { IconDefinition } from "@fortawesome/fontawesome-svg-core"; @@ -85,7 +86,7 @@ const HireMe: { color: "#003600", icon: faBriefcase, label: "Hire Us!", - link: "https://docs.nhcarrigan.com/#/hire", + link: "https://docs.nhcarrigan.com/about/hire/", }; /** @@ -117,7 +118,7 @@ const Donate: { color: "#003600", icon: faMoneyBill, label: "Donate 💜", - link: "https://docs.nhcarrigan.com/#/donate", + link: "https://docs.nhcarrigan.com/about/donate/", }; /** @@ -164,7 +165,7 @@ const Socials: Array<{ link: "https://matrix.to/#/#naomi:matrix.org", }, { - alt: "Hash symbol", + alt: "Octothorpe", background: "#000", color: "#FFF", icon: faHashtag, @@ -380,18 +381,18 @@ const Socials: Array<{ link: "https://docs.nhcarrigan.com/about/contact/#7-email-communication", }, { - alt: "University Icon", + alt: "Coursera Logo", background: "#0056D2", color: "#FFF", - icon: faUniversity, + icon: Coursera, label: "Coursera", link: "https://www.coursera.org/learner/naomi-lgbt", }, { - alt: "University Icon", + alt: "Udemy Logo", background: "#EC5252", color: "#FFF", - icon: faUniversity, + icon: Udemy, label: "Udemy", link: "https://www.udemy.com/user/naomi-carrigan/", }, @@ -412,10 +413,10 @@ const Socials: Array<{ link: "https://pcpartpicker.com/user/nhcarrigan/", }, { - alt: "Gift Icon", + alt: "Throne.me Logo", background: "#000", color: "#FFF", - icon: faGift, + icon: Throne, label: "Throne", link: "https://throne.com/naomilgbt", }, diff --git a/src/icons/Coursera.ts b/src/icons/Coursera.ts new file mode 100644 index 0000000..1108de1 --- /dev/null +++ b/src/icons/Coursera.ts @@ -0,0 +1,126 @@ +/** + * @copyright nhcarrigan + * @license Naomi's Public License + * @author Naomi Carrigan + */ +import type { IconDefinition } from "@fortawesome/fontawesome-svg-core"; + +/** + * Custom FontAwesome icon definition for the + * Coursera logo. + */ +export const Coursera: IconDefinition = { + icon: [ + 825, + 825, + [], + "U+E002", + `M 397.00,0.23 + C 397.00,0.23 421.00,0.23 421.00,0.23 + 421.00,0.23 436.00,1.92 436.00,1.92 + 436.00,1.92 445.00,1.92 445.00,1.92 + 464.83,2.24 500.36,9.70 520.00,14.91 + 564.71,26.78 604.32,45.18 642.72,70.91 + 642.72,70.91 666.54,87.59 666.54,87.59 + 679.69,97.87 691.91,109.49 703.71,121.29 + 750.05,167.63 785.48,224.18 804.76,287.00 + 804.76,287.00 816.55,331.00 816.55,331.00 + 816.55,331.00 818.37,344.00 818.37,344.00 + 820.23,353.40 822.89,368.59 823.09,378.00 + 823.09,378.00 823.09,390.00 823.09,390.00 + 823.81,395.27 824.91,394.72 825.00,402.00 + 825.00,402.00 825.00,419.00 825.00,419.00 + 824.99,427.76 823.88,428.69 823.08,436.00 + 823.08,436.00 823.08,446.00 823.08,446.00 + 822.88,455.97 820.17,471.99 818.31,482.00 + 818.31,482.00 816.76,493.00 816.76,493.00 + 813.29,511.46 806.77,533.13 800.72,551.00 + 800.72,551.00 789.17,579.17 789.17,579.17 + 743.04,685.52 649.01,770.68 538.00,804.76 + 514.40,812.01 493.53,817.23 469.00,820.29 + 469.00,820.29 445.00,823.09 445.00,823.09 + 445.00,823.09 435.00,823.09 435.00,823.09 + 427.99,823.92 429.93,824.98 420.00,825.00 + 420.00,825.00 405.00,825.00 405.00,825.00 + 405.00,825.00 389.00,823.08 389.00,823.08 + 389.00,823.08 379.00,823.08 379.00,823.08 + 368.84,822.88 353.18,820.20 343.00,818.31 + 343.00,818.31 332.00,816.76 332.00,816.76 + 308.66,812.37 277.98,802.81 256.00,793.71 + 211.64,775.33 173.47,751.31 137.46,719.58 + 137.46,719.58 121.29,703.71 121.29,703.71 + 74.93,657.35 39.53,600.84 20.24,538.00 + 20.24,538.00 8.24,493.00 8.24,493.00 + 8.24,493.00 6.81,482.00 6.81,482.00 + 6.81,482.00 5.21,475.00 5.21,475.00 + 3.59,463.77 2.02,452.36 2.00,441.00 + 2.00,441.00 0.00,421.00 0.00,421.00 + 0.00,421.00 0.00,405.00 0.00,405.00 + 0.00,405.00 1.92,389.00 1.92,389.00 + 1.92,389.00 1.92,380.00 1.92,380.00 + 2.15,367.31 4.82,354.46 6.86,342.00 + 12.62,306.62 23.37,271.55 38.55,239.00 + 85.92,137.42 175.10,56.39 282.00,21.68 + 301.73,15.28 321.55,10.19 342.00,6.86 + 342.00,6.86 350.00,5.18 350.00,5.18 + 350.00,5.18 378.00,2.00 378.00,2.00 + 387.62,1.89 387.16,2.56 397.00,0.23 Z + M 654.00,274.00 + C 652.59,267.31 647.58,262.01 643.51,256.72 + 635.54,246.34 626.80,236.89 617.43,227.75 + 582.03,193.25 534.25,169.22 486.00,159.22 + 486.00,159.22 475.00,157.75 475.00,157.75 + 475.00,157.75 469.00,156.26 469.00,156.26 + 469.00,156.26 443.00,154.00 443.00,154.00 + 443.00,154.00 419.00,154.00 419.00,154.00 + 419.00,154.00 393.00,156.26 393.00,156.26 + 393.00,156.26 387.00,157.75 387.00,157.75 + 387.00,157.75 376.00,159.22 376.00,159.22 + 359.46,162.64 337.52,169.42 322.09,176.22 + 310.29,181.41 299.65,187.64 288.72,194.38 + 230.27,230.42 189.21,287.07 173.67,354.00 + 173.67,354.00 169.00,376.00 169.00,376.00 + 169.00,376.00 169.00,384.00 169.00,384.00 + 168.19,389.27 167.09,388.72 167.00,396.00 + 167.00,396.00 167.00,427.00 167.00,427.00 + 167.02,437.40 168.03,434.44 169.06,441.00 + 169.06,441.00 169.06,450.00 169.06,450.00 + 171.22,463.47 175.55,480.94 179.94,493.83 + 208.58,578.07 278.08,641.10 364.00,663.24 + 375.88,666.30 405.06,670.98 417.00,671.00 + 417.00,671.00 445.00,671.00 445.00,671.00 + 445.00,671.00 469.00,668.63 469.00,668.63 + 469.00,668.63 475.00,667.19 475.00,667.19 + 475.00,667.19 486.00,665.76 486.00,665.76 + 486.00,665.76 503.00,661.27 503.00,661.27 + 547.18,648.72 587.85,626.54 620.12,593.45 + 631.39,581.89 638.18,574.40 647.68,561.15 + 651.38,556.00 655.09,552.51 656.00,546.00 + 656.00,546.00 622.00,526.15 622.00,526.15 + 622.00,526.15 574.08,498.06 574.08,498.06 + 574.08,498.06 546.00,482.00 546.00,482.00 + 534.75,499.99 523.33,511.09 506.00,523.14 + 494.21,531.34 480.79,536.77 467.09,540.79 + 458.67,543.26 443.67,545.99 435.00,546.00 + 424.27,546.02 411.46,544.96 401.00,542.55 + 357.16,532.46 320.34,499.15 303.73,457.72 + 300.20,448.91 295.05,428.27 295.00,419.00 + 295.00,419.00 295.00,405.00 295.00,405.00 + 295.08,398.63 295.76,399.32 296.68,394.00 + 298.79,381.82 299.55,375.89 304.46,364.00 + 311.29,347.50 322.76,330.34 335.46,317.80 + 352.35,301.13 374.31,288.72 397.17,282.45 + 397.17,282.45 406.00,280.91 406.00,280.91 + 413.70,279.46 411.09,279.01 420.00,279.00 + 420.00,279.00 442.00,279.00 442.00,279.00 + 461.77,279.03 484.13,288.32 501.09,297.97 + 508.30,302.07 525.10,315.12 530.25,321.17 + 532.57,323.88 541.23,335.82 544.15,336.27 + 546.02,336.55 550.51,333.55 552.25,332.55 + 552.25,332.55 574.00,319.99 574.00,319.99 + 574.00,319.99 631.99,286.46 631.99,286.46 + 631.99,286.46 654.00,274.00 654.00,274.00 Z`, + ], + iconName: "yyy", + prefix: "xxx", +} as never; diff --git a/src/icons/Throne.ts b/src/icons/Throne.ts new file mode 100644 index 0000000..85f9b65 --- /dev/null +++ b/src/icons/Throne.ts @@ -0,0 +1,93 @@ +/** + * @copyright nhcarrigan + * @license Naomi's Public License + * @author Naomi Carrigan + */ +import type { IconDefinition } from "@fortawesome/fontawesome-svg-core"; + +/** + * Custom FontAwesome icon definition for the + * Throne.me logo. + */ +export const Throne: IconDefinition = { + icon: [ + 738, + 621, + [], + "U+E002", + `M 364.00,0.65 + C 380.58,-2.77 384.82,13.12 390.75,25.00 + 390.75,25.00 436.25,114.00 436.25,114.00 + 436.25,114.00 501.25,242.00 501.25,242.00 + 509.55,258.59 526.58,284.93 526.99,303.00 + 527.58,329.12 511.89,341.11 495.00,358.00 + 495.00,358.00 471.96,382.00 471.96,382.00 + 471.96,382.00 440.00,414.00 440.00,414.00 + 440.00,414.00 418.96,436.00 418.96,436.00 + 418.96,436.00 390.00,465.00 390.00,465.00 + 383.82,471.18 376.58,481.87 367.00,480.80 + 359.95,480.01 351.03,469.03 346.00,464.00 + 346.00,464.00 300.00,417.00 300.00,417.00 + 300.00,417.00 239.00,355.00 239.00,355.00 + 224.23,340.23 209.04,325.70 210.51,303.00 + 210.78,298.85 211.58,292.96 212.77,289.00 + 212.77,289.00 235.75,242.00 235.75,242.00 + 235.75,242.00 298.75,119.00 298.75,119.00 + 298.75,119.00 320.74,76.00 320.74,76.00 + 320.74,76.00 343.69,31.00 343.69,31.00 + 346.88,25.11 354.43,7.97 358.21,4.21 + 360.24,2.20 361.48,1.71 364.00,0.65 Z + M 11.00,204.59 + C 13.62,204.13 15.36,203.84 18.00,204.59 + 23.76,206.43 34.33,218.33 39.00,223.00 + 39.00,223.00 102.00,287.00 102.00,287.00 + 102.00,287.00 131.96,317.00 131.96,317.00 + 131.96,317.00 141.01,327.00 141.01,327.00 + 141.01,327.00 176.96,363.00 176.96,363.00 + 176.96,363.00 186.01,373.00 186.01,373.00 + 186.01,373.00 235.96,423.00 235.96,423.00 + 235.96,423.00 316.00,505.00 316.00,505.00 + 316.00,505.00 361.00,551.00 361.00,551.00 + 366.28,556.28 376.33,564.19 376.52,572.00 + 376.65,577.57 372.41,583.22 367.00,584.58 + 367.00,584.58 360.00,585.17 360.00,585.17 + 360.00,585.17 345.00,586.00 345.00,586.00 + 345.00,586.00 334.00,586.96 334.00,586.96 + 299.43,588.47 264.60,593.98 231.00,602.37 + 212.72,606.95 183.56,617.64 167.00,618.82 + 163.80,619.04 161.15,618.37 158.00,618.07 + 135.75,616.01 118.33,604.57 107.44,585.00 + 102.99,577.01 98.68,561.24 96.15,552.00 + 96.15,552.00 64.42,443.00 64.42,443.00 + 64.42,443.00 26.02,311.00 26.02,311.00 + 26.02,311.00 3.42,233.00 3.42,233.00 + 0.08,221.29 -4.79,209.26 11.00,204.59 Z + M 720.00,204.61 + C 730.62,202.68 739.38,209.98 737.53,221.00 + 737.53,221.00 729.71,249.00 729.71,249.00 + 729.71,249.00 714.58,301.00 714.58,301.00 + 714.58,301.00 678.42,425.00 678.42,425.00 + 678.42,425.00 646.72,534.00 646.72,534.00 + 638.26,562.21 633.90,594.97 606.00,611.13 + 601.21,613.90 595.29,616.69 590.00,618.28 + 569.52,624.42 556.97,616.60 538.00,611.14 + 538.00,611.14 500.00,600.87 500.00,600.87 + 491.51,598.75 482.71,597.52 475.00,593.12 + 465.90,587.93 450.35,570.35 442.00,562.00 + 442.00,562.00 416.72,536.00 416.72,536.00 + 412.08,530.66 408.35,526.63 410.43,519.00 + 411.45,515.25 414.38,512.76 416.92,510.00 + 416.92,510.00 432.00,494.00 432.00,494.00 + 432.00,494.00 465.96,460.01 465.96,460.01 + 465.96,460.01 483.00,442.00 483.00,442.00 + 483.00,442.00 509.96,415.00 509.96,415.00 + 509.96,415.00 519.01,405.00 519.01,405.00 + 519.01,405.00 552.96,371.01 552.96,371.01 + 552.96,371.01 651.00,271.00 651.00,271.00 + 651.00,271.00 665.04,256.00 665.04,256.00 + 665.04,256.00 696.00,225.00 696.00,225.00 + 702.65,218.35 711.22,207.67 720.00,204.61 Z`, + ], + iconName: "yyy", + prefix: "xxx", +} as never; diff --git a/src/icons/Udemy.ts b/src/icons/Udemy.ts new file mode 100644 index 0000000..340548a --- /dev/null +++ b/src/icons/Udemy.ts @@ -0,0 +1,155 @@ +/** + * @copyright nhcarrigan + * @license Naomi's Public License + * @author Naomi Carrigan + */ +import type { IconDefinition } from "@fortawesome/fontawesome-svg-core"; + +/** + * Custom FontAwesome icon definition for the + * Udemy logo. + */ +export const Udemy: IconDefinition = { + icon: [ + 2118, + 2118, + [], + "U+E002", + `M 1027.00,0.21 + C 1027.00,0.21 1083.00,0.21 1083.00,0.21 + 1083.00,0.21 1098.00,1.00 1098.00,1.00 + 1098.00,1.00 1148.00,3.83 1148.00,3.83 + 1148.00,3.83 1185.00,7.28 1185.00,7.28 + 1242.11,14.33 1297.67,25.39 1353.00,41.29 + 1516.40,88.26 1668.12,175.50 1791.00,293.04 + 1933.98,429.80 2037.87,606.97 2085.87,799.00 + 2098.98,851.43 2107.96,904.25 2113.17,958.00 + 2113.17,958.00 2116.00,989.00 2116.00,989.00 + 2116.00,989.00 2116.00,998.00 2116.00,998.00 + 2116.00,998.00 2117.04,1008.00 2117.04,1008.00 + 2117.04,1008.00 2117.04,1023.00 2117.04,1023.00 + 2117.04,1023.00 2118.00,1035.00 2118.00,1035.00 + 2118.00,1035.00 2118.00,1083.00 2118.00,1083.00 + 2118.00,1083.00 2117.00,1100.00 2117.00,1100.00 + 2117.00,1100.00 2114.17,1148.00 2114.17,1148.00 + 2114.17,1148.00 2110.72,1185.00 2110.72,1185.00 + 2102.72,1249.75 2089.35,1313.67 2069.98,1376.00 + 2017.27,1545.58 1919.24,1702.31 1791.00,1824.96 + 1682.55,1928.69 1552.28,2008.03 1411.00,2058.31 + 1330.08,2087.10 1245.37,2104.90 1160.00,2113.17 + 1160.00,2113.17 1100.00,2117.00 1100.00,2117.00 + 1100.00,2117.00 1083.00,2118.00 1083.00,2118.00 + 1083.00,2118.00 1035.00,2118.00 1035.00,2118.00 + 1035.00,2118.00 1023.00,2117.04 1023.00,2117.04 + 1023.00,2117.04 1008.00,2117.04 1008.00,2117.04 + 1008.00,2117.04 982.00,2115.09 982.00,2115.09 + 982.00,2115.09 915.00,2108.28 915.00,2108.28 + 844.59,2099.01 767.69,2080.44 701.00,2056.05 + 586.65,2014.23 483.34,1956.90 389.00,1879.58 + 306.50,1811.96 237.26,1735.42 177.67,1647.00 + 84.91,1509.36 27.58,1349.41 7.28,1185.00 + 4.48,1162.30 1.03,1122.44 1.00,1100.00 + 1.00,1100.00 0.00,1083.00 0.00,1083.00 + 0.00,1083.00 0.00,1035.00 0.00,1035.00 + 0.00,1035.00 0.96,1023.00 0.96,1023.00 + 0.96,1023.00 0.96,1008.00 0.96,1008.00 + 0.96,1008.00 2.91,982.00 2.91,982.00 + 4.78,954.82 7.75,927.99 11.73,901.00 + 26.84,798.40 56.07,704.16 99.31,610.00 + 99.31,610.00 112.75,583.00 112.75,583.00 + 133.71,541.08 157.37,500.60 183.98,462.00 + 216.84,414.31 252.99,368.87 293.04,327.00 + 415.66,198.79 572.46,100.72 742.00,48.02 + 801.74,29.46 871.80,13.82 934.00,7.16 + 934.00,7.16 989.00,2.00 989.00,2.00 + 989.00,2.00 998.00,2.00 998.00,2.00 + 998.00,2.00 1009.00,1.00 1009.00,1.00 + 1015.02,0.97 1021.03,1.27 1027.00,0.21 Z + M 1518.00,690.00 + C 1518.00,690.00 1518.00,426.00 1518.00,426.00 + 1518.00,423.77 1518.24,420.43 1517.01,418.53 + 1515.72,416.54 1512.07,414.78 1510.00,413.58 + 1510.00,413.58 1494.00,404.15 1494.00,404.15 + 1494.00,404.15 1419.00,359.80 1419.00,359.80 + 1419.00,359.80 1152.00,202.20 1152.00,202.20 + 1152.00,202.20 1083.00,161.58 1083.00,161.58 + 1083.00,161.58 1066.00,151.58 1066.00,151.58 + 1063.11,149.94 1060.49,148.08 1057.00,148.74 + 1054.06,149.29 1047.80,153.32 1045.00,155.00 + 1045.00,155.00 1024.00,167.40 1024.00,167.40 + 1024.00,167.40 952.00,209.99 952.00,209.99 + 952.00,209.99 789.00,306.01 789.00,306.01 + 789.00,306.01 704.00,356.28 704.00,356.28 + 704.00,356.28 624.00,403.42 624.00,403.42 + 624.00,403.42 608.00,412.99 608.00,412.99 + 605.78,414.29 601.33,416.46 599.99,418.53 + 598.76,420.43 599.00,423.77 599.00,426.00 + 599.00,426.00 599.00,689.00 599.00,689.00 + 599.00,689.00 638.00,667.20 638.00,667.20 + 638.00,667.20 710.00,624.60 710.00,624.60 + 710.00,624.60 940.00,488.99 940.00,488.99 + 940.00,488.99 1024.00,439.40 1024.00,439.40 + 1024.00,439.40 1048.00,425.15 1048.00,425.15 + 1048.00,425.15 1058.00,420.19 1058.00,420.19 + 1058.00,420.19 1068.00,424.72 1068.00,424.72 + 1068.00,424.72 1092.00,438.99 1092.00,438.99 + 1092.00,438.99 1183.00,492.60 1183.00,492.60 + 1183.00,492.60 1399.00,620.01 1399.00,620.01 + 1399.00,620.01 1481.00,668.42 1481.00,668.42 + 1481.00,668.42 1518.00,690.00 1518.00,690.00 Z + M 839.73,918.00 + C 839.73,918.00 599.00,918.00 599.00,918.00 + 599.00,918.00 599.00,1443.00 599.00,1443.00 + 599.00,1443.00 599.00,1516.00 599.00,1516.00 + 599.00,1516.00 599.00,1560.00 599.00,1560.00 + 599.00,1560.00 603.45,1613.28 603.45,1613.28 + 614.91,1704.32 653.23,1790.62 722.00,1852.83 + 783.61,1908.57 850.45,1939.88 931.00,1957.58 + 951.57,1962.09 982.42,1966.56 1003.32,1967.81 + 1003.32,1967.81 1027.00,1969.00 1027.00,1969.00 + 1030.87,1969.05 1037.63,1969.15 1041.00,1971.00 + 1041.00,1971.00 1042.00,1969.00 1042.00,1969.00 + 1042.00,1969.00 1043.00,1971.00 1043.00,1971.00 + 1043.00,1971.00 1045.00,1969.00 1045.00,1969.00 + 1048.05,1971.81 1060.56,1971.10 1064.00,1969.00 + 1066.02,1970.69 1067.61,1969.70 1070.00,1969.00 + 1070.00,1969.00 1070.00,1971.00 1070.00,1971.00 + 1070.00,1971.00 1071.00,1971.00 1071.00,1971.00 + 1071.00,1971.00 1072.00,1969.00 1072.00,1969.00 + 1072.00,1969.00 1072.00,1971.00 1072.00,1971.00 + 1072.00,1971.00 1073.00,1971.00 1073.00,1971.00 + 1075.36,1968.47 1082.62,1969.01 1086.00,1969.00 + 1086.00,1969.00 1108.49,1967.78 1108.49,1967.78 + 1140.13,1965.79 1171.15,1959.99 1202.00,1952.99 + 1202.00,1952.99 1237.00,1942.67 1237.00,1942.67 + 1300.26,1921.58 1359.29,1886.83 1406.87,1839.86 + 1468.86,1778.64 1503.51,1693.23 1513.68,1607.58 + 1520.04,1554.02 1518.00,1499.86 1518.00,1446.00 + 1518.00,1446.00 1518.00,918.00 1518.00,918.00 + 1518.00,918.00 1277.21,918.00 1277.21,918.00 + 1277.21,918.00 1277.21,965.89 1277.21,965.89 + 1277.21,965.89 1277.21,1030.98 1277.21,1030.98 + 1277.21,1030.98 1277.21,1173.03 1277.21,1173.03 + 1277.21,1173.03 1277.21,1493.03 1277.21,1493.03 + 1277.21,1493.03 1277.21,1523.00 1277.21,1523.00 + 1276.78,1553.58 1270.07,1585.69 1258.55,1614.00 + 1225.82,1694.41 1146.00,1745.09 1060.00,1746.00 + 994.18,1746.69 928.60,1716.51 887.45,1665.00 + 876.49,1651.28 867.61,1635.94 860.31,1620.00 + 845.13,1586.86 839.80,1549.82 839.73,1513.67 + 839.73,1513.67 839.73,1116.33 839.73,1116.33 + 839.73,1116.33 839.73,984.33 839.73,984.33 + 839.73,984.33 839.73,941.67 839.73,941.67 + 839.73,941.67 839.73,918.00 839.73,918.00 Z + M 1048.00,1970.00 + C 1048.00,1970.00 1047.00,1970.00 1047.00,1970.00 + 1047.00,1970.00 1048.00,1971.00 1048.00,1971.00 + 1048.00,1971.00 1048.00,1970.00 1048.00,1970.00 Z + M 1068.00,1970.00 + C 1068.00,1970.00 1067.00,1970.00 1067.00,1970.00 + 1067.00,1970.00 1068.00,1971.00 1068.00,1971.00 + 1068.00,1971.00 1068.00,1970.00 1068.00,1970.00 Z`, + ], + iconName: "yyy", + prefix: "xxx", +} as never; diff --git a/test/app/api/activity/route.spec.ts b/test/app/api/activity/route.spec.ts new file mode 100644 index 0000000..471ec64 --- /dev/null +++ b/test/app/api/activity/route.spec.ts @@ -0,0 +1,100 @@ +/** + * @copyright nhcarrigan + * @license Naomi's Public License + * @author Naomi Carrigan + */ +/* eslint-disable new-cap */ +import { NextResponse } from "next/server"; +import { describe, it, expect, vi } from "vitest"; +import { GET } from "../../../../src/app/api/activity/route"; +import { getCodebergData } from "../../../../src/lib/codeberg"; +import { getGithubData } from "../../../../src/lib/github"; + +vi.mock("../../../../src/lib/codeberg"); +vi.mock("../../../../src/lib/github"); + +describe("gET /api/activity", () => { + it("should return a sorted and limited list of activities", async() => { + expect.assertions(2); + const mockCodebergData = [ + { + created: "2023-01-01T00:00:00Z", + op_type: "push", + repo: { full_name: "repo1", html_url: "https://codeberg.org/repo1" }, + }, + ]; + const mockGithubData = [ + { + created_at: "2023-01-02T00:00:00Z", + repo: { + name: "repo2", + url: "https://api.github.com/repos/repo2", + }, + type: "pull_request", + }, + ]; + + vi.mocked(getCodebergData).mockResolvedValue(mockCodebergData); + vi.mocked(getGithubData).mockResolvedValue(mockGithubData); + + const response = await GET(); + const json = await response.json(); + + expect(response, "did not respond with Next").toBeInstanceOf(NextResponse); + expect(json, "incorrect payload").toStrictEqual([ + { + date: "2023-01-02T00:00:00.000Z", + repo: "https://github.com/repo2", + repoName: "repo2", + type: "pull_request", + }, + { + date: "2023-01-01T00:00:00.000Z", + repo: "https://codeberg.org/repo1", + repoName: "repo1", + type: "push", + }, + ]); + }); + + it("should handle empty data from both sources", async() => { + expect.assertions(2); + vi.mocked(getCodebergData).mockResolvedValue([]); + vi.mocked(getGithubData).mockResolvedValue([]); + + const response = await GET(); + const json = await response.json(); + + expect(response, "did not use Next to respond"). + toBeInstanceOf(NextResponse); + expect(json, "was not empty array").toStrictEqual([]); + }); + + it("should limit the results to 100 entries", async() => { + expect.assertions(2); + const mockCodebergData = Array.from({ length: 60 }, (_, index) => { + return { + created: `2023-01-${index + 1}T00:00:00Z`, + op_type: "push", + repo: { full_name: `repo${index + 1}`, html_url: `https://codeberg.org/repo${index + 1}` }, + }; + }); + const mockGithubData = Array.from({ length: 60 }, (_, index) => { + return { + created_at: `2023-02-${index + 1}T00:00:00Z`, + repo: { name: `repo${index + 61}`, url: `https://api.github.com/repos/repo${index + 61}` }, + type: "pull_request", + }; + }); + + vi.mocked(getCodebergData).mockResolvedValue(mockCodebergData); + vi.mocked(getGithubData).mockResolvedValue(mockGithubData); + + const response = await GET(); + const json = await response.json(); + + expect(response, "did not use Next to respond"). + toBeInstanceOf(NextResponse); + expect(json, "did not limit to 100 entries").toHaveLength(100); + }); +}); diff --git a/test/config/Art.spec.ts b/test/config/Art.spec.ts new file mode 100644 index 0000000..98ff80d --- /dev/null +++ b/test/config/Art.spec.ts @@ -0,0 +1,37 @@ +/** + * @copyright nhcarrigan + * @license Naomi's Public License + * @author Naomi Carrigan + */ +import { describe, it, expect } from "vitest"; +import { Art } from "../../src/config/Art"; + +describe("art objects", () => { + it("should have unique names", () => { + expect.assertions(1); + const set = new Set( + Art.map((a) => { + return a.name; + }), + ); + expect(set, "are not unique").toHaveLength(Art.length); + }); + + it("should have unique img properties", () => { + expect.assertions(1); + const set = new Set( + Art.map((a) => { + return a.img; + }), + ); + expect(set, "are not unique").toHaveLength(Art.length); + }); + + it("should have alt text", () => { + expect.assertions(1); + const noText = Art.filter((a) => { + return a.alt.length === 0; + }); + expect(noText, "found missing alt").toHaveLength(0); + }); +}); diff --git a/test/config/Certifications.spec.ts b/test/config/Certifications.spec.ts new file mode 100644 index 0000000..726722b --- /dev/null +++ b/test/config/Certifications.spec.ts @@ -0,0 +1,29 @@ +/** + * @copyright nhcarrigan + * @license Naomi's Public License + * @author Naomi Carrigan + */ +import { describe, it, expect } from "vitest"; +import { Certifications } from "../../src/config/Certifications"; + +describe("certification objects", () => { + it("should have unique names", () => { + expect.assertions(1); + const set = new Set( + Certifications.map((c) => { + return c.name; + }), + ); + expect(set, "are not unique").toHaveLength(Certifications.length); + }); + + it("should have unique file names", () => { + expect.assertions(1); + const set = new Set( + Certifications.map((c) => { + return c.fileName; + }), + ); + expect(set, "are not unique").toHaveLength(Certifications.length); + }); +}); diff --git a/test/config/Games.spec.ts b/test/config/Games.spec.ts new file mode 100644 index 0000000..e0089b4 --- /dev/null +++ b/test/config/Games.spec.ts @@ -0,0 +1,47 @@ +/** + * @copyright nhcarrigan + * @license Naomi's Public License + * @author Naomi Carrigan + */ +import { describe, it, expect } from "vitest"; +import { Games } from "../../src/config/Games"; + +describe("games objects", () => { + it("should have unique names", () => { + expect.assertions(1); + const set = new Set( + Games.map((g) => { + return g.name; + }), + ); + expect(set, "are not unique").toHaveLength(Games.length); + }); + + it("should have unique img properties", () => { + expect.assertions(1); + const set = new Set( + Games.map((g) => { + return g.img; + }), + ); + expect(set, "are not unique").toHaveLength(Games.length); + }); + + it("should have unique urls", () => { + expect.assertions(1); + const set = new Set( + Games.map((g) => { + return g.url; + }), + ); + expect(set, "are not unique").toHaveLength(Games.length); + }); + + it("should have alt text", () => { + expect.assertions(1); + const noText = Games.filter((g) => { + return g.alt.length === 0; + }); + expect(noText, "found missing alt").toHaveLength(0); + }); +}); diff --git a/test/config/Jobs.spec.ts b/test/config/Jobs.spec.ts new file mode 100644 index 0000000..6d1afa0 --- /dev/null +++ b/test/config/Jobs.spec.ts @@ -0,0 +1,33 @@ +/** + * @copyright nhcarrigan + * @license Naomi's Public License + * @author Naomi Carrigan + */ +import { describe, it, expect } from "vitest"; +import { Jobs } from "../../src/config/Jobs"; + +describe("jobs objects", () => { + it("should have correct start dates", () => { + expect.hasAssertions(); + for (const job of Jobs.slice(1)) { + expect(job.start.getDate(), `${job.title} has bad start`).toBe(5); + } + }); + + it("should have correct end dates", () => { + expect.hasAssertions(); + for (const job of Jobs.slice(1)) { + if (!job.end) { + continue; + } + expect(job.end.getDate(), `${job.title} has bad end`).toBe(5); + } + }); + + it("should have no future start dates", () => { + expect.hasAssertions(); + expect(Jobs.filter((job) => { + return job.start > new Date(); + }), "have future start dates").toHaveLength(0); + }); +}); diff --git a/test/config/NavItems.spec.ts b/test/config/NavItems.spec.ts new file mode 100644 index 0000000..2aaa6fd --- /dev/null +++ b/test/config/NavItems.spec.ts @@ -0,0 +1,30 @@ +/** + * @copyright nhcarrigan + * @license Naomi's Public License + * @author Naomi Carrigan + */ +import { describe, it, expect } from "vitest"; +import { NavItems } from "../../src/config/NavItems"; + +describe("nav items", () => { + it("should be unique", () => { + expect.assertions(2); + const href = new Set( + NavItems.map((n) => { + return n.href; + }), + ); + const text = new Set(NavItems.map((n) => { + return n.text; + })); + expect(href, "links are not unique").toHaveLength(NavItems.length); + expect(text, "names are not unique").toHaveLength(NavItems.length); + }); + + it("should be internal links", () => { + expect.hasAssertions(); + for (const nav of NavItems) { + expect(nav.href, `${nav.href} is not internal`).toMatch(/^\/[\da-z-]+$/); + } + }); +}); diff --git a/test/config/Partners.spec.ts b/test/config/Partners.spec.ts new file mode 100644 index 0000000..12622cd --- /dev/null +++ b/test/config/Partners.spec.ts @@ -0,0 +1,29 @@ +/** + * @copyright nhcarrigan + * @license Naomi's Public License + * @author Naomi Carrigan + */ +import { describe, it, expect } from "vitest"; +import { Partners } from "../../src/config/Partners"; + +describe("partner objects", () => { + it("should have unique names", () => { + expect.assertions(1); + const set = new Set( + Partners.map((p) => { + return p.name; + }), + ); + expect(set, "are not unique").toHaveLength(Partners.length); + }); + + it("should have unique avatars", () => { + expect.assertions(1); + const set = new Set( + Partners.map((p) => { + return p.avatar; + }), + ); + expect(set, "are not unique").toHaveLength(Partners.length); + }); +}); diff --git a/test/config/Socials.spec.ts b/test/config/Socials.spec.ts new file mode 100644 index 0000000..67b0396 --- /dev/null +++ b/test/config/Socials.spec.ts @@ -0,0 +1,109 @@ +/** + * @copyright nhcarrigan + * @license Naomi's Public License + * @author Naomi Carrigan + */ +import { faBriefcase, faMoneyBill } from "@fortawesome/free-solid-svg-icons"; +import { describe, it, expect } from "vitest"; +import { HireMe, Donate, Socials } from "../../src/config/Socials"; + +describe("socials objects", () => { + it("should have unique labels", () => { + expect.assertions(1); + const set = new Set( + Socials.map((s) => { + return s.label; + }), + ); + expect(set, "are not unique").toHaveLength(Socials.length); + }); + + it("should have unique links", () => { + expect.assertions(1); + const set = new Set( + Socials.map((s) => { + return s.link; + }), + ); + expect(set, "are not unique").toHaveLength(Socials.length); + }); + + it("should have unique icons", () => { + expect.assertions(1); + const set = new Set( + Socials.map((s) => { + return s.icon; + }), + ); + expect(set, "are not unique").toHaveLength(Socials.length); + }); +}); + +describe("hire me object", () => { + it("should have correct label", () => { + expect.assertions(1); + expect(HireMe.label, "does not").toBe("Hire Us!"); + }); + + it("should have correct link", () => { + expect.assertions(1); + expect(HireMe.link, "does not"). + toBe("https://docs.nhcarrigan.com/about/hire/"); + }); + + it("should have correct colours", () => { + expect.assertions(2); + expect(HireMe.color, "colour is wrong").toBe("#003600"); + expect(HireMe.background, "background is wrong").toBe(`linear-gradient( + 90deg, + #5bcefa, + #f5a9b8, + #ffffff, + #f5a9b8, + #5bcefa + )`); + }); + + it("should have correct icon", () => { + expect.assertions(1); + expect(HireMe.icon, "does not").toBe(faBriefcase); + }); +}); + +describe("donate object", () => { + it("should have correct label", () => { + expect.assertions(1); + expect(Donate.label, "does not").toBe("Donate 💜"); + }); + + it("should have correct link", () => { + expect.assertions(1); + expect(Donate.link, "does not"). + toBe("https://docs.nhcarrigan.com/about/donate/"); + }); + + it("should have correct colours", () => { + expect.assertions(2); + expect(Donate.color, "has wrong colour").toBe("#003600"); + expect(Donate.background, "has wrong background").toBe(`linear-gradient( + 90deg, + rgba(255, 0, 0, 1) 0%, + rgba(251, 7, 217, 1) 10%, + rgba(186, 12, 248, 1) 20%, + rgba(95, 21, 242, 1) 30%, + rgba(28, 127, 238, 1) 40%, + rgba(47, 201, 226, 1) 50%, + rgba(63, 218, 216, 1) 60%, + rgba(79, 220, 74, 1) 70%, + rgba(208, 222, 33, 1) 80%, + rgba(255, 154, 0, 1) 90%, + rgba(255, 0, 0, 1) 100% + )`); + }); + + it("should have correct icon", () => { + expect.assertions(1); + expect(Donate.icon, "does not").toBe(faMoneyBill); + }); +}); + diff --git a/test/config/TeamMembers.spec.ts b/test/config/TeamMembers.spec.ts new file mode 100644 index 0000000..db7f70d --- /dev/null +++ b/test/config/TeamMembers.spec.ts @@ -0,0 +1,36 @@ +/** + * @copyright nhcarrigan + * @license Naomi's Public License + * @author Naomi Carrigan + */ +import { describe, it, expect } from "vitest"; +import { TeamMembers } from "../../src/config/TeamMembers"; + +describe("partner objects", () => { + it("should have unique names", () => { + expect.assertions(1); + const set = new Set( + TeamMembers.map((t) => { + return t.name; + }), + ); + expect(set, "are not unique").toHaveLength(TeamMembers.length); + }); + + it("should have unique avatars", () => { + expect.assertions(1); + const set = new Set( + TeamMembers.map((t) => { + return t.avatar; + }), + ); + expect(set, "are not unique").toHaveLength(TeamMembers.length); + }); + + it("should have no future dates", () => { + expect.assertions(1); + expect(TeamMembers.filter((t) => { + return new Date(t.joinDate) > new Date(); + }), "have future dates").toHaveLength(0); + }); +}); diff --git a/test/config/Testimonials.spec.ts b/test/config/Testimonials.spec.ts new file mode 100644 index 0000000..adc7381 --- /dev/null +++ b/test/config/Testimonials.spec.ts @@ -0,0 +1,26 @@ +/** + * @copyright nhcarrigan + * @license Naomi's Public License + * @author Naomi Carrigan + */ +import { describe, it, expect } from "vitest"; +import { Testimonials } from "../../src/config/Testimonials"; + +describe("testimonial objects", () => { + it("should have unique names", () => { + expect.assertions(1); + const set = new Set( + Testimonials.map((t) => { + return t.name; + }), + ); + expect(set, "are not unique").toHaveLength(Testimonials.length); + }); + + it("should have no future dates", () => { + expect.assertions(1); + expect(Testimonials.filter((t) => { + return new Date(t.date) > new Date(); + }), "have future dates").toHaveLength(0); + }); +}); diff --git a/test/icons/Codeberg.spec.ts b/test/icons/Codeberg.spec.ts new file mode 100644 index 0000000..d078ec5 --- /dev/null +++ b/test/icons/Codeberg.spec.ts @@ -0,0 +1,38 @@ +/** + * @copyright nhcarrigan + * @license Naomi's Public License + * @author Naomi Carrigan + */ + +import { describe, it, expect } from "vitest"; +import { Codeberg } from "../../src/icons/Codeberg"; + +describe("codeberg icon", () => { + it("should have a valid width", () => { + expect.assertions(1); + expect(Codeberg.icon[0], "width is negative").toBeGreaterThan(0); + }); + + it("should have a valid height", () => { + expect.assertions(1); + expect(Codeberg.icon[1], "height is negative").toBeGreaterThan(0); + }); + + it("should not have any ligatures", () => { + expect.assertions(1); + expect(Codeberg.icon[2], "ligatures are present").toStrictEqual([]); + }); + + it("should have a valid unicode set", () => { + expect.assertions(1); + expect(Codeberg.icon[3], "unicode set is wrong").toBe("U+E002"); + }); + + it("should have valid SVG path data", () => { + expect.assertions(1); + expect(Codeberg.icon[4], "path data is bad").toMatch( + // eslint-disable-next-line stylistic/max-len + /(?:[lm]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:[hv]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:c\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){5})|(?:q\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3}(?:\s?t?\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))*)|(?:a\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2}[\s,]?(?:[01][\s,]+){2}(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2})|(?:s\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3})|z/gi, + ); + }); +}); diff --git a/test/icons/Coursera.spec.ts b/test/icons/Coursera.spec.ts new file mode 100644 index 0000000..8156800 --- /dev/null +++ b/test/icons/Coursera.spec.ts @@ -0,0 +1,38 @@ +/** + * @copyright nhcarrigan + * @license Naomi's Public License + * @author Naomi Carrigan + */ + +import { describe, it, expect } from "vitest"; +import { Coursera } from "../../src/icons/Coursera"; + +describe("coursera icon", () => { + it("should have a valid width", () => { + expect.assertions(1); + expect(Coursera.icon[0], "width is negative").toBeGreaterThan(0); + }); + + it("should have a valid height", () => { + expect.assertions(1); + expect(Coursera.icon[1], "height is negative").toBeGreaterThan(0); + }); + + it("should not have any ligatures", () => { + expect.assertions(1); + expect(Coursera.icon[2], "ligatures are present").toStrictEqual([]); + }); + + it("should have a valid unicode set", () => { + expect.assertions(1); + expect(Coursera.icon[3], "unicode set is wrong").toBe("U+E002"); + }); + + it("should have valid SVG path data", () => { + expect.assertions(1); + expect(Coursera.icon[4], "path data is bad").toMatch( + // eslint-disable-next-line stylistic/max-len + /(?:[lm]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:[hv]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:c\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){5})|(?:q\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3}(?:\s?t?\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))*)|(?:a\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2}[\s,]?(?:[01][\s,]+){2}(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2})|(?:s\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3})|z/gi, + ); + }); +}); diff --git a/test/icons/Fiverr.spec.ts b/test/icons/Fiverr.spec.ts new file mode 100644 index 0000000..433f13c --- /dev/null +++ b/test/icons/Fiverr.spec.ts @@ -0,0 +1,38 @@ +/** + * @copyright nhcarrigan + * @license Naomi's Public License + * @author Naomi Carrigan + */ + +import { describe, it, expect } from "vitest"; +import { Fiverr } from "../../src/icons/Fiverr"; + +describe("fiverr icon", () => { + it("should have a valid width", () => { + expect.assertions(1); + expect(Fiverr.icon[0], "width is negative").toBeGreaterThan(0); + }); + + it("should have a valid height", () => { + expect.assertions(1); + expect(Fiverr.icon[1], "height is negative").toBeGreaterThan(0); + }); + + it("should not have any ligatures", () => { + expect.assertions(1); + expect(Fiverr.icon[2], "ligatures are present").toStrictEqual([]); + }); + + it("should have a valid unicode set", () => { + expect.assertions(1); + expect(Fiverr.icon[3], "unicode set is wrong").toBe("U+E002"); + }); + + it("should have valid SVG path data", () => { + expect.assertions(1); + expect(Fiverr.icon[4], "path data is bad").toMatch( + // eslint-disable-next-line stylistic/max-len + /(?:[lm]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:[hv]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:c\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){5})|(?:q\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3}(?:\s?t?\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))*)|(?:a\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2}[\s,]?(?:[01][\s,]+){2}(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2})|(?:s\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3})|z/gi, + ); + }); +}); diff --git a/test/icons/Gather.spec.ts b/test/icons/Gather.spec.ts new file mode 100644 index 0000000..c50e9fa --- /dev/null +++ b/test/icons/Gather.spec.ts @@ -0,0 +1,38 @@ +/** + * @copyright nhcarrigan + * @license Naomi's Public License + * @author Naomi Carrigan + */ + +import { describe, it, expect } from "vitest"; +import { Gather } from "../../src/icons/Gather"; + +describe("gather icon", () => { + it("should have a valid width", () => { + expect.assertions(1); + expect(Gather.icon[0], "width is negative").toBeGreaterThan(0); + }); + + it("should have a valid height", () => { + expect.assertions(1); + expect(Gather.icon[1], "height is negative").toBeGreaterThan(0); + }); + + it("should not have any ligatures", () => { + expect.assertions(1); + expect(Gather.icon[2], "ligatures are present").toStrictEqual([]); + }); + + it("should have a valid unicode set", () => { + expect.assertions(1); + expect(Gather.icon[3], "unicode set is wrong").toBe("U+E002"); + }); + + it("should have valid SVG path data", () => { + expect.assertions(1); + expect(Gather.icon[4], "path data is bad").toMatch( + // eslint-disable-next-line stylistic/max-len + /(?:[lm]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:[hv]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:c\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){5})|(?:q\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3}(?:\s?t?\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))*)|(?:a\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2}[\s,]?(?:[01][\s,]+){2}(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2})|(?:s\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3})|z/gi, + ); + }); +}); diff --git a/test/icons/Gog.spec.ts b/test/icons/Gog.spec.ts new file mode 100644 index 0000000..3c505bb --- /dev/null +++ b/test/icons/Gog.spec.ts @@ -0,0 +1,38 @@ +/** + * @copyright nhcarrigan + * @license Naomi's Public License + * @author Naomi Carrigan + */ + +import { describe, it, expect } from "vitest"; +import { Gog } from "../../src/icons/Gog"; + +describe("gog icon", () => { + it("should have a valid width", () => { + expect.assertions(1); + expect(Gog.icon[0], "width is negative").toBeGreaterThan(0); + }); + + it("should have a valid height", () => { + expect.assertions(1); + expect(Gog.icon[1], "height is negative").toBeGreaterThan(0); + }); + + it("should not have any ligatures", () => { + expect.assertions(1); + expect(Gog.icon[2], "ligatures are present").toStrictEqual([]); + }); + + it("should have a valid unicode set", () => { + expect.assertions(1); + expect(Gog.icon[3], "unicode set is wrong").toBe("U+E002"); + }); + + it("should have valid SVG path data", () => { + expect.assertions(1); + expect(Gog.icon[4], "path data is bad").toMatch( + // eslint-disable-next-line stylistic/max-len + /(?:[lm]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:[hv]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:c\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){5})|(?:q\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3}(?:\s?t?\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))*)|(?:a\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2}[\s,]?(?:[01][\s,]+){2}(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2})|(?:s\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3})|z/gi, + ); + }); +}); diff --git a/test/icons/Kofi.spec.ts b/test/icons/Kofi.spec.ts new file mode 100644 index 0000000..396e310 --- /dev/null +++ b/test/icons/Kofi.spec.ts @@ -0,0 +1,38 @@ +/** + * @copyright nhcarrigan + * @license Naomi's Public License + * @author Naomi Carrigan + */ + +import { describe, it, expect } from "vitest"; +import { Kofi } from "../../src/icons/KoFi"; + +describe("kofi icon", () => { + it("should have a valid width", () => { + expect.assertions(1); + expect(Kofi.icon[0], "width is negative").toBeGreaterThan(0); + }); + + it("should have a valid height", () => { + expect.assertions(1); + expect(Kofi.icon[1], "height is negative").toBeGreaterThan(0); + }); + + it("should not have any ligatures", () => { + expect.assertions(1); + expect(Kofi.icon[2], "ligatures are present").toStrictEqual([]); + }); + + it("should have a valid unicode set", () => { + expect.assertions(1); + expect(Kofi.icon[3], "unicode set is wrong").toBe("U+E002"); + }); + + it("should have valid SVG path data", () => { + expect.assertions(1); + expect(Kofi.icon[4], "path data is bad").toMatch( + // eslint-disable-next-line stylistic/max-len + /(?:[lm]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:[hv]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:c\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){5})|(?:q\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3}(?:\s?t?\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))*)|(?:a\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2}[\s,]?(?:[01][\s,]+){2}(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2})|(?:s\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3})|z/gi, + ); + }); +}); diff --git a/test/icons/Matrix.spec.ts b/test/icons/Matrix.spec.ts new file mode 100644 index 0000000..506bbc9 --- /dev/null +++ b/test/icons/Matrix.spec.ts @@ -0,0 +1,38 @@ +/** + * @copyright nhcarrigan + * @license Naomi's Public License + * @author Naomi Carrigan + */ + +import { describe, it, expect } from "vitest"; +import { Matrix } from "../../src/icons/Matrix"; + +describe("matrix icon", () => { + it("should have a valid width", () => { + expect.assertions(1); + expect(Matrix.icon[0], "width is negative").toBeGreaterThan(0); + }); + + it("should have a valid height", () => { + expect.assertions(1); + expect(Matrix.icon[1], "height is negative").toBeGreaterThan(0); + }); + + it("should not have any ligatures", () => { + expect.assertions(1); + expect(Matrix.icon[2], "ligatures are present").toStrictEqual([]); + }); + + it("should have a valid unicode set", () => { + expect.assertions(1); + expect(Matrix.icon[3], "unicode set is wrong").toBe("U+E002"); + }); + + it("should have valid SVG path data", () => { + expect.assertions(1); + expect(Matrix.icon[4], "path data is bad").toMatch( + // eslint-disable-next-line stylistic/max-len + /(?:[lm]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:[hv]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:c\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){5})|(?:q\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3}(?:\s?t?\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))*)|(?:a\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2}[\s,]?(?:[01][\s,]+){2}(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2})|(?:s\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3})|z/gi, + ); + }); +}); diff --git a/test/icons/Peerlist.spec.ts b/test/icons/Peerlist.spec.ts new file mode 100644 index 0000000..f427344 --- /dev/null +++ b/test/icons/Peerlist.spec.ts @@ -0,0 +1,38 @@ +/** + * @copyright nhcarrigan + * @license Naomi's Public License + * @author Naomi Carrigan + */ + +import { describe, it, expect } from "vitest"; +import { Peerlist } from "../../src/icons/Peerlist"; + +describe("peerlist icon", () => { + it("should have a valid width", () => { + expect.assertions(1); + expect(Peerlist.icon[0], "width is negative").toBeGreaterThan(0); + }); + + it("should have a valid height", () => { + expect.assertions(1); + expect(Peerlist.icon[1], "height is negative").toBeGreaterThan(0); + }); + + it("should not have any ligatures", () => { + expect.assertions(1); + expect(Peerlist.icon[2], "ligatures are present").toStrictEqual([]); + }); + + it("should have a valid unicode set", () => { + expect.assertions(1); + expect(Peerlist.icon[3], "unicode set is wrong").toBe("U+E002"); + }); + + it("should have valid SVG path data", () => { + expect.assertions(1); + expect(Peerlist.icon[4], "path data is bad").toMatch( + // eslint-disable-next-line stylistic/max-len + /(?:[lm]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:[hv]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:c\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){5})|(?:q\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3}(?:\s?t?\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))*)|(?:a\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2}[\s,]?(?:[01][\s,]+){2}(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2})|(?:s\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3})|z/gi, + ); + }); +}); diff --git a/test/icons/Pixiv.spec.ts b/test/icons/Pixiv.spec.ts new file mode 100644 index 0000000..30685e3 --- /dev/null +++ b/test/icons/Pixiv.spec.ts @@ -0,0 +1,38 @@ +/** + * @copyright nhcarrigan + * @license Naomi's Public License + * @author Naomi Carrigan + */ + +import { describe, it, expect } from "vitest"; +import { Pixiv } from "../../src/icons/Pixiv"; + +describe("pixiv icon", () => { + it("should have a valid width", () => { + expect.assertions(1); + expect(Pixiv.icon[0], "width is negative").toBeGreaterThan(0); + }); + + it("should have a valid height", () => { + expect.assertions(1); + expect(Pixiv.icon[1], "height is negative").toBeGreaterThan(0); + }); + + it("should not have any ligatures", () => { + expect.assertions(1); + expect(Pixiv.icon[2], "ligatures are present").toStrictEqual([]); + }); + + it("should have a valid unicode set", () => { + expect.assertions(1); + expect(Pixiv.icon[3], "unicode set is wrong").toBe("U+E002"); + }); + + it("should have valid SVG path data", () => { + expect.assertions(1); + expect(Pixiv.icon[4], "path data is bad").toMatch( + // eslint-disable-next-line stylistic/max-len + /(?:[lm]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:[hv]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:c\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){5})|(?:q\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3}(?:\s?t?\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))*)|(?:a\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2}[\s,]?(?:[01][\s,]+){2}(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2})|(?:s\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3})|z/gi, + ); + }); +}); diff --git a/test/icons/Polywork.spec.ts b/test/icons/Polywork.spec.ts new file mode 100644 index 0000000..927bc04 --- /dev/null +++ b/test/icons/Polywork.spec.ts @@ -0,0 +1,38 @@ +/** + * @copyright nhcarrigan + * @license Naomi's Public License + * @author Naomi Carrigan + */ + +import { describe, it, expect } from "vitest"; +import { Polywork } from "../../src/icons/Polywork"; + +describe("polywork icon", () => { + it("should have a valid width", () => { + expect.assertions(1); + expect(Polywork.icon[0], "width is negative").toBeGreaterThan(0); + }); + + it("should have a valid height", () => { + expect.assertions(1); + expect(Polywork.icon[1], "height is negative").toBeGreaterThan(0); + }); + + it("should not have any ligatures", () => { + expect.assertions(1); + expect(Polywork.icon[2], "ligatures are present").toStrictEqual([]); + }); + + it("should have a valid unicode set", () => { + expect.assertions(1); + expect(Polywork.icon[3], "unicode set is wrong").toBe("U+E002"); + }); + + it("should have valid SVG path data", () => { + expect.assertions(1); + expect(Polywork.icon[4], "path data is bad").toMatch( + // eslint-disable-next-line stylistic/max-len + /(?:[lm]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:[hv]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:c\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){5})|(?:q\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3}(?:\s?t?\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))*)|(?:a\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2}[\s,]?(?:[01][\s,]+){2}(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2})|(?:s\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3})|z/gi, + ); + }); +}); diff --git a/test/icons/Saylor.spec.ts b/test/icons/Saylor.spec.ts new file mode 100644 index 0000000..e80b899 --- /dev/null +++ b/test/icons/Saylor.spec.ts @@ -0,0 +1,38 @@ +/** + * @copyright nhcarrigan + * @license Naomi's Public License + * @author Naomi Carrigan + */ + +import { describe, it, expect } from "vitest"; +import { Saylor } from "../../src/icons/Saylor"; + +describe("saylor icon", () => { + it("should have a valid width", () => { + expect.assertions(1); + expect(Saylor.icon[0], "width is negative").toBeGreaterThan(0); + }); + + it("should have a valid height", () => { + expect.assertions(1); + expect(Saylor.icon[1], "height is negative").toBeGreaterThan(0); + }); + + it("should not have any ligatures", () => { + expect.assertions(1); + expect(Saylor.icon[2], "ligatures are present").toStrictEqual([]); + }); + + it("should have a valid unicode set", () => { + expect.assertions(1); + expect(Saylor.icon[3], "unicode set is wrong").toBe("U+E002"); + }); + + it("should have valid SVG path data", () => { + expect.assertions(1); + expect(Saylor.icon[4], "path data is bad").toMatch( + // eslint-disable-next-line stylistic/max-len + /(?:[lm]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:[hv]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:c\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){5})|(?:q\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3}(?:\s?t?\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))*)|(?:a\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2}[\s,]?(?:[01][\s,]+){2}(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2})|(?:s\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3})|z/gi, + ); + }); +}); diff --git a/test/icons/TeeSpring.spec.ts b/test/icons/TeeSpring.spec.ts new file mode 100644 index 0000000..01aa234 --- /dev/null +++ b/test/icons/TeeSpring.spec.ts @@ -0,0 +1,38 @@ +/** + * @copyright nhcarrigan + * @license Naomi's Public License + * @author Naomi Carrigan + */ + +import { describe, it, expect } from "vitest"; +import { TeeSpring } from "../../src/icons/TeeSpring"; + +describe("teespring icon", () => { + it("should have a valid width", () => { + expect.assertions(1); + expect(TeeSpring.icon[0], "width is negative").toBeGreaterThan(0); + }); + + it("should have a valid height", () => { + expect.assertions(1); + expect(TeeSpring.icon[1], "height is negative").toBeGreaterThan(0); + }); + + it("should not have any ligatures", () => { + expect.assertions(1); + expect(TeeSpring.icon[2], "ligatures are present").toStrictEqual([]); + }); + + it("should have a valid unicode set", () => { + expect.assertions(1); + expect(TeeSpring.icon[3], "unicode set is wrong").toBe("U+E002"); + }); + + it("should have valid SVG path data", () => { + expect.assertions(1); + expect(TeeSpring.icon[4], "path data is bad").toMatch( + // eslint-disable-next-line stylistic/max-len + /(?:[lm]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:[hv]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:c\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){5})|(?:q\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3}(?:\s?t?\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))*)|(?:a\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2}[\s,]?(?:[01][\s,]+){2}(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2})|(?:s\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3})|z/gi, + ); + }); +}); diff --git a/test/icons/Throne.spec.ts b/test/icons/Throne.spec.ts new file mode 100644 index 0000000..a90a3c4 --- /dev/null +++ b/test/icons/Throne.spec.ts @@ -0,0 +1,38 @@ +/** + * @copyright nhcarrigan + * @license Naomi's Public License + * @author Naomi Carrigan + */ + +import { describe, it, expect } from "vitest"; +import { Throne } from "../../src/icons/Throne"; + +describe("throne icon", () => { + it("should have a valid width", () => { + expect.assertions(1); + expect(Throne.icon[0], "width is negative").toBeGreaterThan(0); + }); + + it("should have a valid height", () => { + expect.assertions(1); + expect(Throne.icon[1], "height is negative").toBeGreaterThan(0); + }); + + it("should not have any ligatures", () => { + expect.assertions(1); + expect(Throne.icon[2], "ligatures are present").toStrictEqual([]); + }); + + it("should have a valid unicode set", () => { + expect.assertions(1); + expect(Throne.icon[3], "unicode set is wrong").toBe("U+E002"); + }); + + it("should have valid SVG path data", () => { + expect.assertions(1); + expect(Throne.icon[4], "path data is bad").toMatch( + // eslint-disable-next-line stylistic/max-len + /(?:[lm]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:[hv]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:c\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){5})|(?:q\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3}(?:\s?t?\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))*)|(?:a\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2}[\s,]?(?:[01][\s,]+){2}(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2})|(?:s\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3})|z/gi, + ); + }); +}); diff --git a/test/icons/TreeNation.spec.ts b/test/icons/TreeNation.spec.ts new file mode 100644 index 0000000..fe36f2d --- /dev/null +++ b/test/icons/TreeNation.spec.ts @@ -0,0 +1,38 @@ +/** + * @copyright nhcarrigan + * @license Naomi's Public License + * @author Naomi Carrigan + */ + +import { describe, it, expect } from "vitest"; +import { TreeNation } from "../../src/icons/TreeNation"; + +describe("treenation icon", () => { + it("should have a valid width", () => { + expect.assertions(1); + expect(TreeNation.icon[0], "width is negative").toBeGreaterThan(0); + }); + + it("should have a valid height", () => { + expect.assertions(1); + expect(TreeNation.icon[1], "height is negative").toBeGreaterThan(0); + }); + + it("should not have any ligatures", () => { + expect.assertions(1); + expect(TreeNation.icon[2], "ligatures are present").toStrictEqual([]); + }); + + it("should have a valid unicode set", () => { + expect.assertions(1); + expect(TreeNation.icon[3], "unicode set is wrong").toBe("U+E002"); + }); + + it("should have valid SVG path data", () => { + expect.assertions(1); + expect(TreeNation.icon[4], "path data is bad").toMatch( + // eslint-disable-next-line stylistic/max-len + /(?:[lm]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:[hv]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:c\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){5})|(?:q\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3}(?:\s?t?\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))*)|(?:a\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2}[\s,]?(?:[01][\s,]+){2}(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2})|(?:s\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3})|z/gi, + ); + }); +}); diff --git a/test/icons/Udemy.spec.ts b/test/icons/Udemy.spec.ts new file mode 100644 index 0000000..9c8c7cf --- /dev/null +++ b/test/icons/Udemy.spec.ts @@ -0,0 +1,38 @@ +/** + * @copyright nhcarrigan + * @license Naomi's Public License + * @author Naomi Carrigan + */ + +import { describe, it, expect } from "vitest"; +import { Udemy } from "../../src/icons/Udemy"; + +describe("udemy icon", () => { + it("should have a valid width", () => { + expect.assertions(1); + expect(Udemy.icon[0], "width is negative").toBeGreaterThan(0); + }); + + it("should have a valid height", () => { + expect.assertions(1); + expect(Udemy.icon[1], "height is negative").toBeGreaterThan(0); + }); + + it("should not have any ligatures", () => { + expect.assertions(1); + expect(Udemy.icon[2], "ligatures are present").toStrictEqual([]); + }); + + it("should have a valid unicode set", () => { + expect.assertions(1); + expect(Udemy.icon[3], "unicode set is wrong").toBe("U+E002"); + }); + + it("should have valid SVG path data", () => { + expect.assertions(1); + expect(Udemy.icon[4], "path data is bad").toMatch( + // eslint-disable-next-line stylistic/max-len + /(?:[lm]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:[hv]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:c\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){5})|(?:q\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3}(?:\s?t?\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))*)|(?:a\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2}[\s,]?(?:[01][\s,]+){2}(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2})|(?:s\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3})|z/gi, + ); + }); +}); diff --git a/test/icons/VRoid.spec.ts b/test/icons/VRoid.spec.ts new file mode 100644 index 0000000..7b05791 --- /dev/null +++ b/test/icons/VRoid.spec.ts @@ -0,0 +1,38 @@ +/** + * @copyright nhcarrigan + * @license Naomi's Public License + * @author Naomi Carrigan + */ + +import { describe, it, expect } from "vitest"; +import { VRoid } from "../../src/icons/VRoid"; + +describe("vroid icon", () => { + it("should have a valid width", () => { + expect.assertions(1); + expect(VRoid.icon[0], "width is negative").toBeGreaterThan(0); + }); + + it("should have a valid height", () => { + expect.assertions(1); + expect(VRoid.icon[1], "height is negative").toBeGreaterThan(0); + }); + + it("should not have any ligatures", () => { + expect.assertions(1); + expect(VRoid.icon[2], "ligatures are present").toStrictEqual([]); + }); + + it("should have a valid unicode set", () => { + expect.assertions(1); + expect(VRoid.icon[3], "unicode set is wrong").toBe("U+E002"); + }); + + it("should have valid SVG path data", () => { + expect.assertions(1); + expect(VRoid.icon[4], "path data is bad").toMatch( + // eslint-disable-next-line stylistic/max-len + /(?:[lm]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:[hv]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:c\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){5})|(?:q\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3}(?:\s?t?\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))*)|(?:a\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2}[\s,]?(?:[01][\s,]+){2}(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2})|(?:s\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3})|z/gi, + ); + }); +}); diff --git a/test/icons/Volunteer.spec.ts b/test/icons/Volunteer.spec.ts new file mode 100644 index 0000000..5ab0c01 --- /dev/null +++ b/test/icons/Volunteer.spec.ts @@ -0,0 +1,38 @@ +/** + * @copyright nhcarrigan + * @license Naomi's Public License + * @author Naomi Carrigan + */ + +import { describe, it, expect } from "vitest"; +import { Volunteer } from "../../src/icons/Volunteer"; + +describe("volunteer icon", () => { + it("should have a valid width", () => { + expect.assertions(1); + expect(Volunteer.icon[0], "width is negative").toBeGreaterThan(0); + }); + + it("should have a valid height", () => { + expect.assertions(1); + expect(Volunteer.icon[1], "height is negative").toBeGreaterThan(0); + }); + + it("should not have any ligatures", () => { + expect.assertions(1); + expect(Volunteer.icon[2], "ligatures are present").toStrictEqual([]); + }); + + it("should have a valid unicode set", () => { + expect.assertions(1); + expect(Volunteer.icon[3], "unicode set is wrong").toBe("U+E002"); + }); + + it("should have valid SVG path data", () => { + expect.assertions(1); + expect(Volunteer.icon[4], "path data is bad").toMatch( + // eslint-disable-next-line stylistic/max-len + /(?:[lm]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:[hv]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:c\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){5})|(?:q\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3}(?:\s?t?\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))*)|(?:a\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2}[\s,]?(?:[01][\s,]+){2}(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2})|(?:s\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3})|z/gi, + ); + }); +}); diff --git a/test/lib/codeberg.spec.ts b/test/lib/codeberg.spec.ts new file mode 100644 index 0000000..a304d27 --- /dev/null +++ b/test/lib/codeberg.spec.ts @@ -0,0 +1,345 @@ +/** + * @copyright nhcarrigan + * @license Naomi's Public License + * @author Naomi Carrigan + */ +import { describe, it, expect, vi, beforeEach } from "vitest"; +import { getCodebergData } from "../../src/lib/codeberg"; + +describe("codeberg", () => { + beforeEach(() => { + vi.resetAllMocks(); + }); + + it("should fetch and cache activities", async() => { + expect.assertions(1); + const mockResponse = [ + { + act_user: { + active: true, + avatar_url: "https://example.com/avatar.png", + created: "2023-01-01T00:00:00Z", + description: "Developer", + email: "naomi@example.com", + followers_count: 100, + following_count: 50, + full_name: "Naomi Carrigan", + html_url: "https://example.com", + id: 1, + is_admin: false, + language: "en", + last_login: "2023-01-01T00:00:00Z", + location: "Earth", + login: "naomi", + login_name: "naomi", + prohibit_login: false, + pronouns: "she/her", + restricted: false, + source_id: 1, + starred_repos_count: 10, + username: "naomi", + visibility: "public", + website: "https://example.com", + }, + act_user_id: 1, + comment: { + assets: [], + body: "A comment", + created_at: "2023-01-01T00:00:00Z", + html_url: "https://example.com/comment", + id: 1, + issue_url: "https://example.com/issue", + original_author: "naomi", + original_author_id: 1, + pull_request_url: "https://example.com/pr", + updated_at: "2023-01-01T00:00:00Z", + user: null, + }, + comment_id: 1, + content: "Activity content", + created: "2023-01-01T00:00:00Z", + id: 1, + is_private: false, + op_type: "create", + ref_name: "main", + repo: { + allow_fast_forward_only_merge: false, + allow_merge_commits: true, + allow_rebase: true, + allow_rebase_explicit: true, + allow_rebase_update: true, + allow_squash_merge: true, + archived: false, + archived_at: "2023-01-01T00:00:00Z", + avatar_url: "https://example.com/avatar.png", + clone_url: "https://example.com/repo.git", + created_at: "2023-01-01T00:00:00Z", + default_allow_maintainer_edit: true, + default_branch: "main", + default_delete_branch_after_merge: true, + default_merge_style: "merge", + description: "A repository", + empty: false, + external_tracker: { + external_tracker_format: "format", + external_tracker_regexp_pattern: "pattern", + external_tracker_style: "style", + external_tracker_url: "https://example.com/tracker", + }, + fork: false, + forks_count: 5, + full_name: "naomi/repo", + globally_editable_wiki: false, + has_actions: true, + has_issues: true, + has_packages: true, + has_projects: true, + has_pull_requests: true, + has_releases: true, + has_wiki: true, + html_url: "https://example.com/repo", + id: 1, + ignore_whitespace_conflicts: false, + internal: false, + language: "TypeScript", + languages_url: "https://example.com/languages", + link: "https://example.com/repo", + mirror: false, + mirror_interval: "24h", + mirror_updated: "2023-01-01T00:00:00Z", + name: "repo", + object_format_name: "format", + open_issues_count: 1, + open_pr_counter: 0, + original_url: "https://example.com/repo", + owner: { + active: true, + avatar_url: "https://example.com/avatar.png", + created: "2023-01-01T00:00:00Z", + description: "Developer", + email: "naomi@example.com", + followers_count: 100, + following_count: 50, + full_name: "Naomi Carrigan", + html_url: "https://example.com", + id: 1, + is_admin: false, + language: "en", + last_login: "2023-01-01T00:00:00Z", + location: "Earth", + login: "naomi", + login_name: "naomi", + prohibit_login: false, + pronouns: "she/her", + restricted: false, + source_id: 1, + starred_repos_count: 10, + username: "naomi", + visibility: "public", + website: "https://example.com", + }, + parent: null, + permissions: { + admin: true, + pull: true, + push: true, + }, + private: false, + release_counter: 0, + repo_transfer: null, + size: 100, + ssh_url: "git@example.com:repo.git", + stars_count: 10, + template: false, + topics: [], + updated_at: "2023-01-01T00:00:00Z", + url: "https://example.com/repo", + watchers_count: 3, + website: "https://example.com", + wiki_branch: "main", + }, + repo_id: 1, + user_id: 1, + }, + ]; + + vi.spyOn(global, "fetch").mockResolvedValue({ + json: () => { + return Promise.resolve(mockResponse); + }, + }); + + const data = await getCodebergData(); + expect(data, "did not have correct payload"). + toStrictEqual(mockResponse); + }); + + it("should return cached data if not stale", async() => { + expect.assertions(3); + const mockResponse = [ + { + act_user: { + active: true, + avatar_url: "https://example.com/avatar.png", + created: "2023-01-01T00:00:00Z", + description: "Developer", + email: "naomi@example.com", + followers_count: 100, + following_count: 50, + full_name: "Naomi Carrigan", + html_url: "https://example.com", + id: 1, + is_admin: false, + language: "en", + last_login: "2023-01-01T00:00:00Z", + location: "Earth", + login: "naomi", + login_name: "naomi", + prohibit_login: false, + pronouns: "she/her", + restricted: false, + source_id: 1, + starred_repos_count: 10, + username: "naomi", + visibility: "public", + website: "https://example.com", + }, + act_user_id: 1, + comment: { + assets: [], + body: "A comment", + created_at: "2023-01-01T00:00:00Z", + html_url: "https://example.com/comment", + id: 1, + issue_url: "https://example.com/issue", + original_author: "naomi", + original_author_id: 1, + pull_request_url: "https://example.com/pr", + updated_at: "2023-01-01T00:00:00Z", + user: null, + }, + comment_id: 1, + content: "Activity content", + created: "2023-01-01T00:00:00Z", + id: 1, + is_private: false, + op_type: "create", + ref_name: "main", + repo: { + allow_fast_forward_only_merge: false, + allow_merge_commits: true, + allow_rebase: true, + allow_rebase_explicit: true, + allow_rebase_update: true, + allow_squash_merge: true, + archived: false, + archived_at: "2023-01-01T00:00:00Z", + avatar_url: "https://example.com/avatar.png", + clone_url: "https://example.com/repo.git", + created_at: "2023-01-01T00:00:00Z", + default_allow_maintainer_edit: true, + default_branch: "main", + default_delete_branch_after_merge: true, + default_merge_style: "merge", + description: "A repository", + empty: false, + external_tracker: { + external_tracker_format: "format", + external_tracker_regexp_pattern: "pattern", + external_tracker_style: "style", + external_tracker_url: "https://example.com/tracker", + }, + fork: false, + forks_count: 5, + full_name: "naomi/repo", + globally_editable_wiki: false, + has_actions: true, + has_issues: true, + has_packages: true, + has_projects: true, + has_pull_requests: true, + has_releases: true, + has_wiki: true, + html_url: "https://example.com/repo", + id: 1, + ignore_whitespace_conflicts: false, + internal: false, + language: "TypeScript", + languages_url: "https://example.com/languages", + link: "https://example.com/repo", + mirror: false, + mirror_interval: "24h", + mirror_updated: "2023-01-01T00:00:00Z", + name: "repo", + object_format_name: "format", + open_issues_count: 1, + open_pr_counter: 0, + original_url: "https://example.com/repo", + owner: { + active: true, + avatar_url: "https://example.com/avatar.png", + created: "2023-01-01T00:00:00Z", + description: "Developer", + email: "naomi@example.com", + followers_count: 100, + following_count: 50, + full_name: "Naomi Carrigan", + html_url: "https://example.com", + id: 1, + is_admin: false, + language: "en", + last_login: "2023-01-01T00:00:00Z", + location: "Earth", + login: "naomi", + login_name: "naomi", + prohibit_login: false, + pronouns: "she/her", + restricted: false, + source_id: 1, + starred_repos_count: 10, + username: "naomi", + visibility: "public", + website: "https://example.com", + }, + parent: null, + permissions: { + admin: true, + pull: true, + push: true, + }, + private: false, + release_counter: 0, + repo_transfer: null, + size: 100, + ssh_url: "git@example.com:repo.git", + stars_count: 10, + template: false, + topics: [], + updated_at: "2023-01-01T00:00:00Z", + url: "https://example.com/repo", + watchers_count: 3, + website: "https://example.com", + wiki_branch: "main", + }, + repo_id: 1, + user_id: 1, + }, + ]; + + vi.spyOn(global, "fetch").mockResolvedValue({ + json: () => { + return Promise.resolve(mockResponse); + }, + }); + + const data = await getCodebergData(); + expect(data, "did not have correct payload"). + toStrictEqual(mockResponse); + + // Call again to check if cached data is returned + const cachedData = await getCodebergData(); + expect(cachedData, "did not cache correct payload"). + toStrictEqual(mockResponse); + expect(global.fetch, "did not hit cache").not.toHaveBeenCalled(); + }); +}); diff --git a/test/lib/github.spec.ts b/test/lib/github.spec.ts new file mode 100644 index 0000000..e56bc4c --- /dev/null +++ b/test/lib/github.spec.ts @@ -0,0 +1,343 @@ +/** + * @copyright nhcarrigan + * @license Naomi's Public License + * @author Naomi Carrigan + */ +import { describe, it, expect, vi, beforeEach } from "vitest"; +import { getGithubData } from "../../src/lib/github"; + +describe("github", () => { + beforeEach(() => { + vi.resetAllMocks(); + }); + + it("should fetch and cache activities", async() => { + expect.assertions(1); + const mockResponse = [ + { + act_user: { + active: true, + avatar_url: "https://example.com/avatar.png", + created: "2023-01-01T00:00:00Z", + description: "Developer", + email: "naomi@example.com", + followers_count: 100, + following_count: 50, + full_name: "Naomi Carrigan", + html_url: "https://example.com", + id: 1, + is_admin: false, + language: "en", + last_login: "2023-01-01T00:00:00Z", + location: "Earth", + login: "naomi", + login_name: "naomi", + prohibit_login: false, + pronouns: "she/her", + restricted: false, + source_id: 1, + starred_repos_count: 10, + username: "naomi", + visibility: "public", + website: "https://example.com", + }, + act_user_id: 1, + comment: { + assets: [], + body: "A comment", + created_at: "2023-01-01T00:00:00Z", + html_url: "https://example.com/comment", + id: 1, + issue_url: "https://example.com/issue", + original_author: "naomi", + original_author_id: 1, + pull_request_url: "https://example.com/pr", + updated_at: "2023-01-01T00:00:00Z", + user: null, + }, + comment_id: 1, + content: "Activity content", + created: "2023-01-01T00:00:00Z", + id: 1, + is_private: false, + op_type: "create", + ref_name: "main", + repo: { + allow_fast_forward_only_merge: false, + allow_merge_commits: true, + allow_rebase: true, + allow_rebase_explicit: true, + allow_rebase_update: true, + allow_squash_merge: true, + archived: false, + archived_at: "2023-01-01T00:00:00Z", + avatar_url: "https://example.com/avatar.png", + clone_url: "https://example.com/repo.git", + created_at: "2023-01-01T00:00:00Z", + default_allow_maintainer_edit: true, + default_branch: "main", + default_delete_branch_after_merge: true, + default_merge_style: "merge", + description: "A repository", + empty: false, + external_tracker: { + external_tracker_format: "format", + external_tracker_regexp_pattern: "pattern", + external_tracker_style: "style", + external_tracker_url: "https://example.com/tracker", + }, + fork: false, + forks_count: 5, + full_name: "naomi/repo", + globally_editable_wiki: false, + has_actions: true, + has_issues: true, + has_packages: true, + has_projects: true, + has_pull_requests: true, + has_releases: true, + has_wiki: true, + html_url: "https://example.com/repo", + id: 1, + ignore_whitespace_conflicts: false, + internal: false, + language: "TypeScript", + languages_url: "https://example.com/languages", + link: "https://example.com/repo", + mirror: false, + mirror_interval: "24h", + mirror_updated: "2023-01-01T00:00:00Z", + name: "repo", + object_format_name: "format", + open_issues_count: 1, + open_pr_counter: 0, + original_url: "https://example.com/repo", + owner: { + active: true, + avatar_url: "https://example.com/avatar.png", + created: "2023-01-01T00:00:00Z", + description: "Developer", + email: "naomi@example.com", + followers_count: 100, + following_count: 50, + full_name: "Naomi Carrigan", + html_url: "https://example.com", + id: 1, + is_admin: false, + language: "en", + last_login: "2023-01-01T00:00:00Z", + location: "Earth", + login: "naomi", + login_name: "naomi", + prohibit_login: false, + pronouns: "she/her", + restricted: false, + source_id: 1, + starred_repos_count: 10, + username: "naomi", + visibility: "public", + website: "https://example.com", + }, + parent: null, + permissions: { + admin: true, + pull: true, + push: true, + }, + private: false, + release_counter: 0, + repo_transfer: null, + size: 100, + ssh_url: "git@example.com:repo.git", + stars_count: 10, + template: false, + topics: [], + updated_at: "2023-01-01T00:00:00Z", + url: "https://example.com/repo", + watchers_count: 3, + website: "https://example.com", + wiki_branch: "main", + }, + repo_id: 1, + user_id: 1, + }, + ]; + + vi.spyOn(global, "fetch").mockResolvedValue({ + json: () => { + return Promise.resolve(mockResponse); + }, + }); + + const data = await getGithubData(); + expect(data, "did not have correct payload").toStrictEqual(mockResponse); + }); + + it("should return cached data if not stale", async() => { + expect.assertions(3); + const mockResponse = [ + { + act_user: { + active: true, + avatar_url: "https://example.com/avatar.png", + created: "2023-01-01T00:00:00Z", + description: "Developer", + email: "naomi@example.com", + followers_count: 100, + following_count: 50, + full_name: "Naomi Carrigan", + html_url: "https://example.com", + id: 1, + is_admin: false, + language: "en", + last_login: "2023-01-01T00:00:00Z", + location: "Earth", + login: "naomi", + login_name: "naomi", + prohibit_login: false, + pronouns: "she/her", + restricted: false, + source_id: 1, + starred_repos_count: 10, + username: "naomi", + visibility: "public", + website: "https://example.com", + }, + act_user_id: 1, + comment: { + assets: [], + body: "A comment", + created_at: "2023-01-01T00:00:00Z", + html_url: "https://example.com/comment", + id: 1, + issue_url: "https://example.com/issue", + original_author: "naomi", + original_author_id: 1, + pull_request_url: "https://example.com/pr", + updated_at: "2023-01-01T00:00:00Z", + user: null, + }, + comment_id: 1, + content: "Activity content", + created: "2023-01-01T00:00:00Z", + id: 1, + is_private: false, + op_type: "create", + ref_name: "main", + repo: { + allow_fast_forward_only_merge: false, + allow_merge_commits: true, + allow_rebase: true, + allow_rebase_explicit: true, + allow_rebase_update: true, + allow_squash_merge: true, + archived: false, + archived_at: "2023-01-01T00:00:00Z", + avatar_url: "https://example.com/avatar.png", + clone_url: "https://example.com/repo.git", + created_at: "2023-01-01T00:00:00Z", + default_allow_maintainer_edit: true, + default_branch: "main", + default_delete_branch_after_merge: true, + default_merge_style: "merge", + description: "A repository", + empty: false, + external_tracker: { + external_tracker_format: "format", + external_tracker_regexp_pattern: "pattern", + external_tracker_style: "style", + external_tracker_url: "https://example.com/tracker", + }, + fork: false, + forks_count: 5, + full_name: "naomi/repo", + globally_editable_wiki: false, + has_actions: true, + has_issues: true, + has_packages: true, + has_projects: true, + has_pull_requests: true, + has_releases: true, + has_wiki: true, + html_url: "https://example.com/repo", + id: 1, + ignore_whitespace_conflicts: false, + internal: false, + language: "TypeScript", + languages_url: "https://example.com/languages", + link: "https://example.com/repo", + mirror: false, + mirror_interval: "24h", + mirror_updated: "2023-01-01T00:00:00Z", + name: "repo", + object_format_name: "format", + open_issues_count: 1, + open_pr_counter: 0, + original_url: "https://example.com/repo", + owner: { + active: true, + avatar_url: "https://example.com/avatar.png", + created: "2023-01-01T00:00:00Z", + description: "Developer", + email: "naomi@example.com", + followers_count: 100, + following_count: 50, + full_name: "Naomi Carrigan", + html_url: "https://example.com", + id: 1, + is_admin: false, + language: "en", + last_login: "2023-01-01T00:00:00Z", + location: "Earth", + login: "naomi", + login_name: "naomi", + prohibit_login: false, + pronouns: "she/her", + restricted: false, + source_id: 1, + starred_repos_count: 10, + username: "naomi", + visibility: "public", + website: "https://example.com", + }, + parent: null, + permissions: { + admin: true, + pull: true, + push: true, + }, + private: false, + release_counter: 0, + repo_transfer: null, + size: 100, + ssh_url: "git@example.com:repo.git", + stars_count: 10, + template: false, + topics: [], + updated_at: "2023-01-01T00:00:00Z", + url: "https://example.com/repo", + watchers_count: 3, + website: "https://example.com", + wiki_branch: "main", + }, + repo_id: 1, + user_id: 1, + }, + ]; + + vi.spyOn(global, "fetch").mockResolvedValue({ + json: () => { + return Promise.resolve(mockResponse); + }, + }); + + const data = await getGithubData(); + expect(data, "did not have correct payload").toStrictEqual(mockResponse); + + // Call again to check if cached data is returned + const cachedData = await getGithubData(); + expect(cachedData, "did not cache correct payload"). + toStrictEqual(mockResponse); + expect(global.fetch, "did not hit cache").not.toHaveBeenCalled(); + }); +}); diff --git a/tsconfig.json b/tsconfig.json index 8ecb82f..85ef36e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,11 +18,6 @@ "name": "next" } ], - "paths": { - "@/*": [ - "./src/*" - ] - }, "allowJs": true }, "include": [ @@ -32,6 +27,7 @@ ".next/types/**/*.ts" ], "exclude": [ - "node_modules" + "node_modules", + "vitest.config.ts" ] } diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..32fce47 --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,20 @@ +import { coverageConfigDefaults, defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + coverage: { + provider: "istanbul", + reporter: ["text", "html"], + all: true, + allowExternal: true, + thresholds: { + lines: 100, + statements: 100, + functions: 100, + branches: 100 + }, + extension: [".ts"], + exclude: [...coverageConfigDefaults.exclude, "tailwind.config.ts"] + } + } +}); \ No newline at end of file