From ac029210cee21e129577e03f47959a58b949bc55 Mon Sep 17 00:00:00 2001 From: BHyeonKim Date: Mon, 26 May 2025 14:47:48 +0900 Subject: [PATCH 01/40] =?UTF-8?q?config:=20=EC=95=84=ED=82=A4=ED=85=8D?= =?UTF-8?q?=EC=B3=90=20=EB=B3=80=EA=B2=BD=EC=97=90=20=EB=94=B0=EB=A5=B8=20?= =?UTF-8?q?=EC=8A=A4=ED=81=AC=EB=A6=BD=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy.yml | 17 +++++++++++++++-- .github/workflows/start-services.yml | 27 --------------------------- 2 files changed, 15 insertions(+), 29 deletions(-) delete mode 100644 .github/workflows/start-services.yml diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index e4cc15b..6d165af 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,6 +1,6 @@ name: Deploy remix app -run-name: Deploy remix app to docker hub by ${{github.actor}} +run-name: Deploy remix app to docker hub and start services by ${{github.actor}} on: push: @@ -19,6 +19,8 @@ jobs: echo "VITE_OAUTH_URL=${{ secrets.VITE_OAUTH_URL }}" > .env echo "VITE_API_URL=${{ secrets.VITE_API_URL }}" >> .env echo "VITE_STOMP_URL=${{ secrets.VITE_STOMP_URL }}" >> .env + echo "VITE_AI_URL=${{ secrets.VITE_AI_URL }}" >> .env + - name: Build image run: | docker build -t ${{secrets.DOCKER_USERNAME}}/if-fe:latest . @@ -29,4 +31,15 @@ jobs: password: ${{secrets.DOCKER_TOKEN}} - name: Push image to Docker hub run: | - docker push ${{secrets.DOCKER_USERNAME}}/if-fe:latest \ No newline at end of file + docker push ${{secrets.DOCKER_USERNAME}}/if-fe:latest + - name: Connect to cloud server + uses: appleboy/ssh-action@v1.2.2 + with: + host: ${{secrets.FRONTEND_HOST}} + username: ${{secrets.FRONTEND_USERNAME}} + key: ${{secrets.FRONTEND_SECRET_KEY}} + port: ${{secrets.FRONTEND_PORT}} + script: | + cd ~ + docker pull ${{secrets.DOCKER_USERNAME}}/if-fe:latest + docker run -d --name frontend -p 3000:3000 ${{secrets.DOCKER_USERNAME}}/if-fe:latest \ No newline at end of file diff --git a/.github/workflows/start-services.yml b/.github/workflows/start-services.yml deleted file mode 100644 index 9e69bd2..0000000 --- a/.github/workflows/start-services.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Start services - -run-name: Start containers in cloud service by ${{ github.actor }} - -on: - workflow_run: - workflows: [Deploy remix app] - types: - - completed - -jobs: - start-services: - runs-on: ubuntu-latest - steps: - - name: Connect to cloud server - uses: appleboy/ssh-action@v1.2.2 - with: - host: ${{secrets.CLOUD_HOST}} - username: ${{secrets.CLOUD_USERNAME}} - key: ${{secrets.CLOUD_SECRET_KEY}} - port: ${{secrets.CLOUD_PORT}} - script: | - cd ~ - sudo docker compose down --volumes - sudo docker container prune -f - sudo docker image prune -a -f - sudo docker compose up -d From 6f4704b8e3e3382523c26f5aefa229831d832868 Mon Sep 17 00:00:00 2001 From: BHyeonKim Date: Mon, 26 May 2025 14:55:11 +0900 Subject: [PATCH 02/40] =?UTF-8?q?config:=20=EA=B8=B0=EC=A1=B4=20=EC=BB=A8?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EB=84=88=20=EB=B0=8F=20=EC=9D=B4=EB=AF=B8?= =?UTF-8?q?=EC=A7=80=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit issue: #19 --- .github/workflows/deploy.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 6d165af..98ebda9 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -41,5 +41,8 @@ jobs: port: ${{secrets.FRONTEND_PORT}} script: | cd ~ + docker stop frontend || true + docker rm frontend || true + docker rmi ${{secrets.DOCKER_USERNAME}}/if-fe:latest || true docker pull ${{secrets.DOCKER_USERNAME}}/if-fe:latest docker run -d --name frontend -p 3000:3000 ${{secrets.DOCKER_USERNAME}}/if-fe:latest \ No newline at end of file From d812fdb34d597aa1b5d291c3cdde64663357ae3a Mon Sep 17 00:00:00 2001 From: BHyeonKim Date: Wed, 28 May 2025 13:36:38 +0900 Subject: [PATCH 03/40] =?UTF-8?q?config:=20=EB=8B=A8=EC=9C=84=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=ED=99=98=EA=B2=BD=20=EA=B5=AC=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit issue: #19 --- .gitignore | 1 + biome.json | 6 +- package.json | 15 +- setupTests.ts | 1 + vitest.config.ts | 16 ++ yarn.lock | 722 ++++++++++++++++++++++++++++++++++++++++++++++- 6 files changed, 743 insertions(+), 18 deletions(-) create mode 100644 setupTests.ts create mode 100644 vitest.config.ts diff --git a/.gitignore b/.gitignore index 43e0676..0c55d88 100644 --- a/.gitignore +++ b/.gitignore @@ -27,4 +27,5 @@ dist-ssr /.react-router/ /build/ +coverage .env \ No newline at end of file diff --git a/biome.json b/biome.json index 058c00e..534c288 100644 --- a/biome.json +++ b/biome.json @@ -47,7 +47,7 @@ "options": { "strictCase": false, "requireAscii": true, - "filenameCases": ["camelCase", "export"] + "filenameCases": ["PascalCase", "camelCase", "export"] } } }, @@ -60,8 +60,8 @@ { "include": [ "test/**/*", - "*.test.{ts,tsx,js,jsx}", - "*.spec.{ts,tsx,js,jsx}", + "**/*.test.{ts,tsx,js,jsx}", + "**/*.spec.{ts,tsx,js,jsx}", "src/app/routes/*" ], "linter": { diff --git a/package.json b/package.json index e6af6a0..184046f 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,8 @@ "build": "react-router build", "start": "react-router-serve ./build/server/index.js", "typecheck": "react-router typegen && tsc", + "test": "vitest", + "coverage": "vitest run --coverage", "lint": "biome check --write --unsafe .", "postinstall": "husky" }, @@ -38,23 +40,28 @@ "@react-router/dev": "^7.5.3", "@svgr/rollup": "^8.1.0", "@tailwindcss/vite": "^4.1.5", + "@testing-library/dom": "^10.4.0", + "@testing-library/jest-dom": "^6.6.3", + "@testing-library/react": "^16.3.0", + "@testing-library/user-event": "^14.6.1", "@types/node": "^22.15.3", "@types/react": "^19.1.2", "@types/react-dom": "^19.1.3", + "@vitest/coverage-v8": "^3.1.4", "globals": "^16.0.0", "husky": "^9.1.7", + "jsdom": "^26.1.0", "lint-staged": "^15.5.1", "msw": "^2.8.2", "tailwindcss": "^4.1.5", "typescript": "~5.7.2", "vite": "^6.3.1", "vite-plugin-svgr": "^4.3.0", - "vite-tsconfig-paths": "^5.1.4" + "vite-tsconfig-paths": "^5.1.4", + "vitest": "^3.1.4" }, "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e", "msw": { - "workerDirectory": [ - "public" - ] + "workerDirectory": ["public"] } } diff --git a/setupTests.ts b/setupTests.ts new file mode 100644 index 0000000..7b0828b --- /dev/null +++ b/setupTests.ts @@ -0,0 +1 @@ +import '@testing-library/jest-dom'; diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..3f0b619 --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,16 @@ +/// +import { defineConfig } from 'vite'; + +export default defineConfig({ + test: { + environment: 'jsdom', + globals: true, + setupFiles: ['./setupTests.ts'], + coverage: { + provider: 'v8', + reporter: ['html', 'json', 'text'], + include: ['src/**/*'], + exclude: ['**/*.test.{ts,tsx}', '**/__tests__/**', '**/types/**'], + }, + }, +}); diff --git a/yarn.lock b/yarn.lock index 92278d1..2e02f88 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,11 @@ # yarn lockfile v1 +"@adobe/css-tools@^4.4.0": + version "4.4.3" + resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.4.3.tgz#beebbefb0264fdeb32d3052acae0e0d94315a9a2" + integrity sha512-VQKMkwriZbaOgVCby1UDY/LDk5fIjhQicCvVPFqfe+69fWaPWydbWJ3wRt59/YzIwda1I81loas3oCoHxnqvdA== + "@amcharts/amcharts5@^5.12.1": version "5.12.1" resolved "https://registry.yarnpkg.com/@amcharts/amcharts5/-/amcharts5-5.12.1.tgz#c416a98c7e5b19bcf1f7e91600ea29a2fe578325" @@ -33,7 +38,7 @@ svg-arc-to-cubic-bezier "^3.2.0" tslib "^2.2.0" -"@ampproject/remapping@^2.2.0": +"@ampproject/remapping@^2.2.0", "@ampproject/remapping@^2.3.0": version "2.3.0" resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== @@ -41,7 +46,18 @@ "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.24" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.27.1": +"@asamuzakjp/css-color@^3.1.2": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@asamuzakjp/css-color/-/css-color-3.2.0.tgz#cc42f5b85c593f79f1fa4f25d2b9b321e61d1794" + integrity sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw== + dependencies: + "@csstools/css-calc" "^2.1.3" + "@csstools/css-color-parser" "^3.0.9" + "@csstools/css-parser-algorithms" "^3.0.4" + "@csstools/css-tokenizer" "^3.0.3" + lru-cache "^10.4.3" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.27.1": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be" integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg== @@ -240,6 +256,13 @@ dependencies: "@babel/types" "^7.27.1" +"@babel/parser@^7.25.4": + version "7.27.3" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.27.3.tgz#1b7533f0d908ad2ac545c4d05cbe2fb6dc8cfaaf" + integrity sha512-xyYxRj6+tLNDTWi0KCBcZ9V7yg3/lwL9DWh9Uwh/RIVlIfFidggcgxKX3GCXwCiswwcGRawBKbEg2LG/Y8eJhw== + dependencies: + "@babel/types" "^7.27.3" + "@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.27.1": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.27.1.tgz#61dd8a8e61f7eb568268d1b5f129da3eee364bf9" @@ -872,6 +895,11 @@ "@babel/plugin-transform-modules-commonjs" "^7.27.1" "@babel/plugin-transform-typescript" "^7.27.1" +"@babel/runtime@^7.12.5": + version "7.27.3" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.27.3.tgz#10491113799fb8d77e1d9273384d5d68deeea8f6" + integrity sha512-7EYtGezsdiDMyY80+65EzwiGmcJqpmcZCojSXaRgdrBaGtWTgDZKq69cPIVped6MkIM78cTQ2GOiEYjwOlG4xw== + "@babel/template@^7.27.1": version "7.27.2" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.2.tgz#fa78ceed3c4e7b63ebf6cb39e5852fca45f6809d" @@ -902,6 +930,19 @@ "@babel/helper-string-parser" "^7.27.1" "@babel/helper-validator-identifier" "^7.27.1" +"@babel/types@^7.25.4", "@babel/types@^7.27.3": + version "7.27.3" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.27.3.tgz#c0257bedf33aad6aad1f406d35c44758321eb3ec" + integrity sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw== + dependencies: + "@babel/helper-string-parser" "^7.27.1" + "@babel/helper-validator-identifier" "^7.27.1" + +"@bcoe/v8-coverage@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz#bbe12dca5b4ef983a0d0af4b07b9bc90ea0ababa" + integrity sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA== + "@biomejs/biome@1.9.4": version "1.9.4" resolved "https://registry.yarnpkg.com/@biomejs/biome/-/biome-1.9.4.tgz#89766281cbc3a0aae865a7ff13d6aaffea2842bf" @@ -978,6 +1019,34 @@ "@types/tough-cookie" "^4.0.5" tough-cookie "^4.1.4" +"@csstools/color-helpers@^5.0.2": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@csstools/color-helpers/-/color-helpers-5.0.2.tgz#82592c9a7c2b83c293d9161894e2a6471feb97b8" + integrity sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA== + +"@csstools/css-calc@^2.1.3", "@csstools/css-calc@^2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@csstools/css-calc/-/css-calc-2.1.4.tgz#8473f63e2fcd6e459838dd412401d5948f224c65" + integrity sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ== + +"@csstools/css-color-parser@^3.0.9": + version "3.0.10" + resolved "https://registry.yarnpkg.com/@csstools/css-color-parser/-/css-color-parser-3.0.10.tgz#79fc68864dd43c3b6782d2b3828bc0fa9d085c10" + integrity sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg== + dependencies: + "@csstools/color-helpers" "^5.0.2" + "@csstools/css-calc" "^2.1.4" + +"@csstools/css-parser-algorithms@^3.0.4": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz#5755370a9a29abaec5515b43c8b3f2cf9c2e3076" + integrity sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ== + +"@csstools/css-tokenizer@^3.0.3": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz#333fedabc3fd1a8e5d0100013731cf19e6a8c5d3" + integrity sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw== + "@emnapi/core@^1.4.0", "@emnapi/core@^1.4.3": version "1.4.3" resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-1.4.3.tgz#9ac52d2d5aea958f67e52c40a065f51de59b77d6" @@ -1207,6 +1276,11 @@ wrap-ansi "^8.1.0" wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" +"@istanbuljs/schema@^0.1.2": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== + "@jridgewell/gen-mapping@^0.3.5": version "0.3.8" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz#4f0e06362e01362f823d348f1872b08f666d8142" @@ -1226,12 +1300,12 @@ resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== -"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== -"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": +"@jridgewell/trace-mapping@^0.3.23", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": version "0.3.25" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== @@ -1717,6 +1791,45 @@ "@tailwindcss/oxide" "4.1.5" tailwindcss "4.1.5" +"@testing-library/dom@^10.4.0": + version "10.4.0" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-10.4.0.tgz#82a9d9462f11d240ecadbf406607c6ceeeff43a8" + integrity sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/runtime" "^7.12.5" + "@types/aria-query" "^5.0.1" + aria-query "5.3.0" + chalk "^4.1.0" + dom-accessibility-api "^0.5.9" + lz-string "^1.5.0" + pretty-format "^27.0.2" + +"@testing-library/jest-dom@^6.6.3": + version "6.6.3" + resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-6.6.3.tgz#26ba906cf928c0f8172e182c6fe214eb4f9f2bd2" + integrity sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA== + dependencies: + "@adobe/css-tools" "^4.4.0" + aria-query "^5.0.0" + chalk "^3.0.0" + css.escape "^1.5.1" + dom-accessibility-api "^0.6.3" + lodash "^4.17.21" + redent "^3.0.0" + +"@testing-library/react@^16.3.0": + version "16.3.0" + resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-16.3.0.tgz#3a85bb9bdebf180cd76dba16454e242564d598a6" + integrity sha512-kFSyxiEDwv1WLl2fgsq6pPBbw5aWKrsY2/noi1Id0TK0UParSF62oFQFGHXIyaG4pp2tEub/Zlel+fjjZILDsw== + dependencies: + "@babel/runtime" "^7.12.5" + +"@testing-library/user-event@^14.6.1": + version "14.6.1" + resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-14.6.1.tgz#13e09a32d7a8b7060fe38304788ebf4197cd2149" + integrity sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw== + "@trysound/sax@0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" @@ -1729,6 +1842,11 @@ dependencies: tslib "^2.4.0" +"@types/aria-query@^5.0.1": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.4.tgz#1a31c3d378850d2778dabb6374d036dcba4ba708" + integrity sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw== + "@types/cookie@^0.6.0": version "0.6.0" resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.6.0.tgz#eac397f28bf1d6ae0ae081363eca2f425bedf0d5" @@ -2017,6 +2135,83 @@ resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304" integrity sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA== +"@vitest/coverage-v8@^3.1.4": + version "3.1.4" + resolved "https://registry.yarnpkg.com/@vitest/coverage-v8/-/coverage-v8-3.1.4.tgz#faffd0d22795938b69aa4fedc78622bce299ec26" + integrity sha512-G4p6OtioySL+hPV7Y6JHlhpsODbJzt1ndwHAFkyk6vVjpK03PFsKnauZIzcd0PrK4zAbc5lc+jeZ+eNGiMA+iw== + dependencies: + "@ampproject/remapping" "^2.3.0" + "@bcoe/v8-coverage" "^1.0.2" + debug "^4.4.0" + istanbul-lib-coverage "^3.2.2" + istanbul-lib-report "^3.0.1" + istanbul-lib-source-maps "^5.0.6" + istanbul-reports "^3.1.7" + magic-string "^0.30.17" + magicast "^0.3.5" + std-env "^3.9.0" + test-exclude "^7.0.1" + tinyrainbow "^2.0.0" + +"@vitest/expect@3.1.4": + version "3.1.4" + resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-3.1.4.tgz#837651a71682e3611c3df7b58b157ba485bd8029" + integrity sha512-xkD/ljeliyaClDYqHPNCiJ0plY5YIcM0OlRiZizLhlPmpXWpxnGMyTZXOHFhFeG7w9P5PBeL4IdtJ/HeQwTbQA== + dependencies: + "@vitest/spy" "3.1.4" + "@vitest/utils" "3.1.4" + chai "^5.2.0" + tinyrainbow "^2.0.0" + +"@vitest/mocker@3.1.4": + version "3.1.4" + resolved "https://registry.yarnpkg.com/@vitest/mocker/-/mocker-3.1.4.tgz#73441022b86c7299bfbd11a9fb2e99a7ddc2bb0e" + integrity sha512-8IJ3CvwtSw/EFXqWFL8aCMu+YyYXG2WUSrQbViOZkWTKTVicVwZ/YiEZDSqD00kX+v/+W+OnxhNWoeVKorHygA== + dependencies: + "@vitest/spy" "3.1.4" + estree-walker "^3.0.3" + magic-string "^0.30.17" + +"@vitest/pretty-format@3.1.4", "@vitest/pretty-format@^3.1.4": + version "3.1.4" + resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-3.1.4.tgz#da3e98c250cde3ce39fe8e709339814607b185e8" + integrity sha512-cqv9H9GvAEoTaoq+cYqUTCGscUjKqlJZC7PRwY5FMySVj5J+xOm1KQcCiYHJOEzOKRUhLH4R2pTwvFlWCEScsg== + dependencies: + tinyrainbow "^2.0.0" + +"@vitest/runner@3.1.4": + version "3.1.4" + resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-3.1.4.tgz#19fa16eb397f5325b99baca48c2bca6cadd098fa" + integrity sha512-djTeF1/vt985I/wpKVFBMWUlk/I7mb5hmD5oP8K9ACRmVXgKTae3TUOtXAEBfslNKPzUQvnKhNd34nnRSYgLNQ== + dependencies: + "@vitest/utils" "3.1.4" + pathe "^2.0.3" + +"@vitest/snapshot@3.1.4": + version "3.1.4" + resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-3.1.4.tgz#7897d4960a3cf617fb0f17e182cc15c7e3e4ed3f" + integrity sha512-JPHf68DvuO7vilmvwdPr9TS0SuuIzHvxeaCkxYcCD4jTk67XwL45ZhEHFKIuCm8CYstgI6LZ4XbwD6ANrwMpFg== + dependencies: + "@vitest/pretty-format" "3.1.4" + magic-string "^0.30.17" + pathe "^2.0.3" + +"@vitest/spy@3.1.4": + version "3.1.4" + resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-3.1.4.tgz#94bb566da7ef6deb7c4e1fd79b78f19aa5465b9f" + integrity sha512-Xg1bXhu+vtPXIodYN369M86K8shGLouNjoVI78g8iAq2rFoHFdajNvJJ5A/9bPMFcfQqdaCpOgWKEoMQg/s0Yg== + dependencies: + tinyspy "^3.0.2" + +"@vitest/utils@3.1.4": + version "3.1.4" + resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-3.1.4.tgz#f9f20d92f1384a9d66548c480885390760047b5e" + integrity sha512-yriMuO1cfFhmiGc8ataN51+9ooHRuURdfAZfwFd3usWynjzpLslZdYnRegTv32qdgtJTsj15FoeZe2g15fY1gg== + dependencies: + "@vitest/pretty-format" "3.1.4" + loupe "^3.1.3" + tinyrainbow "^2.0.0" + "@xstate/react@^5.0.4": version "5.0.4" resolved "https://registry.yarnpkg.com/@xstate/react/-/react-5.0.4.tgz#e04b5d558fd54e3d1704efb13b3ac22636d95e8e" @@ -2033,6 +2228,11 @@ accepts@~1.3.8: mime-types "~2.1.34" negotiator "0.6.3" +agent-base@^7.1.0, agent-base@^7.1.2: + version "7.1.3" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.3.tgz#29435eb821bc4194633a5b89e5bc4703bafc25a1" + integrity sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw== + ansi-escapes@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" @@ -2057,13 +2257,18 @@ ansi-regex@^6.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654" integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== -ansi-styles@^4.0.0: +ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: color-convert "^2.0.1" +ansi-styles@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== + ansi-styles@^6.0.0, ansi-styles@^6.1.0, ansi-styles@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" @@ -2079,11 +2284,28 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== +aria-query@5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.0.tgz#650c569e41ad90b51b3d7df5e5eed1c7549c103e" + integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A== + dependencies: + dequal "^2.0.3" + +aria-query@^5.0.0: + version "5.3.2" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.2.tgz#93f81a43480e33a338f19163a3d10a50c01dcd59" + integrity sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw== + array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== +assertion-error@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-2.0.1.tgz#f641a196b335690b1070bf00b6e7593fec190bf7" + integrity sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA== + babel-dead-code-elimination@^1.0.6: version "1.0.10" resolved "https://registry.yarnpkg.com/babel-dead-code-elimination/-/babel-dead-code-elimination-1.0.10.tgz#e230562b57bf72ff3de4639ac763ba54f15d37b0" @@ -2250,11 +2472,43 @@ caniuse-lite@^1.0.30001716: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001717.tgz#5d9fec5ce09796a1893013825510678928aca129" integrity sha512-auPpttCq6BDEG8ZAuHJIplGw6GODhjw+/11e7IjpnYCxZcW/ONgPs0KVBJ0d1bY3e2+7PRe5RCLyP+PfwVgkYw== +chai@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/chai/-/chai-5.2.0.tgz#1358ee106763624114addf84ab02697e411c9c05" + integrity sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw== + dependencies: + assertion-error "^2.0.1" + check-error "^2.1.1" + deep-eql "^5.0.1" + loupe "^3.1.0" + pathval "^2.0.0" + +chalk@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" + integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chalk@^4.1.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + chalk@^5.4.1: version "5.4.1" resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.4.1.tgz#1b48bf0963ec158dce2aacf69c093ae2dd2092d8" integrity sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w== +check-error@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-2.1.1.tgz#87eb876ae71ee388fa0471fe423f494be1d96ccc" + integrity sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw== + chokidar@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.3.tgz#7be37a4c03c9aee1ecfe862a4a23b2c70c205d30" @@ -2448,6 +2702,11 @@ css-what@^6.1.0: resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== +css.escape@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb" + integrity sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg== + csso@^5.0.5: version "5.0.5" resolved "https://registry.yarnpkg.com/csso/-/csso-5.0.5.tgz#f9b7fe6cc6ac0b7d90781bb16d5e9874303e2ca6" @@ -2455,6 +2714,14 @@ csso@^5.0.5: dependencies: css-tree "~2.2.0" +cssstyle@^4.2.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-4.3.1.tgz#68a3c9f5a70aa97d5a6ebecc9805e511fc022eb8" + integrity sha512-ZgW+Jgdd7i52AaLYCriF8Mxqft0gD/R9i9wi6RWBhs1pqdPEzPjym7rvRKi397WmQFf3SlyUsszhw+VVCbx79Q== + dependencies: + "@asamuzakjp/css-color" "^3.1.2" + rrweb-cssom "^0.8.0" + csstype@^3.0.2: version "3.1.3" resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" @@ -2771,6 +3038,14 @@ d3@^7.0.0: d3-transition "3" d3-zoom "3" +data-urls@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-5.0.0.tgz#2f76906bce1824429ffecb6920f45a0b30f00dde" + integrity sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg== + dependencies: + whatwg-mimetype "^4.0.0" + whatwg-url "^14.0.0" + debug@2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -2778,6 +3053,13 @@ debug@2.6.9: dependencies: ms "2.0.0" +debug@4, debug@^4.3.4: + version "4.4.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.1.tgz#e5a8bc6cbc4c6cd3e64308b0693a3d4fa550189b" + integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ== + dependencies: + ms "^2.1.3" + debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" @@ -2785,11 +3067,21 @@ debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.4.0: dependencies: ms "^2.1.3" +decimal.js@^10.5.0: + version "10.5.0" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.5.0.tgz#0f371c7cf6c4898ce0afb09836db73cd82010f22" + integrity sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw== + dedent@^1.5.3: version "1.6.0" resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.6.0.tgz#79d52d6389b1ffa67d2bcef59ba51847a9d503b2" integrity sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA== +deep-eql@^5.0.1: + version "5.0.2" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-5.0.2.tgz#4b756d8d770a9257300825d52a2c2cff99c3a341" + integrity sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q== + deep-equal@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.2.tgz#78a561b7830eef3134c7f6f3a3d6af272a678761" @@ -2837,6 +3129,11 @@ depd@2.0.0, depd@~2.0.0: resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== +dequal@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" + integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== + destroy@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" @@ -2852,6 +3149,16 @@ dfa@^1.2.0: resolved "https://registry.yarnpkg.com/dfa/-/dfa-1.2.0.tgz#96ac3204e2d29c49ea5b57af8d92c2ae12790657" integrity sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q== +dom-accessibility-api@^0.5.9: + version "0.5.16" + resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453" + integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg== + +dom-accessibility-api@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz#993e925cc1d73f2c662e7d75dd5a5445259a8fd8" + integrity sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w== + dom-serializer@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" @@ -2952,6 +3259,11 @@ entities@^4.2.0, entities@^4.4.0: resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== +entities@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-6.0.0.tgz#09c9e29cb79b0a6459a9b9db9efb418ac5bb8e51" + integrity sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw== + environment@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/environment/-/environment-1.1.0.tgz#8e86c66b180f363c7ab311787e0259665f45a9f1" @@ -2979,7 +3291,7 @@ es-errors@^1.3.0: resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== -es-module-lexer@^1.3.1, es-module-lexer@^1.5.4: +es-module-lexer@^1.3.1, es-module-lexer@^1.5.4, es-module-lexer@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.7.0.tgz#9159601561880a85f2734560a9099b2c31e5372a" integrity sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA== @@ -3037,6 +3349,13 @@ estree-walker@^2.0.2: resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== +estree-walker@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-3.0.3.tgz#67c3e549ec402a487b4fc193d1953a524752340d" + integrity sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g== + dependencies: + "@types/estree" "^1.0.0" + esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" @@ -3072,6 +3391,11 @@ exit-hook@2.2.1: resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-2.2.1.tgz#007b2d92c6428eda2b76e7016a34351586934593" integrity sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw== +expect-type@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/expect-type/-/expect-type-1.2.1.tgz#af76d8b357cf5fa76c41c09dafb79c549e75f71f" + integrity sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw== + express@^4.19.2: version "4.21.2" resolved "https://registry.yarnpkg.com/express/-/express-4.21.2.tgz#cf250e48362174ead6cea4a566abef0162c1ec32" @@ -3239,7 +3563,7 @@ get-stream@^8.0.1: resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-8.0.1.tgz#def9dfd71742cd7754a7761ed43749a27d02eca2" integrity sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA== -glob@^10.2.2: +glob@^10.2.2, glob@^10.4.1: version "10.4.5" resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== @@ -3281,6 +3605,11 @@ graphql@^16.8.1: resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.11.0.tgz#96d17f66370678027fdf59b2d4c20b4efaa8a633" integrity sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw== +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" @@ -3319,6 +3648,18 @@ hosted-git-info@^6.0.0, hosted-git-info@^6.1.1: dependencies: lru-cache "^7.5.1" +html-encoding-sniffer@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz#696df529a7cfd82446369dc5193e590a3735b448" + integrity sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ== + dependencies: + whatwg-encoding "^3.1.1" + +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + http-errors@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" @@ -3330,6 +3671,22 @@ http-errors@2.0.0: statuses "2.0.1" toidentifier "1.0.1" +http-proxy-agent@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e" + integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig== + dependencies: + agent-base "^7.1.0" + debug "^4.3.4" + +https-proxy-agent@^7.0.6: + version "7.0.6" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz#da8dfeac7da130b05c2ba4b59c9b6cd66611a6b9" + integrity sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw== + dependencies: + agent-base "^7.1.2" + debug "4" + human-signals@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28" @@ -3347,7 +3704,7 @@ iconv-lite@0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@0.6, iconv-lite@^0.6.3: +iconv-lite@0.6, iconv-lite@0.6.3, iconv-lite@^0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== @@ -3362,6 +3719,11 @@ import-fresh@^3.3.0: parent-module "^1.0.0" resolve-from "^4.0.0" +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + inherits@2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" @@ -3437,6 +3799,11 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== +is-potential-custom-element-name@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" + integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== + is-regex@^1.1.4: version "1.2.1" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.2.1.tgz#76d70a3ed10ef9be48eb577887d74205bf0cad22" @@ -3462,6 +3829,37 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" + integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== + +istanbul-lib-report@^3.0.0, istanbul-lib-report@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d" + integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw== + dependencies: + istanbul-lib-coverage "^3.0.0" + make-dir "^4.0.0" + supports-color "^7.1.0" + +istanbul-lib-source-maps@^5.0.6: + version "5.0.6" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz#acaef948df7747c8eb5fbf1265cb980f6353a441" + integrity sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A== + dependencies: + "@jridgewell/trace-mapping" "^0.3.23" + debug "^4.1.1" + istanbul-lib-coverage "^3.0.0" + +istanbul-reports@^3.1.7: + version "3.1.7" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.7.tgz#daed12b9e1dca518e15c056e1e537e741280fa0b" + integrity sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + jackspeak@^3.1.2: version "3.4.3" resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" @@ -3493,6 +3891,32 @@ js-yaml@^4.1.0: dependencies: argparse "^2.0.1" +jsdom@^26.1.0: + version "26.1.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-26.1.0.tgz#ab5f1c1cafc04bd878725490974ea5e8bf0c72b3" + integrity sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg== + dependencies: + cssstyle "^4.2.1" + data-urls "^5.0.0" + decimal.js "^10.5.0" + html-encoding-sniffer "^4.0.0" + http-proxy-agent "^7.0.2" + https-proxy-agent "^7.0.6" + is-potential-custom-element-name "^1.0.1" + nwsapi "^2.2.16" + parse5 "^7.2.1" + rrweb-cssom "^0.8.0" + saxes "^6.0.0" + symbol-tree "^3.2.4" + tough-cookie "^5.1.1" + w3c-xmlserializer "^5.0.0" + webidl-conversions "^7.0.0" + whatwg-encoding "^3.1.1" + whatwg-mimetype "^4.0.0" + whatwg-url "^14.1.1" + ws "^8.18.0" + xml-name-validator "^5.0.0" + jsesc@3.0.2, jsesc@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.0.2.tgz#bb8b09a6597ba426425f2e4a07245c3d00b9343e" @@ -3659,6 +4083,11 @@ log-update@^6.1.0: strip-ansi "^7.1.0" wrap-ansi "^9.0.0" +loupe@^3.1.0, loupe@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/loupe/-/loupe-3.1.3.tgz#042a8f7986d77f3d0f98ef7990a2b2fef18b0fd2" + integrity sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug== + lower-case@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" @@ -3666,7 +4095,7 @@ lower-case@^2.0.2: dependencies: tslib "^2.0.3" -lru-cache@^10.2.0: +lru-cache@^10.2.0, lru-cache@^10.4.3: version "10.4.3" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== @@ -3683,6 +4112,34 @@ lru-cache@^7.4.4, lru-cache@^7.5.1: resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== +lz-string@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941" + integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ== + +magic-string@^0.30.17: + version "0.30.17" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.17.tgz#450a449673d2460e5bbcfba9a61916a1714c7453" + integrity sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA== + dependencies: + "@jridgewell/sourcemap-codec" "^1.5.0" + +magicast@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/magicast/-/magicast-0.3.5.tgz#8301c3c7d66704a0771eb1bad74274f0ec036739" + integrity sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ== + dependencies: + "@babel/parser" "^7.25.4" + "@babel/types" "^7.25.4" + source-map-js "^1.2.0" + +make-dir@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" + integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== + dependencies: + semver "^7.5.3" + markerjs2@^2.29.4: version "2.32.4" resolved "https://registry.yarnpkg.com/markerjs2/-/markerjs2-2.32.4.tgz#7aa79e87192ae3a8effd4235ae116ed4b8239c31" @@ -3763,6 +4220,11 @@ mimic-function@^5.0.0: resolved "https://registry.yarnpkg.com/mimic-function/-/mimic-function-5.0.1.tgz#acbe2b3349f99b9deaca7fb70e48b83e94e67076" integrity sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA== +min-indent@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" + integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== + minimatch@^9.0.0, minimatch@^9.0.4: version "9.0.5" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" @@ -3929,6 +4391,11 @@ nth-check@^2.0.1: dependencies: boolbase "^1.0.0" +nwsapi@^2.2.16: + version "2.2.20" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.20.tgz#22e53253c61e7b0e7e93cef42c891154bcca11ef" + integrity sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA== + object-inspect@^1.13.3: version "1.13.4" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" @@ -4012,6 +4479,13 @@ parse-json@^5.2.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" +parse5@^7.2.1: + version "7.3.0" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.3.0.tgz#d7e224fa72399c7a175099f45fc2ad024b05ec05" + integrity sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw== + dependencies: + entities "^6.0.0" + parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" @@ -4060,6 +4534,16 @@ pathe@^1.1.2: resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec" integrity sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ== +pathe@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/pathe/-/pathe-2.0.3.tgz#3ecbec55421685b70a9da872b2cff3e1cbed1716" + integrity sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w== + +pathval@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-2.0.0.tgz#7e2550b422601d4f6b8e26f1301bc8f15a741a25" + integrity sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA== + pdfmake@^0.2.2: version "0.2.19" resolved "https://registry.yarnpkg.com/pdfmake/-/pdfmake-0.2.19.tgz#23d6862b395de95e41089263936f0ff806193ea7" @@ -4116,6 +4600,15 @@ prettier@^2.7.1: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== +pretty-format@^27.0.2: + version "27.5.1" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" + integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== + dependencies: + ansi-regex "^5.0.1" + ansi-styles "^5.0.0" + react-is "^17.0.1" + proc-log@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-3.0.0.tgz#fb05ef83ccd64fd7b20bbe9c8c1070fc08338dd8" @@ -4188,6 +4681,11 @@ react-dom@^19.1.0: dependencies: scheduler "^0.26.0" +react-is@^17.0.1: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" + integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== + react-refresh@^0.14.0: version "0.14.2" resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.2.tgz#3833da01ce32da470f1f936b9d477da5c7028bf9" @@ -4218,6 +4716,14 @@ readdirp@^4.0.1: resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.1.2.tgz#eb85801435fbf2a7ee58f19e0921b068fc69948d" integrity sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg== +redent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" + integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg== + dependencies: + indent-string "^4.0.0" + strip-indent "^3.0.0" + regenerate-unicode-properties@^10.2.0: version "10.2.0" resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz#626e39df8c372338ea9b8028d1f99dc3fd9c3db0" @@ -4342,6 +4848,11 @@ rollup@^4.34.9: "@rollup/rollup-win32-x64-msvc" "4.40.2" fsevents "~2.3.2" +rrweb-cssom@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz#3021d1b4352fbf3b614aaeed0bc0d5739abe0bc2" + integrity sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw== + rw@1: version "1.3.3" resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4" @@ -4367,6 +4878,13 @@ sax@^1.2.4: resolved "https://registry.yarnpkg.com/sax/-/sax-1.4.1.tgz#44cc8988377f126304d3b3fc1010c733b929ef0f" integrity sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg== +saxes@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5" + integrity sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA== + dependencies: + xmlchars "^2.2.0" + scheduler@^0.26.0: version "0.26.0" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.26.0.tgz#4ce8a8c2a2095f13ea11bf9a445be50c555d6337" @@ -4500,6 +5018,11 @@ side-channel@^1.0.6: side-channel-map "^1.0.1" side-channel-weakmap "^1.0.2" +siginfo@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30" + integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g== + signal-exit@^4.0.1, signal-exit@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" @@ -4529,7 +5052,7 @@ snake-case@^3.0.4: dot-case "^3.0.4" tslib "^2.0.3" -source-map-js@^1.0.1, source-map-js@^1.2.1: +source-map-js@^1.0.1, source-map-js@^1.2.0, source-map-js@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== @@ -4573,11 +5096,21 @@ spdx-license-ids@^3.0.0: resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz#6d6e980c9df2b6fc905343a3b2d702a6239536c3" integrity sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg== +stackback@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/stackback/-/stackback-0.0.2.tgz#1ac8a0d9483848d1695e418b6d031a3c3ce68e3b" + integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw== + statuses@2.0.1, statuses@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== +std-env@^3.9.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.9.0.tgz#1a6f7243b339dca4c9fd55e1c7504c77ef23e8f1" + integrity sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw== + stream-slice@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/stream-slice/-/stream-slice-0.1.2.tgz#2dc4f4e1b936fb13f3eb39a2def1932798d07a4b" @@ -4655,6 +5188,20 @@ strip-final-newline@^3.0.0: resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== +strip-indent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" + integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== + dependencies: + min-indent "^1.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" @@ -4683,6 +5230,11 @@ svgo@^3.0.2: csso "^5.0.5" picocolors "^1.0.0" +symbol-tree@^3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" + integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== + tailwindcss@4.1.5, tailwindcss@^4.1.5: version "4.1.5" resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-4.1.5.tgz#d35607f1a351051bd29cda7e59ab2c222ca8deb6" @@ -4693,11 +5245,30 @@ tapable@^2.2.0: resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== +test-exclude@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-7.0.1.tgz#20b3ba4906ac20994e275bbcafd68d510264c2a2" + integrity sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg== + dependencies: + "@istanbuljs/schema" "^0.1.2" + glob "^10.4.1" + minimatch "^9.0.4" + tiny-inflate@^1.0.0, tiny-inflate@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/tiny-inflate/-/tiny-inflate-1.0.3.tgz#122715494913a1805166aaf7c93467933eea26c4" integrity sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw== +tinybench@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.9.0.tgz#103c9f8ba6d7237a47ab6dd1dcff77251863426b" + integrity sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg== + +tinyexec@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-0.3.2.tgz#941794e657a85e496577995c6eef66f53f42b3d2" + integrity sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA== + tinyglobby@^0.2.13: version "0.2.13" resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.13.tgz#a0e46515ce6cbcd65331537e57484af5a7b2ff7e" @@ -4706,11 +5277,38 @@ tinyglobby@^0.2.13: fdir "^6.4.4" picomatch "^4.0.2" +tinypool@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-1.0.2.tgz#706193cc532f4c100f66aa00b01c42173d9051b2" + integrity sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA== + tinyqueue@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/tinyqueue/-/tinyqueue-2.0.3.tgz#64d8492ebf39e7801d7bd34062e29b45b2035f08" integrity sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA== +tinyrainbow@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/tinyrainbow/-/tinyrainbow-2.0.0.tgz#9509b2162436315e80e3eee0fcce4474d2444294" + integrity sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw== + +tinyspy@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-3.0.2.tgz#86dd3cf3d737b15adcf17d7887c84a75201df20a" + integrity sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q== + +tldts-core@^6.1.86: + version "6.1.86" + resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-6.1.86.tgz#a93e6ed9d505cb54c542ce43feb14c73913265d8" + integrity sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA== + +tldts@^6.1.32: + version "6.1.86" + resolved "https://registry.yarnpkg.com/tldts/-/tldts-6.1.86.tgz#087e0555b31b9725ee48ca7e77edc56115cd82f7" + integrity sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ== + dependencies: + tldts-core "^6.1.86" + to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -4733,6 +5331,20 @@ tough-cookie@^4.1.4: universalify "^0.2.0" url-parse "^1.5.3" +tough-cookie@^5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-5.1.2.tgz#66d774b4a1d9e12dc75089725af3ac75ec31bed7" + integrity sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A== + dependencies: + tldts "^6.1.32" + +tr46@^5.1.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-5.1.1.tgz#96ae867cddb8fdb64a49cc3059a8d428bcf238ca" + integrity sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw== + dependencies: + punycode "^2.3.1" + tsconfck@^3.0.3: version "3.1.5" resolved "https://registry.yarnpkg.com/tsconfck/-/tsconfck-3.1.5.tgz#2f07f9be6576825e7a77470a5304ce06c7746e61" @@ -4895,6 +5507,17 @@ vite-node@3.0.0-beta.2: pathe "^1.1.2" vite "^5.0.0 || ^6.0.0" +vite-node@3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-3.1.4.tgz#13f10b2cb155197a971cb2761664ec952c6cae18" + integrity sha512-6enNwYnpyDo4hEgytbmc6mYWHXDHYEn0D1/rw4Q+tnHUGtKTJsn8T1YkX6Q18wI5LCrS8CTYlBaiCqxOy2kvUA== + dependencies: + cac "^6.7.14" + debug "^4.4.0" + es-module-lexer "^1.7.0" + pathe "^2.0.3" + vite "^5.0.0 || ^6.0.0" + vite-plugin-svgr@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/vite-plugin-svgr/-/vite-plugin-svgr-4.3.0.tgz#742f16f11375996306c696ec323e4d23f6005075" @@ -4927,6 +5550,65 @@ vite-tsconfig-paths@^5.1.4: optionalDependencies: fsevents "~2.3.3" +vitest@^3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/vitest/-/vitest-3.1.4.tgz#5f495b7dbb1d4d208b88508cd4dfceb006f8b7e6" + integrity sha512-Ta56rT7uWxCSJXlBtKgIlApJnT6e6IGmTYxYcmxjJ4ujuZDI59GUQgVDObXXJujOmPDBYXHK1qmaGtneu6TNIQ== + dependencies: + "@vitest/expect" "3.1.4" + "@vitest/mocker" "3.1.4" + "@vitest/pretty-format" "^3.1.4" + "@vitest/runner" "3.1.4" + "@vitest/snapshot" "3.1.4" + "@vitest/spy" "3.1.4" + "@vitest/utils" "3.1.4" + chai "^5.2.0" + debug "^4.4.0" + expect-type "^1.2.1" + magic-string "^0.30.17" + pathe "^2.0.3" + std-env "^3.9.0" + tinybench "^2.9.0" + tinyexec "^0.3.2" + tinyglobby "^0.2.13" + tinypool "^1.0.2" + tinyrainbow "^2.0.0" + vite "^5.0.0 || ^6.0.0" + vite-node "3.1.4" + why-is-node-running "^2.3.0" + +w3c-xmlserializer@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz#f925ba26855158594d907313cedd1476c5967f6c" + integrity sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA== + dependencies: + xml-name-validator "^5.0.0" + +webidl-conversions@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" + integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== + +whatwg-encoding@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz#d0f4ef769905d426e1688f3e34381a99b60b76e5" + integrity sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ== + dependencies: + iconv-lite "0.6.3" + +whatwg-mimetype@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz#bc1bf94a985dc50388d54a9258ac405c3ca2fc0a" + integrity sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg== + +whatwg-url@^14.0.0, whatwg-url@^14.1.1: + version "14.2.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-14.2.0.tgz#4ee02d5d725155dae004f6ae95c73e7ef5d95663" + integrity sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw== + dependencies: + tr46 "^5.1.0" + webidl-conversions "^7.0.0" + which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" @@ -4941,6 +5623,14 @@ which@^3.0.0: dependencies: isexe "^2.0.0" +why-is-node-running@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/why-is-node-running/-/why-is-node-running-2.3.0.tgz#a3f69a97107f494b3cdc3bdddd883a7d65cebf04" + integrity sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w== + dependencies: + siginfo "^2.0.0" + stackback "0.0.2" + "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" @@ -4986,11 +5676,21 @@ wrap-ansi@^9.0.0: string-width "^7.0.0" strip-ansi "^7.1.0" -ws@^8.18.2: +ws@^8.18.0, ws@^8.18.2: version "8.18.2" resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.2.tgz#42738b2be57ced85f46154320aabb51ab003705a" integrity sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ== +xml-name-validator@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-5.0.0.tgz#82be9b957f7afdacf961e5980f1bf227c0bf7673" + integrity sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg== + +xmlchars@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" + integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== + xmldoc@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/xmldoc/-/xmldoc-2.0.1.tgz#a901f6a6341e4d8cba3dbc5fc61017249f2adf24" From 2d6c4c0dfe841e4220dcb7449f0584fa07a9781e Mon Sep 17 00:00:00 2001 From: BHyeonKim Date: Wed, 28 May 2025 14:01:20 +0900 Subject: [PATCH 04/40] =?UTF-8?q?test:=20=EC=8A=A4=EC=9C=84=EC=B9=98=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EB=8B=A8=EC=9C=84?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit issue: #19 --- src/shared/ui/Switch/Switch.test.tsx | 56 ++++++++++++++++++++++++++++ src/shared/ui/Switch/index.tsx | 7 +++- 2 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 src/shared/ui/Switch/Switch.test.tsx diff --git a/src/shared/ui/Switch/Switch.test.tsx b/src/shared/ui/Switch/Switch.test.tsx new file mode 100644 index 0000000..9a99766 --- /dev/null +++ b/src/shared/ui/Switch/Switch.test.tsx @@ -0,0 +1,56 @@ +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import Switch from '.'; + +const SWITCH_PROPS = { + value1: 'value1', + value2: 'value2', + text1: 'text1', + text2: 'text2', + selected: 'value1', + onChange: vi.fn(), +}; + +describe('Switch 컴포넌트 테스트', () => { + beforeEach(() => { + render(); + }); + + it('Switch에 props로 전달된 text1과 text2가 렌더링 된다.', async () => { + const text1 = screen.getByText(SWITCH_PROPS.text1); + const text2 = screen.getByText(SWITCH_PROPS.text2); + + expect(text1).toBeInTheDocument(); + expect(text2).toBeInTheDocument(); + }); + + it('Switch에 props로 전달된 value1과 value2가 렌더링 된다.', async () => { + const switchButton1 = screen.getByTestId('switch-button-1'); + const switchButton2 = screen.getByTestId('switch-button-2'); + + expect(switchButton1).toHaveValue(SWITCH_PROPS.value1); + expect(switchButton2).toHaveValue(SWITCH_PROPS.value2); + }); + + it('Switch를 누르면 해당 값이 선택된다.', async () => { + const user = userEvent.setup(); + + await user.click(screen.getByTestId('switch-button-1')); + expect(SWITCH_PROPS.onChange).toHaveBeenCalledWith(SWITCH_PROPS.value1); + + await user.click(screen.getByTestId('switch-button-2')); + expect(SWITCH_PROPS.onChange).toHaveBeenCalledWith(SWITCH_PROPS.value2); + }); + + it('같은 버튼을 연속으로 클릭할 수 있다.', async () => { + const user = userEvent.setup(); + + await user.click(screen.getByTestId('switch-button-1')); + expect(SWITCH_PROPS.onChange).toHaveBeenCalledWith(SWITCH_PROPS.value1); + + await user.click(screen.getByTestId('switch-button-1')); + expect(SWITCH_PROPS.onChange).toHaveBeenCalledWith(SWITCH_PROPS.value1); + }); +}); diff --git a/src/shared/ui/Switch/index.tsx b/src/shared/ui/Switch/index.tsx index 558f3e6..9b684bb 100644 --- a/src/shared/ui/Switch/index.tsx +++ b/src/shared/ui/Switch/index.tsx @@ -29,7 +29,10 @@ export default function Switch({ }; return ( -
+
({ onClick={handleSwitch} tabIndex={0} value={value1} + data-testid="switch-button-1" > {text1} @@ -51,6 +55,7 @@ export default function Switch({ onClick={handleSwitch} tabIndex={0} value={value2} + data-testid="switch-button-2" > {text2} From 6cf60945eebe70d1c8db16b926f4a6387dee3c46 Mon Sep 17 00:00:00 2001 From: BHyeonKim Date: Wed, 28 May 2025 14:26:42 +0900 Subject: [PATCH 05/40] =?UTF-8?q?test:=20NumberInput=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EB=8B=A8=EC=9C=84=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit issue: #19 --- .../ui/NumberInput/NumberInput.test.tsx | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/shared/ui/NumberInput/NumberInput.test.tsx diff --git a/src/shared/ui/NumberInput/NumberInput.test.tsx b/src/shared/ui/NumberInput/NumberInput.test.tsx new file mode 100644 index 0000000..18863c0 --- /dev/null +++ b/src/shared/ui/NumberInput/NumberInput.test.tsx @@ -0,0 +1,31 @@ +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import type { ChangeEvent } from 'react'; +import { describe, expect, it, vi } from 'vitest'; +import NumberInput from '.'; + +describe('NumberInput 컴포넌트 테스트', () => { + it('사용자가 숫자를 입력하면 onChange 이벤트가 발생한다.', async () => { + const onChange = (e: ChangeEvent) => {}; + const onChangeSpy = vi.fn(onChange); + render(); + const input = screen.getByRole('spinbutton'); + + const user = userEvent.setup(); + await user.type(input, '123'); + + expect(onChangeSpy).toHaveBeenCalled(); + expect(onChangeSpy.mock.calls[0][0].target.value).toBe('123'); + }); + + it('숫자가 아닌 문자를 입력하면 값이 변경되지 않는다', async () => { + const onChangeSpy = vi.fn(); + render(); + const input = screen.getByRole('spinbutton'); + + const user = userEvent.setup(); + await user.type(input, 'abc'); + + expect(input).toHaveValue(null); + }); +}); From d141ee4bc760d8bbd38d2423599bede9713c107f Mon Sep 17 00:00:00 2001 From: BHyeonKim Date: Wed, 28 May 2025 14:34:07 +0900 Subject: [PATCH 06/40] =?UTF-8?q?config:=20vitest=20=EC=A0=88=EB=8C=80?= =?UTF-8?q?=EA=B2=BD=EB=A1=9C=20=EC=84=A4=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit issue: #19 --- vitest.config.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vitest.config.ts b/vitest.config.ts index 3f0b619..9af6bbf 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -1,7 +1,9 @@ /// import { defineConfig } from 'vite'; +import tsconfigPaths from 'vite-tsconfig-paths'; export default defineConfig({ + plugins: [tsconfigPaths()], test: { environment: 'jsdom', globals: true, From e25f6132cabf6eb342e20d6596fb29f0ef46de49 Mon Sep 17 00:00:00 2001 From: BHyeonKim Date: Wed, 28 May 2025 14:39:15 +0900 Subject: [PATCH 07/40] =?UTF-8?q?test:=20logo=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EB=8B=A8=EC=9C=84=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit issue: #19 --- src/shared/ui/Logo/Logo.test.tsx | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/shared/ui/Logo/Logo.test.tsx diff --git a/src/shared/ui/Logo/Logo.test.tsx b/src/shared/ui/Logo/Logo.test.tsx new file mode 100644 index 0000000..51b4dd8 --- /dev/null +++ b/src/shared/ui/Logo/Logo.test.tsx @@ -0,0 +1,23 @@ +import { render, screen } from '@testing-library/react'; +import { describe, expect, it } from 'vitest'; + +import Logo from '.'; + +import CloudLogoBlack from '~/assets/images/cloud-black.webp'; +import CloudLogo from '~/assets/images/cloud.webp'; + +describe('Logo 컴포넌트 테스트', () => { + it('isBlack이 true일 때 블랙 로고가 렌더링 된다.', () => { + render(); + const logo = screen.getByRole('img'); + + expect(logo).toHaveAttribute('src', CloudLogoBlack); + }); + + it('isBlack이 false일 때 흰색 로고가 렌더링 된다.', () => { + render(); + const logo = screen.getByRole('img'); + + expect(logo).toHaveAttribute('src', CloudLogo); + }); +}); From 23adab0196298b57091c09dcf065b4b73ef1a640 Mon Sep 17 00:00:00 2001 From: BHyeonKim Date: Wed, 28 May 2025 17:00:17 +0900 Subject: [PATCH 08/40] =?UTF-8?q?test:=20LogoWithTitle=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EB=8B=A8=EC=9C=84=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit issue: #19 --- src/shared/ui/LogoWithTitle/LogoWithTitle.test.tsx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 src/shared/ui/LogoWithTitle/LogoWithTitle.test.tsx diff --git a/src/shared/ui/LogoWithTitle/LogoWithTitle.test.tsx b/src/shared/ui/LogoWithTitle/LogoWithTitle.test.tsx new file mode 100644 index 0000000..e56b2d8 --- /dev/null +++ b/src/shared/ui/LogoWithTitle/LogoWithTitle.test.tsx @@ -0,0 +1,14 @@ +import { render, screen } from '@testing-library/react'; +import { describe, expect, it } from 'vitest'; + +import LogoWithTitle from '.'; + +describe('LogoWithTitle 컴포넌트 테스트', () => { + it('props로 전달된 serviceName이 렌더링 된다.', () => { + render(); + + const component = screen.getByRole('heading'); + + expect(component).toHaveTextContent('서비스'); + }); +}); From e92967c45e31f56d8768f2a4917b45e080110bc2 Mon Sep 17 00:00:00 2001 From: BHyeonKim Date: Wed, 28 May 2025 17:11:50 +0900 Subject: [PATCH 09/40] =?UTF-8?q?test:=20Button=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EB=8B=A8=EC=9C=84=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit issue: #19 --- src/shared/ui/Button/Button.test.tsx | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/shared/ui/Button/Button.test.tsx diff --git a/src/shared/ui/Button/Button.test.tsx b/src/shared/ui/Button/Button.test.tsx new file mode 100644 index 0000000..1d11f6e --- /dev/null +++ b/src/shared/ui/Button/Button.test.tsx @@ -0,0 +1,28 @@ +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { describe, expect, it, vi } from 'vitest'; + +import Button from '.'; + +describe('Button 컴포넌트 테스트', () => { + it('props로 전달된 children이 렌더링 된다.', () => { + const text = '버튼'; + render(); + + const button = screen.getByRole('button'); + + expect(button).toHaveTextContent(text); + }); + + it('button을 클릭하면 props로 전달된 onClick이 호출된다.', async () => { + const spy = vi.fn(); + const user = userEvent.setup(); + + render(); + + const button = screen.getByRole('button'); + await user.click(button); + + expect(spy).toHaveBeenCalled(); + }); +}); From 6bca61de26619dfdb08d6fae4885688eead755ce Mon Sep 17 00:00:00 2001 From: BHyeonKim Date: Wed, 28 May 2025 17:48:22 +0900 Subject: [PATCH 10/40] =?UTF-8?q?config:=20lint=20unsafe=20fix=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 184046f..05c9eac 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "typecheck": "react-router typegen && tsc", "test": "vitest", "coverage": "vitest run --coverage", - "lint": "biome check --write --unsafe .", + "lint": "biome check --write .", "postinstall": "husky" }, "lint-staged": { From 178a6286ca5c4b211a47c2527ff55666d76ad935 Mon Sep 17 00:00:00 2001 From: BHyeonKim Date: Wed, 28 May 2025 17:48:51 +0900 Subject: [PATCH 11/40] =?UTF-8?q?test:=20util=20=ED=95=A8=EC=88=98=20test?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit issue: #19 --- src/shared/utils/utils.test.ts | 104 +++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 src/shared/utils/utils.test.ts diff --git a/src/shared/utils/utils.test.ts b/src/shared/utils/utils.test.ts new file mode 100644 index 0000000..ad21aa2 --- /dev/null +++ b/src/shared/utils/utils.test.ts @@ -0,0 +1,104 @@ +import type { KeyboardEvent } from 'react'; +import { describe, expect, it, vi } from 'vitest'; + +import { + filterNumber, + formatCurrencyKR, + isNegative, + isUndefined, + preventNonNumericInput, +} from '.'; + +describe('util 함수 테스트', () => { + describe('isUndefined 테스트', () => { + it('undefined가 전달되면 true를 반환한다.', () => { + const result = isUndefined(undefined); + + expect(result).toBe(true); + }); + + it('undefined를 제외한 값이 전달되면 false를 반환한다.', () => { + const result = isUndefined(123); + + expect(result).toBe(false); + }); + }); + + describe('isNegative 테스트', () => { + it('음수를 전달하면 true를 반환한다.', () => { + const result = isNegative(-1); + + expect(result).toBe(true); + }); + + it('양수를 전달하면 false를 반환한다.', () => { + const result = isNegative(1); + + expect(result).toBe(false); + }); + }); + + describe('formatCurrencyKR 테스트', () => { + it('123을 전달하면 문자열 123을 반환한다.', () => { + const result = formatCurrencyKR(123); + + expect(result).toBe('123'); + }); + + it('1234을 전달하면 문자열 1,234을 반환한다.', () => { + const result = formatCurrencyKR(1234); + + expect(result).toBe('1,234'); + }); + + it('1234.567을 전달하면 문자열 1,234.57을 반환한다.', () => { + const result = formatCurrencyKR(1234.567); + + expect(result).toBe('1,234.57'); + }); + + it('10000000을 전달하면 문자열 10,000,000을 반환한다.', () => { + const result = formatCurrencyKR(10000000); + + expect(result).toBe('10,000,000'); + }); + }); + + describe('filterNumber 테스트', () => { + it('숫자를 전달하면 true를 반환한다.', () => { + const result = filterNumber('123'); + + expect(result).toBe(true); + }); + + it('문자를 전달하면 false를 반환한다.', () => { + const result = filterNumber('qwer'); + + expect(result).toBe(false); + }); + }); + + describe('preventNonNumericInput 테스트', () => { + it('숫자 key가 아닌 key가 전달되면 preventDefault가 호출된다.', () => { + const event = { + key: 'a', + preventDefault: vi.fn(), + } as unknown as KeyboardEvent; + + preventNonNumericInput(event); + + expect(event.preventDefault).toHaveBeenCalled(); + }); + + it('숫자 key가 전달되면 preventDefault가 호출되지 않는다.', () => { + const event = { + key: '1', + preventDefault: vi.fn(), + } as unknown as KeyboardEvent; + + preventNonNumericInput(event); + + expect(event.preventDefault).not.toHaveBeenCalled(); + }); + }); +}); From 0547245534f5374409cfa20038e15964770769c2 Mon Sep 17 00:00:00 2001 From: BHyeonKim Date: Thu, 29 May 2025 10:26:01 +0900 Subject: [PATCH 12/40] =?UTF-8?q?test:=20ContainerTitle=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EB=8B=A8=EC=9C=84=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit issue: #19 --- .../ui/ContainerTitle/ContainerTitle.test.tsx | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/shared/ui/ContainerTitle/ContainerTitle.test.tsx diff --git a/src/shared/ui/ContainerTitle/ContainerTitle.test.tsx b/src/shared/ui/ContainerTitle/ContainerTitle.test.tsx new file mode 100644 index 0000000..7e0d8cc --- /dev/null +++ b/src/shared/ui/ContainerTitle/ContainerTitle.test.tsx @@ -0,0 +1,13 @@ +import { render, screen } from '@testing-library/react'; +import { describe, expect, it } from 'vitest'; + +import ContainerTitle from '.'; + +describe('ContainerTitle 컴포넌트 테스트', () => { + it('props로 전달된 string이 렌더링 된다.', () => { + const text = 'title'; + render({text}); + + expect(screen.getByText('title')).toBeInTheDocument(); + }); +}); From ca416e7d3c70f1ffe2e7478598041a4dfd6b2b1f Mon Sep 17 00:00:00 2001 From: BHyeonKim Date: Thu, 29 May 2025 14:49:47 +0900 Subject: [PATCH 13/40] =?UTF-8?q?test:=20useClickOutside=20=ED=9B=85=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit issue: #19 --- src/shared/hooks/hooks.test.tsx | 44 +++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 src/shared/hooks/hooks.test.tsx diff --git a/src/shared/hooks/hooks.test.tsx b/src/shared/hooks/hooks.test.tsx new file mode 100644 index 0000000..996ed37 --- /dev/null +++ b/src/shared/hooks/hooks.test.tsx @@ -0,0 +1,44 @@ +import { act, renderHook } from '@testing-library/react'; +import { describe, expect, it, vi } from 'vitest'; + +import useClickOutside from './useClickOutside'; + +describe('useClickOutside 훅 테스트', () => { + it('ref가 부착된 컴포넌트가 아닌 바깥 컴포넌트를 클릭하면 callback함수가 실행된다.', () => { + const innerElement = document.createElement('div'); + const outerElement = document.createElement('div'); + outerElement.appendChild(innerElement); + document.body.appendChild(outerElement); + + const ref = { current: innerElement }; + const callback = vi.fn(); + + renderHook(() => useClickOutside(ref, callback)); + + act(() => { + const event = new MouseEvent('click', { bubbles: true }); + outerElement.dispatchEvent(event); + }); + + expect(callback).toHaveBeenCalled(); + }); + + it('ref가 부착된 컴포넌트를 클릭하면 callback함수가 실행되지 않는다.', () => { + const innerElement = document.createElement('div'); + const outerElement = document.createElement('div'); + outerElement.appendChild(innerElement); + document.body.appendChild(outerElement); + + const ref = { current: innerElement }; + const callback = vi.fn(); + + renderHook(() => useClickOutside(ref, callback)); + + act(() => { + const event = new MouseEvent('click', { bubbles: true }); + innerElement.dispatchEvent(event); + }); + + expect(callback).not.toHaveBeenCalled(); + }); +}); From 0f5c9411eb7a99a799bf4fa42b814f54653fac64 Mon Sep 17 00:00:00 2001 From: BHyeonKim Date: Thu, 29 May 2025 16:21:36 +0900 Subject: [PATCH 14/40] =?UTF-8?q?test:=20useScrollToBottom=20=ED=9B=85=20?= =?UTF-8?q?=EB=8B=A8=EC=9C=84=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit issue: #19 --- src/shared/hooks/hooks.test.tsx | 70 +++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/src/shared/hooks/hooks.test.tsx b/src/shared/hooks/hooks.test.tsx index 996ed37..7046213 100644 --- a/src/shared/hooks/hooks.test.tsx +++ b/src/shared/hooks/hooks.test.tsx @@ -2,6 +2,7 @@ import { act, renderHook } from '@testing-library/react'; import { describe, expect, it, vi } from 'vitest'; import useClickOutside from './useClickOutside'; +import useScrollToBottom from './useScrollToBottom'; describe('useClickOutside 훅 테스트', () => { it('ref가 부착된 컴포넌트가 아닌 바깥 컴포넌트를 클릭하면 callback함수가 실행된다.', () => { @@ -42,3 +43,72 @@ describe('useClickOutside 훅 테스트', () => { expect(callback).not.toHaveBeenCalled(); }); }); + +describe('useScrollToBottom 훅 테스트', () => { + it('dependencies가 변경되면 scrollIntoView를 호출한다.', () => { + const mockScrollIntoView = vi.fn(); + + const { result, rerender } = renderHook( + ({ deps }) => useScrollToBottom(deps), + { initialProps: { deps: [1] } }, + ); + + act(() => { + const element = document.createElement('div'); + element.scrollIntoView = mockScrollIntoView; + result.current.current = element; + }); + + rerender({ deps: [2] }); + + expect(mockScrollIntoView).toHaveBeenCalled(); + }); +}); + +// FIXME: 테스트 환경에서 getBoundingClientRect를 mock으로 처리할 수 없음 +// describe('useDimensions 훅 테스트', () => { +// it('참조하는 element의 width와 height가 반환된다.', () => { +// const element = document.createElement('div'); +// const ref = { current: element }; +// vi.spyOn(element, 'getBoundingClientRect').mockImplementation(() => ({ +// width: 100, +// height: 100, +// x: 0, +// y: 0, +// top: 0, +// left: 0, +// right: 100, +// bottom: 100, +// toJSON: () => {}, +// })); + +// const { result } = renderHook(() => useDimensions(ref)); + +// expect(result.current.width).toBe(100); +// expect(result.current.height).toBe(100); +// }); + +// it('참조하는 element의 width와 height가 변경되면 변경된 값을 반환한다.', () => { +// const element = document.createElement('div'); +// document.body.appendChild(element); +// const ref = { current: element }; + +// element.style.width = '100px'; +// element.style.height = '100px'; +// element.style.border = 'none'; +// element.style.padding = 'none'; + +// const { result, rerender } = renderHook(() => useDimensions(ref)); + +// expect(result.current.width).toBe(100); +// expect(result.current.height).toBe(100); + +// element.style.height = '10px'; +// element.style.width = '10px'; + +// rerender(); + +// expect(result.current.height).toBe(10); +// expect(result.current.width).toBe(10); +// }); +// }); From 4244874d6ffe9b651f0d4981e2e17d0365bcbf62 Mon Sep 17 00:00:00 2001 From: BHyeonKim Date: Thu, 29 May 2025 17:12:03 +0900 Subject: [PATCH 15/40] =?UTF-8?q?test:=20useDimensions=20=ED=9B=85=20?= =?UTF-8?q?=EB=8B=A8=EC=9C=84=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit issue: #19 --- src/shared/hooks/hooks.test.tsx | 66 ++++++++++----------------------- 1 file changed, 19 insertions(+), 47 deletions(-) diff --git a/src/shared/hooks/hooks.test.tsx b/src/shared/hooks/hooks.test.tsx index 7046213..9ee6cce 100644 --- a/src/shared/hooks/hooks.test.tsx +++ b/src/shared/hooks/hooks.test.tsx @@ -2,6 +2,7 @@ import { act, renderHook } from '@testing-library/react'; import { describe, expect, it, vi } from 'vitest'; import useClickOutside from './useClickOutside'; +import useDimensions from './useDimensions'; import useScrollToBottom from './useScrollToBottom'; describe('useClickOutside 훅 테스트', () => { @@ -65,50 +66,21 @@ describe('useScrollToBottom 훅 테스트', () => { }); }); -// FIXME: 테스트 환경에서 getBoundingClientRect를 mock으로 처리할 수 없음 -// describe('useDimensions 훅 테스트', () => { -// it('참조하는 element의 width와 height가 반환된다.', () => { -// const element = document.createElement('div'); -// const ref = { current: element }; -// vi.spyOn(element, 'getBoundingClientRect').mockImplementation(() => ({ -// width: 100, -// height: 100, -// x: 0, -// y: 0, -// top: 0, -// left: 0, -// right: 100, -// bottom: 100, -// toJSON: () => {}, -// })); - -// const { result } = renderHook(() => useDimensions(ref)); - -// expect(result.current.width).toBe(100); -// expect(result.current.height).toBe(100); -// }); - -// it('참조하는 element의 width와 height가 변경되면 변경된 값을 반환한다.', () => { -// const element = document.createElement('div'); -// document.body.appendChild(element); -// const ref = { current: element }; - -// element.style.width = '100px'; -// element.style.height = '100px'; -// element.style.border = 'none'; -// element.style.padding = 'none'; - -// const { result, rerender } = renderHook(() => useDimensions(ref)); - -// expect(result.current.width).toBe(100); -// expect(result.current.height).toBe(100); - -// element.style.height = '10px'; -// element.style.width = '10px'; - -// rerender(); - -// expect(result.current.height).toBe(10); -// expect(result.current.width).toBe(10); -// }); -// }); +describe('useDimensions 훅 테스트', () => { + it('참조하는 element의 width와 height가 반환된다.', () => { + const element = document.createElement('div'); + const ref = { current: element }; + + Object.defineProperty(element, 'offsetWidth', { + value: 100, + }); + Object.defineProperty(element, 'offsetHeight', { + value: 100, + }); + + const { result } = renderHook(() => useDimensions(ref)); + + expect(result.current.width).toBe(100); + expect(result.current.height).toBe(100); + }); +}); From 2e3d162ef901f93d50bd80e01c69db0c68b7357f Mon Sep 17 00:00:00 2001 From: BHyeonKim Date: Thu, 29 May 2025 17:13:40 +0900 Subject: [PATCH 16/40] =?UTF-8?q?fix:=20useDimensions=20=ED=9B=85=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit useEffect depencency 수정 issue: #19 --- src/shared/hooks/useDimensions.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/hooks/useDimensions.tsx b/src/shared/hooks/useDimensions.tsx index 4362a51..1425461 100644 --- a/src/shared/hooks/useDimensions.tsx +++ b/src/shared/hooks/useDimensions.tsx @@ -10,7 +10,7 @@ export default function useDimensions( dimensions.current.width = ref.current.offsetWidth; dimensions.current.height = ref.current.offsetHeight; } - }, [ref]); + }, [ref.current]); return dimensions.current; } From beceefc845edcbcf43f8902b41e5ef61a98d4885 Mon Sep 17 00:00:00 2001 From: BHyeonKim Date: Fri, 30 May 2025 09:30:56 +0900 Subject: [PATCH 17/40] =?UTF-8?q?config:=20workflows=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit main이나 develop 브랜치에 코드를 푸시할 시 sonarqube code 분석이 실행되는 github action 스크립트를 추가했습니다. --- .github/workflows/code-analyze-push.yml | 46 +++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 .github/workflows/code-analyze-push.yml diff --git a/.github/workflows/code-analyze-push.yml b/.github/workflows/code-analyze-push.yml new file mode 100644 index 0000000..8251372 --- /dev/null +++ b/.github/workflows/code-analyze-push.yml @@ -0,0 +1,46 @@ +name: Code Analyze branch + +on: + push: + branches: + - main + - develop + +jobs: + code_analysis: + runs-on: ubuntu-latest + + steps: + - name: 'Checkout repository on branch: ${{ github.REF }}' + uses: actions/checkout@v3 + with: + ref: ${{ github.REF }} + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: ${{ env.NODE_VERSION }} + + - name: Cache node modules + uses: actions/cache@v3 + id: npm-cache + with: + path: '**/node_modules' + key: ${{ runner.os }}-node-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-node- + + - name: Install Dependencies + if: steps.npm-cache.outputs.cache-hit != 'true' + run: yarn install + + - name: Coverage Test + continue-on-error: true + run: yarn run coverage + + - name: 'Run an analysis of the ${{ github.REF }} branch' + uses: sonarsource/sonarqube-scan-action@master + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: ${{ secrets.SONAR_URL }} \ No newline at end of file From cf6d4dd6df5f1b19c5db2479e5122b6016fa5782 Mon Sep 17 00:00:00 2001 From: BHyeonKim Date: Fri, 30 May 2025 09:34:47 +0900 Subject: [PATCH 18/40] =?UTF-8?q?config=20workflow=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit main이나 develop 브랜치에 pr요청시에 sonarqube 분석이 실행되는 스크립트를 추가했습니다. --- .github/workflows/code-analyze-pr.yml | 55 +++++++++++++++++++++++++ .github/workflows/code-analyze-push.yml | 3 ++ 2 files changed, 58 insertions(+) create mode 100644 .github/workflows/code-analyze-pr.yml diff --git a/.github/workflows/code-analyze-pr.yml b/.github/workflows/code-analyze-pr.yml new file mode 100644 index 0000000..6f73800 --- /dev/null +++ b/.github/workflows/code-analyze-pr.yml @@ -0,0 +1,55 @@ +name: Code Analyze Pull Request + +run-name: Run code analyze triggered with pull request by ${{github.actor}} + +on: + pull_request: + types: [opened, reopened, synchronize] + branches: + - main + - develop +env: + NODE_VERSION: 18.12.0 + +jobs: + code_analysis: + + runs-on: ubuntu-latest + + steps: + - name: 'Checkout repository on branch: ${{ github.REF }}' + uses: actions/checkout@v3 + with: + ref: ${{ github.HEAD_REF }} + + - name: Retrieve entire repository history + run: | + git fetch --prune --unshallow + + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: ${{ env.NODE_VERSION }} + + - name: Cache node modules + uses: actions/cache@v3 + id: npm-cache + with: + path: '**/node_modules' + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- + + - name: Install Dependencies + if: steps.npm-cache.outputs.cache-hit != 'true' + run: npm install + + - name: Coverage Test + continue-on-error: true + run: npm run coverage + + - name: Run an analysis of the ${{ github.REF }} branch ${{ github.BASE_REF }} base + uses: sonarsource/sonarqube-scan-action@master + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: ${{ secrets.SONAR_URL }} \ No newline at end of file diff --git a/.github/workflows/code-analyze-push.yml b/.github/workflows/code-analyze-push.yml index 8251372..6a7f095 100644 --- a/.github/workflows/code-analyze-push.yml +++ b/.github/workflows/code-analyze-push.yml @@ -1,5 +1,8 @@ name: Code Analyze branch +run-name: Run code analyze triggered with push by ${{github.actor}} + + on: push: branches: From def077e2a899f23e744484ee67c7faf6b55b24fd Mon Sep 17 00:00:00 2001 From: BHyeonKim Date: Fri, 30 May 2025 09:48:27 +0900 Subject: [PATCH 19/40] =?UTF-8?q?config:=20sonarqube=20=EC=84=A4=EC=A0=95?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/code-analyze-pr.yml | 2 +- .github/workflows/code-analyze-push.yml | 3 +-- sonar-project.properties | 27 +++++++++++++++++++++++++ 3 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 sonar-project.properties diff --git a/.github/workflows/code-analyze-pr.yml b/.github/workflows/code-analyze-pr.yml index 6f73800..1c18b89 100644 --- a/.github/workflows/code-analyze-pr.yml +++ b/.github/workflows/code-analyze-pr.yml @@ -52,4 +52,4 @@ jobs: uses: sonarsource/sonarqube-scan-action@master env: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - SONAR_HOST_URL: ${{ secrets.SONAR_URL }} \ No newline at end of file + SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} \ No newline at end of file diff --git a/.github/workflows/code-analyze-push.yml b/.github/workflows/code-analyze-push.yml index 6a7f095..36c2072 100644 --- a/.github/workflows/code-analyze-push.yml +++ b/.github/workflows/code-analyze-push.yml @@ -2,7 +2,6 @@ name: Code Analyze branch run-name: Run code analyze triggered with push by ${{github.actor}} - on: push: branches: @@ -46,4 +45,4 @@ jobs: uses: sonarsource/sonarqube-scan-action@master env: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - SONAR_HOST_URL: ${{ secrets.SONAR_URL }} \ No newline at end of file + SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} \ No newline at end of file diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 0000000..3c8532e --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,27 @@ +# 프로젝트 식별자 +sonar.projectKey=CleanEngine_cleanengine-fe_c6875537-ed9d-4dfe-b79c-ea5cb6d2a0e3 + +# 프로젝트 이름 +sonar.projectName=CleanEngine Frontend + +# 소스 코드 위치 +sonar.sources=src + +# 테스트 파일 위치 및 패턴 +sonar.tests=src +sonar.test.inclusions=**/*.test.tsx,**/*.test.ts,**/*.spec.tsx,**/*.spec.ts + +# TypeScript/JavaScript 설정 +sonar.javascript.lcov.reportPaths=coverage/lcov.info +sonar.typescript.tsconfigPath=tsconfig.json + +# 분석에서 제외할 파일/디렉토리 +sonar.exclusions=node_modules/**,build/**,dist/**,coverage/**,public/**,**/*.test.tsx,**/*.test.ts,**/*.spec.tsx,**/*.spec.ts + +# 코드 중복 검사 제외 +sonar.cpd.exclusions=**/*.test.tsx,**/*.test.ts + +# 언어 설정 +sonar.language=ts +sonar.typescript.file.suffixes=.ts,.tsx +sonar.javascript.file.suffixes=.js,.jsx \ No newline at end of file From 6aa7a2e35eedf7c4dac019748a2c290782dcc842 Mon Sep 17 00:00:00 2001 From: BHyeonKim Date: Fri, 30 May 2025 15:32:32 +0900 Subject: [PATCH 20/40] =?UTF-8?q?test:=20CoinPriceWithName=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EB=8B=A8=EC=9C=84=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit issue: #19 --- .../CoinPriceWithName.test.tsx | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 src/entities/coin/ui/CoinPriceWithName/CoinPriceWithName.test.tsx diff --git a/src/entities/coin/ui/CoinPriceWithName/CoinPriceWithName.test.tsx b/src/entities/coin/ui/CoinPriceWithName/CoinPriceWithName.test.tsx new file mode 100644 index 0000000..ed88c23 --- /dev/null +++ b/src/entities/coin/ui/CoinPriceWithName/CoinPriceWithName.test.tsx @@ -0,0 +1,63 @@ +import { render, screen } from '@testing-library/react'; +import { describe, expect, it, vi } from 'vitest'; +import CloudImage from '~/assets/images/cloud.webp'; + +import '../../hooks/useCurrentPrice'; +import CoinPriceWithName from '.'; + +const mockPriceData = { + changeRate: 4, + currentPrice: 100_000_000, + prevClose: 100_000_000, + ticker: 'BTC', + timestamp: '2025-05-30T15:00:00.000Z', +}; + +vi.mock('../../hooks/useCurrentPrice', () => ({ + default: vi.fn().mockImplementation((ticker) => { + return mockPriceData; + }), +})); + +describe('CoinPriceWithName 컴포넌트 테스트', () => { + it('name과 ticker과 img가 prop으로 전달되면 화면에 보인다', () => { + const props = { + name: '비트코인', + ticker: 'BTC', + img: CloudImage, + }; + render(); + + const name = screen.getByText('비트코인'); + const ticker = screen.getByText('BTC'); + const img = screen.getByRole('img'); + + expect(name).toBeInTheDocument(); + expect(ticker).toBeInTheDocument(); + expect(img).toBeInTheDocument(); + }); + + it('img가 prop으로 전달되지 않으면 대체 아이콘이 보인다', () => { + const props = { + name: '비트코인', + ticker: 'BTC', + img: undefined, + }; + render(); + + const img = screen.getByText('🪙'); + expect(img).toBeInTheDocument(); + }); + + it('ticker가 주어지면 해당하는 코인의 가격이 한국의 원화 형식에 맞게 화면에 보인다.', () => { + const props = { + name: '비트코인', + ticker: 'BTC', + img: CloudImage, + }; + render(); + + const price = screen.getByText('100,000,000원'); + expect(price).toBeInTheDocument(); + }); +}); From 31238a7bf0fd1e7b314feea827811855ff272f1a Mon Sep 17 00:00:00 2001 From: BHyeonKim Date: Fri, 30 May 2025 15:53:09 +0900 Subject: [PATCH 21/40] =?UTF-8?q?test:=20QuantityInput=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EB=8B=A8=EC=9C=84=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit issue: #19 --- .../ui/QuantityInput/QuantityInput.test.tsx | 51 +++++++++++++++++++ src/entities/order/ui/QuantityInput/index.tsx | 4 +- vitest.config.ts | 3 +- 3 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 src/entities/order/ui/QuantityInput/QuantityInput.test.tsx diff --git a/src/entities/order/ui/QuantityInput/QuantityInput.test.tsx b/src/entities/order/ui/QuantityInput/QuantityInput.test.tsx new file mode 100644 index 0000000..ef52b2f --- /dev/null +++ b/src/entities/order/ui/QuantityInput/QuantityInput.test.tsx @@ -0,0 +1,51 @@ +import { act, render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { describe, expect, it, vi } from 'vitest'; + +import QuantityInput from '.'; + +const initialProps = { + value: undefined, + onClickPlus: vi.fn(), + onClickMinus: vi.fn(), +}; + +describe('QuantityInput 컴포넌트 테스트', () => { + it('금액을 입력하면 해당 금액이 화면에 보인다.', async () => { + const user = userEvent.setup(); + + render(); + + const input = screen.getByRole('spinbutton'); + + await act(async () => { + await user.type(input, '1000'); + }); + + expect(input).toHaveValue(1000); + }); + + it('plus 버튼을 클릭하면 onClickPlus가 호출된다.', async () => { + const user = userEvent.setup(); + + render(); + + const plusButton = screen.getByTestId('plus-button'); + + await user.click(plusButton); + + expect(initialProps.onClickPlus).toHaveBeenCalledTimes(1); + }); + + it('minus 버튼을 클릭하면 onClickMinus가 호출된다.', async () => { + const user = userEvent.setup(); + + render(); + + const minusButton = screen.getByTestId('minus-button'); + + await user.click(minusButton); + + expect(initialProps.onClickMinus).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/entities/order/ui/QuantityInput/index.tsx b/src/entities/order/ui/QuantityInput/index.tsx index f18629e..8c71693 100644 --- a/src/entities/order/ui/QuantityInput/index.tsx +++ b/src/entities/order/ui/QuantityInput/index.tsx @@ -26,7 +26,7 @@ export default function QuantityInput({ className="-translate-y-1/2 absolute top-1/2 right-1 w-7 cursor-pointer rounded-sm p-1.5 px-2 hover:bg-gray-200 disabled:cursor-not-allowed" disabled={props.disabled} > - +
); diff --git a/vitest.config.ts b/vitest.config.ts index 9af6bbf..d0b4e83 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -1,9 +1,10 @@ +import svgr from '@svgr/rollup'; /// import { defineConfig } from 'vite'; import tsconfigPaths from 'vite-tsconfig-paths'; export default defineConfig({ - plugins: [tsconfigPaths()], + plugins: [svgr(), tsconfigPaths()], test: { environment: 'jsdom', globals: true, From 50d2e9bd8bfc0a0826b9b2f5cc2970798e7f95ce Mon Sep 17 00:00:00 2001 From: BHyeonKim Date: Fri, 30 May 2025 17:02:14 +0900 Subject: [PATCH 22/40] =?UTF-8?q?test:=20AIChatBot=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EB=8B=A8=EC=9C=84=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit issue: #19 --- .../chat/ui/AIChatBot/AIChatBot.test.tsx | 54 +++++++++++++++++++ src/features/chat/ui/ChatButton/index.tsx | 1 + src/features/chat/ui/ChatWindow/index.tsx | 2 + 3 files changed, 57 insertions(+) create mode 100644 src/features/chat/ui/AIChatBot/AIChatBot.test.tsx diff --git a/src/features/chat/ui/AIChatBot/AIChatBot.test.tsx b/src/features/chat/ui/AIChatBot/AIChatBot.test.tsx new file mode 100644 index 0000000..87c6b78 --- /dev/null +++ b/src/features/chat/ui/AIChatBot/AIChatBot.test.tsx @@ -0,0 +1,54 @@ +import { render, screen } from '@testing-library/react'; +import { describe, expect, it, vi } from 'vitest'; + +import userEvent from '@testing-library/user-event'; +import AIChatBot from '.'; + +vi.mock('~/shared/hooks/useScrollToBottom', () => ({ + default: () => ({ current: { scrollIntoView: vi.fn() } }), +})); + +describe('AIChatBot 컴포넌트 테스트', () => { + it('초기 상태에서 ChatButton이 보여진다.', () => { + render(); + + const chatButton = screen.getByTestId('chat-button'); + + expect(chatButton).toBeInTheDocument(); + }); + + it('사용자가 ChatButton을 클릭하면 ChatWindow가 보여진다.', async () => { + const user = userEvent.setup(); + render(); + + const chatButton = screen.getByTestId('chat-button'); + + expect(chatButton).toBeInTheDocument(); + + await user.click(chatButton); + + const chatWindow = screen.getByTestId('chat-window'); + + expect(chatWindow).toBeInTheDocument(); + }); + + it('사용자가 ChatButton을 클릭하면 ChatWindow가 보여졌다고 닫기 버튼을 누르면 ChatWindow가 사라지고 ChatButton이 보여진다.', async () => { + const user = userEvent.setup(); + render(); + + const chatButton = screen.getByTestId('chat-button'); + + expect(chatButton).toBeInTheDocument(); + + await user.click(chatButton); + + const chatWindow = screen.getByTestId('chat-window'); + const closeButton = screen.getByTestId('chat-window-close-button'); + + expect(chatWindow).toBeInTheDocument(); + + await user.click(closeButton); + + expect(chatWindow).not.toBeInTheDocument(); + }); +}); diff --git a/src/features/chat/ui/ChatButton/index.tsx b/src/features/chat/ui/ChatButton/index.tsx index ec9ab4e..9f29b6a 100644 --- a/src/features/chat/ui/ChatButton/index.tsx +++ b/src/features/chat/ui/ChatButton/index.tsx @@ -30,6 +30,7 @@ export default function ChatButton({ isOpen, handleClick }: ChatButtonProps) { animate={isOpen ? 'open' : 'closed'} variants={buttonVariant} exit={{ opacity: 0 }} + data-testid="chat-button" > diff --git a/src/features/chat/ui/ChatWindow/index.tsx b/src/features/chat/ui/ChatWindow/index.tsx index c3cf750..fc024bf 100644 --- a/src/features/chat/ui/ChatWindow/index.tsx +++ b/src/features/chat/ui/ChatWindow/index.tsx @@ -99,6 +99,7 @@ export default function ChatWindow({ animate="open" exit="exit" variants={shadowVariant} + data-testid="chat-window" > × From 54341219d20bf95e3715224944525882c14e78d7 Mon Sep 17 00:00:00 2001 From: BHyeonKim Date: Fri, 30 May 2025 17:28:54 +0900 Subject: [PATCH 23/40] =?UTF-8?q?test:=20ChatButton=20=EB=8B=A8=EC=9C=84?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit issue: #19 --- .../chat/ui/ChatButton/ChatButton.test.tsx | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 src/features/chat/ui/ChatButton/ChatButton.test.tsx diff --git a/src/features/chat/ui/ChatButton/ChatButton.test.tsx b/src/features/chat/ui/ChatButton/ChatButton.test.tsx new file mode 100644 index 0000000..9fbf699 --- /dev/null +++ b/src/features/chat/ui/ChatButton/ChatButton.test.tsx @@ -0,0 +1,49 @@ +import { render, screen } from '@testing-library/react'; +import { describe, expect, it, vi } from 'vitest'; + +import { userEvent } from '@testing-library/user-event'; +import ChatButton from '.'; + +const props = { + isOpen: false, + handleClick: vi.fn(), +}; + +describe('ChatButton 컴포넌트 테스트', () => { + it('초기 상태에서 ChatButton이 보여진다.', () => { + render(); + + const button = screen.getByRole('button'); + + expect(button).toBeInTheDocument(); + }); + + it('button의 props로 isOpen이 true일 때 화면에 사라진다.', () => { + render(); + + const button = screen.getByRole('button'); + + expect(button).toHaveStyle({ opacity: 0, boxShadow: 'none' }); + }); + + it('button의 props로 isOpen이 false일 때 화면에 보인다.', () => { + render(); + + const button = screen.getByRole('button'); + + expect(button).toHaveStyle({ opacity: 1 }); + }); + + it('사용자가 button을 클릭하면 handleClick이 호출된다.', async () => { + const user = userEvent.setup(); + render(); + + const button = screen.getByRole('button'); + + expect(button).toBeInTheDocument(); + + await user.click(button); + + expect(props.handleClick).toHaveBeenCalledTimes(1); + }); +}); From d1ae2a5dacaa23a384d537910f1fcdde738453db Mon Sep 17 00:00:00 2001 From: BHyeonKim Date: Fri, 30 May 2025 17:46:10 +0900 Subject: [PATCH 24/40] =?UTF-8?q?test:=20ChatWindow=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EB=8B=A8=EC=9C=84=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit issue: #19 --- .../chat/ui/ChatWindow/ChatWindow.test.tsx | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 src/features/chat/ui/ChatWindow/ChatWindow.test.tsx diff --git a/src/features/chat/ui/ChatWindow/ChatWindow.test.tsx b/src/features/chat/ui/ChatWindow/ChatWindow.test.tsx new file mode 100644 index 0000000..0a50a2c --- /dev/null +++ b/src/features/chat/ui/ChatWindow/ChatWindow.test.tsx @@ -0,0 +1,51 @@ +import { render, screen } from '@testing-library/react'; +import { describe, expect, it, vi } from 'vitest'; + +import userEvent from '@testing-library/user-event'; +import ChatWindow from '.'; + +vi.mock('~/shared/hooks/useScrollToBottom', () => ({ + default: vi.fn(), +})); + +vi.mock('~/shared/hooks/useDimensions', () => ({ + default: () => ({ + height: 480, + width: 80, + }), +})); + +const props = { + children: null, + state: 'idle' as const, + inputValue: '', + messageList: [], + handleInputValueChange: vi.fn(), + handleSubmit: vi.fn(), + handleClose: vi.fn(), +}; + +describe('ChatWindow 컴포넌트 테스트', () => { + it('초기 상태에서 ChatWindow가 보여진다.', () => { + render(); + + const chatWindow = screen.getByTestId('chat-window'); + + expect(chatWindow).toBeInTheDocument(); + }); + + it('사용자는 idle 상태에서 메시지를 입력할 수 있다.', async () => { + // TODO: 테스트 작업중 + const user = userEvent.setup(); + render(); + + const chatWindow = screen.getByTestId('chat-window'); + expect(chatWindow).toBeInTheDocument(); + + const input = screen.getByRole('textbox'); + expect(input).toBeInTheDocument(); + + await user.type(input, 'test'); + expect(input).toHaveValue('test'); + }); +}); From 57738ee769dbf6868bef2b4c7c105b4ce3ae709d Mon Sep 17 00:00:00 2001 From: BHyeonKim Date: Sun, 1 Jun 2025 18:06:27 +0900 Subject: [PATCH 25/40] =?UTF-8?q?test:=20ChatWindow=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EB=8B=A8=EC=9C=84=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit issue: #19 --- .../chat/ui/ChatWindow/ChatWindow.test.tsx | 79 ++++++++++++++++++- 1 file changed, 77 insertions(+), 2 deletions(-) diff --git a/src/features/chat/ui/ChatWindow/ChatWindow.test.tsx b/src/features/chat/ui/ChatWindow/ChatWindow.test.tsx index 0a50a2c..e6357b4 100644 --- a/src/features/chat/ui/ChatWindow/ChatWindow.test.tsx +++ b/src/features/chat/ui/ChatWindow/ChatWindow.test.tsx @@ -35,7 +35,6 @@ describe('ChatWindow 컴포넌트 테스트', () => { }); it('사용자는 idle 상태에서 메시지를 입력할 수 있다.', async () => { - // TODO: 테스트 작업중 const user = userEvent.setup(); render(); @@ -46,6 +45,82 @@ describe('ChatWindow 컴포넌트 테스트', () => { expect(input).toBeInTheDocument(); await user.type(input, 'test'); - expect(input).toHaveValue('test'); + expect(props.handleInputValueChange).toHaveBeenCalledTimes(4); + }); + + it('사용자가 submit 버튼을 클릭하면 handleSubmit이 호출된다.', async () => { + const user = userEvent.setup(); + render(); + + const chatWindow = screen.getByTestId('chat-window'); + expect(chatWindow).toBeInTheDocument(); + + const submitButton = screen.getByRole('button', { name: '↑' }); + expect(submitButton).toBeInTheDocument(); + + await user.click(submitButton); + expect(props.handleSubmit).toHaveBeenCalledTimes(1); + }); + + it('processing 상태에서 input이 disabled된다.', () => { + render(); + + const chatWindow = screen.getByTestId('chat-window'); + expect(chatWindow).toBeInTheDocument(); + + const input = screen.getByRole('textbox'); + expect(input).toBeInTheDocument(); + + expect(input).toBeDisabled(); + }); + + it('complete 상태에서 input이 disabled된다.', () => { + render(); + + const chatWindow = screen.getByTestId('chat-window'); + expect(chatWindow).toBeInTheDocument(); + + const input = screen.getByRole('textbox'); + expect(input).toBeInTheDocument(); + + expect(input).toBeDisabled(); + }); + + it('processing 상태에서 submit 버튼이 disabled된다.', () => { + render(); + + const chatWindow = screen.getByTestId('chat-window'); + expect(chatWindow).toBeInTheDocument(); + + const submitButton = screen.getByRole('button', { name: '↑' }); + expect(submitButton).toBeInTheDocument(); + + expect(submitButton).toBeDisabled(); + }); + + it('complete 상태에서 submit 버튼이 disabled된다.', () => { + render(); + + const chatWindow = screen.getByTestId('chat-window'); + expect(chatWindow).toBeInTheDocument(); + + const submitButton = screen.getByRole('button', { name: '↑' }); + expect(submitButton).toBeInTheDocument(); + + expect(submitButton).toBeDisabled(); + }); + + it('사용자가 close 버튼을 누르면 handleClose 함수가 호출된다.', async () => { + const user = userEvent.setup(); + render(); + + const chatWindow = screen.getByTestId('chat-window'); + expect(chatWindow).toBeInTheDocument(); + + const closeButton = screen.getByRole('button', { name: '×' }); + expect(closeButton).toBeInTheDocument(); + + await user.click(closeButton); + expect(props.handleClose).toBeCalledTimes(1); }); }); From 6c578df968550d4760135a7e605eaefddcbcc548 Mon Sep 17 00:00:00 2001 From: BHyeonKim Date: Sun, 1 Jun 2025 18:08:19 +0900 Subject: [PATCH 26/40] =?UTF-8?q?refactor:=20ChatWindow=20=EB=B6=88?= =?UTF-8?q?=ED=95=84=EC=9A=94=20=EC=BD=94=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 불필요한 prop인 messageList와 내부 useScrollBottom 코드를 제거하였습니다 issue: #19 --- src/features/chat/ui/ChatWindow/index.tsx | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/features/chat/ui/ChatWindow/index.tsx b/src/features/chat/ui/ChatWindow/index.tsx index fc024bf..2164596 100644 --- a/src/features/chat/ui/ChatWindow/index.tsx +++ b/src/features/chat/ui/ChatWindow/index.tsx @@ -7,13 +7,11 @@ import { } from 'react'; import useDimensions from '~/shared/hooks/useDimensions'; -import useScrollToBottom from '~/shared/hooks/useScrollToBottom'; type ChatWindowProps = { children: ReactNode; state: 'idle' | 'processing' | 'complete'; inputValue: string; - messageList: unknown[]; handleInputValueChange: (e: ChangeEvent) => void; handleClose?: VoidFunction; handleSubmit: (e: FormEvent) => void; @@ -84,11 +82,10 @@ export default function ChatWindow({ handleInputValueChange: onInputValueChange, handleSubmit, handleClose, - messageList, }: ChatWindowProps) { const containerRef = useRef(null); - const messagesEndRef = useScrollToBottom([...messageList]); const { height } = useDimensions(containerRef); + const disabled = state === 'processing' || state === 'complete'; return (
-
- {children} -
-
+
{children}
@@ -140,15 +134,13 @@ export default function ChatWindow({ type="text" className="flex-1 rounded-full border border-gray-300 px-4 py-2 focus:border-blue-500 focus:outline-none" placeholder="메시지를 입력하세요..." - disabled={state === 'processing'} - value={ - state === 'processing' ? 'AI가 답변 중입니다.' : inputValue - } + disabled={disabled} + value={disabled ? 'AI가 답변 중입니다.' : inputValue} onChange={onInputValueChange} />