diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index db71e77..844b59a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,100 +2,55 @@ name: CI on: push: - branches: [ main, '**' ] pull_request: - branches: [ main ] + branches: [main] + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true jobs: - backend: + test: + timeout-minutes: 30 runs-on: ubuntu-latest - env: - CI: true - defaults: - run: - working-directory: auth-api - steps: - - uses: actions/checkout@v4 - - - name: Install Postgres server - run: | - sudo apt-get update - sudo apt-get install -y postgresql postgresql-contrib - sudo systemctl start postgresql - sudo -u postgres psql -v ON_ERROR_STOP=1 <<'SQL' - DO $$ - BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'test_user') THEN - CREATE ROLE test_user LOGIN PASSWORD 'teste123' CREATEDB; - ELSE - ALTER ROLE test_user LOGIN PASSWORD 'teste123' CREATEDB; - END IF; - END$$; - SQL - - - name: Setup Node - uses: actions/setup-node@v4 - with: - node-version: 20 - cache: npm - cache-dependency-path: auth-api/package-lock.json - - - name: Install deps (backend) - run: npm ci - - - name: Create .env for CI - run: | - cat > .env <<'EOF' - PORT=4000 - PGHOST=localhost - PGPORT=5432 - PGDATABASE=logindb - PGUSER=test_user - PGPASSWORD=teste123 - CORS_ORIGIN=http://localhost:3000 - JWT_SECRET=ci_dummy_secret - EOF - - name: Init DB (calls initDb) - run: node --input-type=module -e "import('./src/db.js').then(m=>m.initDb())" - - - name: Test (backend) -- if present - run: npm run test --if-present -- --watchAll=false --runInBand --verbose - - frontend: - runs-on: ubuntu-latest - env: - CI: true - defaults: - run: - working-directory: saude_e_bem_estar + strategy: + matrix: + node-version: [18.x] steps: - - uses: actions/checkout@v4 + - name: Checkout code + uses: actions/checkout@v4 - - name: Setup Node + - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: 20 + node-version: ${{ matrix.node-version }} cache: npm - cache-dependency-path: | - package-lock.json - - - name: Install deps (front) - run: npm ci - - name: Test (front) + # --------------------------- + # FRONTEND + # --------------------------- + - name: Install frontend dependencies + env: + CI: false run: | - npm test -- --watchAll=false --runInBand --verbose + while true; do echo "Still getting dependencies..."; sleep 60; done & + npm install + npm ci + kill %1 + working-directory: saude_e_bem_estar - - name: Build (front) - run: npm run build + - name: Build frontend + env: + CI: false + NODE_OPTIONS: --max-old-space-size=6144 + run: | + while true; do echo "Still building..."; sleep 60; done & + npm run build + kill %1 + working-directory: saude_e_bem_estar - - name: Upload build artifact - uses: actions/upload-artifact@v4 - with: - name: front-build - path: | - dist - build - if-no-files-found: ignore + - name: Run frontend tests + run: CI=false npm test -- --verbose + working-directory: saude_e_bem_estar \ No newline at end of file diff --git a/saude_e_bem_estar/package-lock.json b/saude_e_bem_estar/package-lock.json index a76ac33..d68a340 100644 --- a/saude_e_bem_estar/package-lock.json +++ b/saude_e_bem_estar/package-lock.json @@ -98,7 +98,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -709,7 +708,6 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.27.1.tgz", "integrity": "sha512-p9OkPbZ5G7UT1MofwYFigGebnrzGJacoBSQM0/6bi/PUMVE+qlWDD/OalvQKbwgQzU6dl0xAv6r4X7Jme0RYxA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -1593,7 +1591,6 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.27.1.tgz", "integrity": "sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw==", "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-module-imports": "^7.27.1", @@ -2514,7 +2511,6 @@ "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.14.6.tgz", "integrity": "sha512-4uyt8BOrBsSq6i4yiOV/gG6BnnrvTeyymlNcaN/dKvyU1GoolxAafvIvaNP1RCGPlNab3OuE4MKUQuv2lH+PLQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@firebase/component": "0.7.0", "@firebase/logger": "0.5.0", @@ -2581,7 +2577,6 @@ "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.5.6.tgz", "integrity": "sha512-YYGARbutghQY4zZUWMYia0ib0Y/rb52y72/N0z3vglRHL7ii/AaK9SA7S/dzScVOlCdnbHXz+sc5Dq+r8fwFAg==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@firebase/app": "0.14.6", "@firebase/component": "0.7.0", @@ -2597,8 +2592,7 @@ "version": "0.9.3", "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.3.tgz", "integrity": "sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw==", - "license": "Apache-2.0", - "peer": true + "license": "Apache-2.0" }, "node_modules/@firebase/auth": { "version": "1.11.1", @@ -3055,7 +3049,6 @@ "integrity": "sha512-0AZUyYUfpMNcztR5l09izHwXkZpghLgCUaAGjtMwXnCg3bj4ml5VgiwqOMOxJ+Nw4qN/zJAaOQBcJ7KGkWStqQ==", "hasInstallScript": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.1.0" }, @@ -3975,6 +3968,20 @@ "node": ">=18" } }, + "node_modules/@puppeteer/browsers/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/@puppeteer/browsers/node_modules/semver": { "version": "7.7.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", @@ -3987,6 +3994,33 @@ "node": ">=10" } }, + "node_modules/@puppeteer/browsers/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@puppeteer/browsers/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/@remix-run/router": { "version": "1.23.1", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.1.tgz", @@ -4394,7 +4428,6 @@ "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", @@ -4513,7 +4546,6 @@ "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", @@ -4836,7 +4868,6 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.6.tgz", "integrity": "sha512-p/jUvulfgU7oKtj6Xpk8cA2Y1xKTtICGpJYeJXz2YVO2UcvjQgeRMLDGfDeqeRW2Ta+0QNFwcc8X3GH8SxZz6w==", "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -4846,7 +4877,6 @@ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "license": "MIT", - "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -5207,7 +5237,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/regexpp": "^4.4.0", "@typescript-eslint/scope-manager": "5.62.0", @@ -5273,7 +5302,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", "license": "BSD-2-Clause", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "5.62.0", "@typescript-eslint/types": "5.62.0", @@ -5658,7 +5686,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5757,7 +5784,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -6784,7 +6810,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", @@ -8252,8 +8277,7 @@ "version": "0.0.1521046", "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1521046.tgz", "integrity": "sha512-vhE6eymDQSKWUXwwA37NtTTVEzjtGVfDr3pRbsWEQ5onH/Snp2c+2xZHWJJawG/0hCCJLRGt4xVtEVUVILol4w==", - "license": "BSD-3-Clause", - "peer": true + "license": "BSD-3-Clause" }, "node_modules/didyoumean": { "version": "1.2.2", @@ -8821,7 +8845,6 @@ "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -11702,7 +11725,6 @@ "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", "license": "MIT", - "peer": true, "dependencies": { "@jest/core": "^27.5.1", "import-local": "^3.0.2", @@ -13387,7 +13409,6 @@ "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", "license": "MIT", - "peer": true, "bin": { "jiti": "bin/jiti.js" } @@ -14123,7 +14144,6 @@ "resolved": "https://registry.npmjs.org/mobx/-/mobx-6.15.0.tgz", "integrity": "sha512-UczzB+0nnwGotYSgllfARAqWCJ5e/skuV2K/l+Zyck/H6pJIhLXuBnz+6vn2i211o7DtbE78HQtsYEKICHGI+g==", "license": "MIT", - "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/mobx" @@ -15004,7 +15024,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -16167,7 +16186,6 @@ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "license": "MIT", - "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -16778,7 +16796,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -16910,7 +16927,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz", "integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -16935,7 +16951,6 @@ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -17160,20 +17175,6 @@ "node": ">=14.0.0" } }, - "node_modules/react-scripts/node_modules/yaml": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", - "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", - "license": "ISC", - "optional": true, - "peer": true, - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - } - }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -17616,7 +17617,6 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz", "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", "license": "MIT", - "peer": true, "bin": { "rollup": "dist/bin/rollup" }, @@ -17896,7 +17896,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -19402,7 +19401,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -19580,7 +19578,6 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "license": "(MIT OR CC0-1.0)", - "peer": true, "engines": { "node": ">=10" }, @@ -19696,7 +19693,6 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -20053,7 +20049,6 @@ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.103.0.tgz", "integrity": "sha512-HU1JOuV1OavsZ+mfigY0j8d1TgQgbZ6M+J75zDkpEAwYeXjWSqrGJtgnPblJjd/mAyTNQ7ygw0MiKOn6etz8yw==", "license": "MIT", - "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -20931,7 +20926,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", diff --git a/saude_e_bem_estar/src/hooks/useLoginSignUp.tsx b/saude_e_bem_estar/src/hooks/useLoginSignUp.tsx index ac2743a..8c60b31 100644 --- a/saude_e_bem_estar/src/hooks/useLoginSignUp.tsx +++ b/saude_e_bem_estar/src/hooks/useLoginSignUp.tsx @@ -36,17 +36,46 @@ export function useLoginSignUp() { const handleSubmit = async () => { setLoading(true); - setMessage(""); + setMessage("Conta criada com sucesso! Você já pode fazer login."); try { if (isSignUpMode) { + // Validação do nome: apenas letras, números e underscore, sem espaços + const nameRegex = /^[A-Za-z0-9_~]+$/; + if (form.name == ""){ + throw new Error("Por favor, preencha o campo nome"); + } + if (form.email == ""){ + throw new Error("Por favor, preencha o campo email"); + } + if (form.password == ""){ + throw new Error("Por favor, preencha o campo senha"); + } + if (!nameRegex.test(form.name)) { + throw new Error("Nome inválido, por favor, não coloque caracteres especiais nem espaços!"); + } + // Validação da senha + // - Entre 6 e 20 caracteres + // - Pelo menos uma letra maiúscula + // - Pelo menos um número + // - Pelo menos um caractere especial + const password = form.password; + const lengthValid = password.length >= 6 && password.length <= 20; + const hasUppercase = /[A-Z]/.test(password); + const hasNumber = /[0-9]/.test(password); + const hasSpecialChar = /[^A-Za-z0-9]/.test(password); + if (!(lengthValid && hasUppercase && hasNumber && hasSpecialChar)) { + throw new Error("Senha inválida"); + } + + await AuthModel.signUp(form.email, form.password); + setMessage("Conta criada com sucesso! Você já pode fazer login."); const userCredential = await AuthModel.signUp(form.email, form.password); if (userCredential.user) { await sendEmailVerification(userCredential.user); } setMessage("Conta criada com sucesso! Um email de verificação foi enviado para seu email."); setAction("Login"); - } else { const userCredential = await AuthModel.login(form.email, form.password); if (userCredential.user.emailVerified) { diff --git a/saude_e_bem_estar/src/hooks/useRecipeSearch.ts b/saude_e_bem_estar/src/hooks/useRecipeSearch.ts index fd2d59d..1f364b9 100644 --- a/saude_e_bem_estar/src/hooks/useRecipeSearch.ts +++ b/saude_e_bem_estar/src/hooks/useRecipeSearch.ts @@ -16,10 +16,18 @@ export function useRecipeSearch() { /** * Perform recipe search */ - const performSearch = useCallback(async (query: string) => { + const performSearch = useCallback(async (query: string | null | undefined) => { // Validate query - if (!query || query.trim().length === 0) { + if (!query || typeof query !== 'string' || query.trim().length === 0) { setError('Por favor, digite o nome de uma receita'); + setSearchResults([]); + setHasSearched(false); + setIsLoading(false); + return; + } + + if (!/^[a-zA-Z0-9À-ÿ% ]+$/.test(query)) { + setError("Por favor, digite apenas letras e números"); return; } diff --git a/saude_e_bem_estar/src/models/firebaseAuthModel.ts b/saude_e_bem_estar/src/models/firebaseAuthModel.ts index 93adc87..ef6e469 100644 --- a/saude_e_bem_estar/src/models/firebaseAuthModel.ts +++ b/saude_e_bem_estar/src/models/firebaseAuthModel.ts @@ -9,7 +9,17 @@ import { export class FirebaseAuthModel { async signUp(email: string, password: string): Promise { - return await createUserWithEmailAndPassword(auth, email, password); + try { + return await createUserWithEmailAndPassword(auth, email, password); + } catch (error: any) { + if (error.code === "auth/email-already-in-use") { + throw new Error("Este email já está vinculado a uma conta."); + } + if (error.code === "auth/invalid-email") { + throw new Error("Este email não é válido."); + } + throw error; + } } async login(email: string, password: string): Promise { diff --git a/saude_e_bem_estar/src/pages/ProductSearchView.tsx b/saude_e_bem_estar/src/pages/ProductSearchView.tsx index 83f22e2..b284f83 100644 --- a/saude_e_bem_estar/src/pages/ProductSearchView.tsx +++ b/saude_e_bem_estar/src/pages/ProductSearchView.tsx @@ -5,9 +5,9 @@ import { SearchInput } from "../components/SearchInput"; import { SuggestionsList } from "../components/SuggestionsList"; import { ProductDetails } from "../components/ProductDetails"; import { SearchHistory } from "../components/SearchHistory"; -import { ProductComparisonModal } from "../components/ProductComparisonModal"; import "../styles/ProductSearch.css"; import AddProductToShoppingListModal from '../components/AddProductToShoppingListModal'; +import { ProductComparisonModal } from "../components/ProductComparisonModal"; const ProductSearchView: React.FC = () => { const vm = useProductSearch(); diff --git a/saude_e_bem_estar/src/pages/RecipeSearch.tsx b/saude_e_bem_estar/src/pages/RecipeSearch.tsx index afe6348..bec71d4 100644 --- a/saude_e_bem_estar/src/pages/RecipeSearch.tsx +++ b/saude_e_bem_estar/src/pages/RecipeSearch.tsx @@ -59,6 +59,7 @@ const RecipeSearch: React.FC = observer(() => { */ const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); + if (!searchQuery.trim()) return; handleSearchSubmit(); }; diff --git a/saude_e_bem_estar/src/services/IngredientMatcherService.test.ts b/saude_e_bem_estar/src/services/IngredientMatcherService.test.ts deleted file mode 100644 index 7b37eb5..0000000 --- a/saude_e_bem_estar/src/services/IngredientMatcherService.test.ts +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Test cases for ingredient extraction - * Run this in browser console to test the extraction logic - */ - -// Import the extraction function (you'll need to export it first for testing) -// For now, copy the function here for testing - -function extractMainIngredient(ingredientText: string): string { - let cleaned = ingredientText.toLowerCase(); - - // Remove frações e números (incluindo decimais, frações, etc) - cleaned = cleaned.replace(/\d+[\.,]?\d*/g, ''); - cleaned = cleaned.replace(/½|¼|¾|⅓|⅔|⅛|⅜|⅝|⅞/g, ''); - - // Remove unidades de medida comuns - cleaned = cleaned.replace(/\b(g|gr|gramas?|kg|quilogramas?)\b/gi, ''); - cleaned = cleaned.replace(/\b(ml|mililitros?|l|litros?)\b/gi, ''); - cleaned = cleaned.replace(/\b(xícara|xícaras|xic|xics)\b/gi, ''); - cleaned = cleaned.replace(/\b(colheres?|colher|col|cols)\b/gi, ''); - cleaned = cleaned.replace(/\b(sopa|sobremesa|chá|café)\b/gi, ''); - cleaned = cleaned.replace(/\b(unidade|unidades|und|pc|pç|peça|peças)\b/gi, ''); - cleaned = cleaned.replace(/\b(dente|dentes)\b/gi, ''); - cleaned = cleaned.replace(/\b(fatia|fatias|rodela|rodelas)\b/gi, ''); - cleaned = cleaned.replace(/\b(pitada|pitadas|punhado|punhados)\b/gi, ''); - cleaned = cleaned.replace(/\b(lata|latas|pacote|pacotes|caixa|caixas)\b/gi, ''); - cleaned = cleaned.replace(/\b(maço|maços|molho|molhos|ramo|ramos)\b/gi, ''); - - // Remove preposições e artigos - cleaned = cleaned.replace(/\b(de|da|do|das|dos|em|no|na|nos|nas)\b/gi, ''); - cleaned = cleaned.replace(/\b(à|ao|aos|às|para|por|com|sem)\b/gi, ''); - cleaned = cleaned.replace(/\b(o|a|os|as|um|uma|uns|umas)\b/gi, ''); - - // Remove adjetivos e estados comuns - cleaned = cleaned.replace(/\b(picado|picada|picados|picadas)\b/gi, ''); - cleaned = cleaned.replace(/\b(ralado|ralada|ralados|raladas)\b/gi, ''); - cleaned = cleaned.replace(/\b(fatiado|fatiada|fatiados|fatiadas)\b/gi, ''); - cleaned = cleaned.replace(/\b(cortado|cortada|cortados|cortadas)\b/gi, ''); - cleaned = cleaned.replace(/\b(moído|moída|moídos|moídas)\b/gi, ''); - cleaned = cleaned.replace(/\b(batido|batida|batidos|batidas)\b/gi, ''); - cleaned = cleaned.replace(/\b(cozido|cozida|cozidos|cozidas)\b/gi, ''); - cleaned = cleaned.replace(/\b(cru|crua|crus|cruas)\b/gi, ''); - cleaned = cleaned.replace(/\b(fresco|fresca|frescos|frescas)\b/gi, ''); - cleaned = cleaned.replace(/\b(seco|seca|secos|secas)\b/gi, ''); - cleaned = cleaned.replace(/\b(maduro|madura|maduros|maduras)\b/gi, ''); - cleaned = cleaned.replace(/\b(verde|verdes|vermelho|vermelha|amarelo|amarela)\b/gi, ''); - cleaned = cleaned.replace(/\b(grande|grandes|pequeno|pequena|pequenos|pequenas)\b/gi, ''); - cleaned = cleaned.replace(/\b(médio|média|médios|médias)\b/gi, ''); - cleaned = cleaned.replace(/\b(inteiro|inteira|inteiros|inteiras)\b/gi, ''); - - // Remove expressões opcionais - cleaned = cleaned.replace(/\b(a gosto|opcional|se necessário|conforme necessário|ou mais)\b/gi, ''); - cleaned = cleaned.replace(/\(.*?\)/g, ''); - - // Remove pontuação - cleaned = cleaned.replace(/[,;:.!?()]/g, ' '); - - // Normaliza espaços - cleaned = cleaned.replace(/\s+/g, ' ').trim(); - - // Singulariza palavras comuns no plural - cleaned = cleaned.replace(/\bovos\b/g, 'ovo'); - cleaned = cleaned.replace(/\btomates\b/g, 'tomate'); - cleaned = cleaned.replace(/\bcebolas\b/g, 'cebola'); - cleaned = cleaned.replace(/\balhos\b/g, 'alho'); - - if (!cleaned || cleaned.length < 2) { - return ingredientText.toLowerCase().trim(); - } - - const words = cleaned.split(/\s+/).filter(w => w.length > 2); - - if (words.length === 0) { - return ingredientText.toLowerCase().trim(); - } - - return words.slice(0, 2).join(' ').trim(); -} - -// Test cases -const testCases = [ - "4 ovos", - "200g de tomate picado", - "1 xícara de farinha de trigo", - "2 dentes de alho", - "1/2 cebola picada", - "500ml de leite", - "3 colheres de sopa de azeite", - "Sal a gosto", - "1 pacote de queijo ralado", - "250g de manteiga sem sal", - "1 lata de leite condensado", - "Pimenta do reino moída (opcional)" -]; - -console.log("=== TESTE DE EXTRAÇÃO DE INGREDIENTES ===\n"); - -testCases.forEach(test => { - const result = extractMainIngredient(test); - console.log(`"${test}"`); - console.log(` → "${result}"\n`); -}); - -export { extractMainIngredient }; diff --git a/saude_e_bem_estar/src/tests/LoginSignUp.test.tsx b/saude_e_bem_estar/src/tests/LoginSignUp.test.tsx deleted file mode 100644 index ba39b38..0000000 --- a/saude_e_bem_estar/src/tests/LoginSignUp.test.tsx +++ /dev/null @@ -1,328 +0,0 @@ -import { render, screen, fireEvent, waitFor } from "@testing-library/react"; -import "@testing-library/jest-dom"; -import LoginSignUp from "../pages/LoginSignUp"; - -// Mock mais simples do useNavigate -const mockNavigate = jest.fn(); - -// Mock completo do react-router-dom -jest.mock("react-router-dom", () => ({ - useNavigate: () => mockNavigate, -})); - -// Mock do fetch -global.fetch = jest.fn(); - -// Mock do localStorage -const localStorageMock = (() => { - let store: { [key: string]: string } = {}; - return { - getItem: jest.fn((key: string) => store[key] || null), - setItem: jest.fn((key: string, value: string) => { - store[key] = value.toString(); - }), - removeItem: jest.fn((key: string) => { - delete store[key]; - }), - clear: jest.fn(() => { - store = {}; - }), - }; -})(); - -Object.defineProperty(window, "localStorage", { - value: localStorageMock, -}); - -describe("LoginSignUp", () => { - beforeEach(() => { - jest.clearAllMocks(); - (fetch as jest.Mock).mockClear(); - mockNavigate.mockClear(); - localStorageMock.setItem.mockClear(); - localStorageMock.getItem.mockClear(); - localStorageMock.removeItem.mockClear(); - }); - - test("renderiza a tela de login por padrão", () => { - render(); - - expect(screen.getAllByText("Login")).toHaveLength(2); - expect(screen.getByPlaceholderText("Email")).toBeInTheDocument(); - expect(screen.getByPlaceholderText("Password")).toBeInTheDocument(); - expect(screen.getByText("Entrar")).toBeInTheDocument(); - expect(screen.getByText("Esqueceu a senha?")).toBeInTheDocument(); - expect(screen.getByText("Clique Aqui")).toBeInTheDocument(); - }); - - test("alterna para tela de cadastro quando clica em Sign Up", () => { - render(); - - fireEvent.click(screen.getByRole("button", { name: "Sign Up" })); - - expect(screen.getAllByText("Sign Up")).toHaveLength(2); - expect(screen.getByPlaceholderText("Name")).toBeInTheDocument(); - expect(screen.getByPlaceholderText("Email")).toBeInTheDocument(); - expect(screen.getByPlaceholderText("Password")).toBeInTheDocument(); - expect(screen.getByText("Criar conta")).toBeInTheDocument(); - expect(screen.queryByText("Esqueceu a senha? Clique Aqui")).not.toBeInTheDocument(); - }); - - test("atualiza os campos do formulário quando digitado", () => { - render(); - - const emailInput = screen.getByPlaceholderText("Email"); - const passwordInput = screen.getByPlaceholderText("Password"); - - fireEvent.change(emailInput, { target: { value: "teste@email.com" } }); - fireEvent.change(passwordInput, { target: { value: "123456" } }); - - expect(emailInput).toHaveValue("teste@email.com"); - expect(passwordInput).toHaveValue("123456"); - }); - - test("preenche campo name no modo Sign Up", () => { - render(); - - // Alterna para Sign Up - fireEvent.click(screen.getByRole("button", { name: "Sign Up" })); - - const nameInput = screen.getByPlaceholderText("Name"); - fireEvent.change(nameInput, { target: { value: "João Silva" } }); - - expect(nameInput).toHaveValue("João Silva"); - }); - - test("mostra erro para e-mail inválido no cadastro", async () => { - render(); - - // Alterna para Sign Up - fireEvent.click(screen.getByRole("button", { name: "Sign Up" })); - - // Preenche com e-mail inválido - fireEvent.change(screen.getByPlaceholderText("Name"), { - target: { value: "João" }, - }); - fireEvent.change(screen.getByPlaceholderText("Email"), { - target: { value: "email-invalido" }, - }); - fireEvent.change(screen.getByPlaceholderText("Password"), { - target: { value: "123456" }, - }); - - // Clica em criar conta - fireEvent.click(screen.getByText("Criar conta")); - - await waitFor(() => { - expect(screen.getByText("E-mail inválido.")).toBeInTheDocument(); - }); - }); - - test("realiza login com sucesso", async () => { - (fetch as jest.Mock).mockResolvedValueOnce({ - ok: true, - json: async () => ({ token: "fake-token" }), - }); - - render(); - - // Preenche os campos - fireEvent.change(screen.getByPlaceholderText("Email"), { - target: { value: "teste@email.com" }, - }); - fireEvent.change(screen.getByPlaceholderText("Password"), { - target: { value: "123456" }, - }); - - // Clica em entrar - fireEvent.click(screen.getByText("Entrar")); - - await waitFor(() => { - expect(localStorageMock.setItem).toHaveBeenCalledWith("token", "fake-token"); - expect(mockNavigate).toHaveBeenCalledWith("/Welcome"); - expect(screen.getByText("Login OK")).toBeInTheDocument(); - }); - }); - - test("mostra erro quando login falha", async () => { - (fetch as jest.Mock).mockResolvedValueOnce({ - ok: false, - json: async () => ({ error: "Credenciais inválidas" }), - }); - - render(); - - // Preenche os campos - fireEvent.change(screen.getByPlaceholderText("Email"), { - target: { value: "teste@email.com" }, - }); - fireEvent.change(screen.getByPlaceholderText("Password"), { - target: { value: "senha-errada" }, - }); - - // Clica em entrar - fireEvent.click(screen.getByText("Entrar")); - - await waitFor(() => { - expect(screen.getByText("Credenciais inválidas")).toBeInTheDocument(); - }); - }); - - test("mostra erro quando fetch falha no login", async () => { - (fetch as jest.Mock).mockRejectedValueOnce(new Error("Erro de rede")); - - render(); - - // Preenche os campos - fireEvent.change(screen.getByPlaceholderText("Email"), { - target: { value: "teste@email.com" }, - }); - fireEvent.change(screen.getByPlaceholderText("Password"), { - target: { value: "123456" }, - }); - - // Clica em entrar - fireEvent.click(screen.getByText("Entrar")); - - await waitFor(() => { - expect(screen.getByText("Erro de rede")).toBeInTheDocument(); - }); - }); - - test("mostra erro quando e-mail já existe no cadastro", async () => { - // Mock para verificação de e-mail já existente - (fetch as jest.Mock).mockResolvedValueOnce({ - ok: true, - json: async () => ({ exists: true }), - }); - - render(); - - // Alterna para Sign Up - fireEvent.click(screen.getByRole("button", { name: "Sign Up" })); - - // Preenche com e-mail válido - fireEvent.change(screen.getByPlaceholderText("Name"), { - target: { value: "João" }, - }); - fireEvent.change(screen.getByPlaceholderText("Email"), { - target: { value: "email@existente.com" }, - }); - fireEvent.change(screen.getByPlaceholderText("Password"), { - target: { value: "123456" }, - }); - - // Clica em criar conta - fireEvent.click(screen.getByText("Criar conta")); - - await waitFor(() => { - expect(screen.getByText("Este e-mail já está registrado.")).toBeInTheDocument(); - }); - }); - - test("mostra erro quando verificação de e-mail falha", async () => { - // Mock para falha na verificação de e-mail - (fetch as jest.Mock).mockRejectedValueOnce(new Error("Erro de rede")); - - render(); - - // Alterna para Sign Up - fireEvent.click(screen.getByRole("button", { name: "Sign Up" })); - - // Preenche com e-mail válido - fireEvent.change(screen.getByPlaceholderText("Name"), { - target: { value: "João" }, - }); - fireEvent.change(screen.getByPlaceholderText("Email"), { - target: { value: "joao@email.com" }, - }); - fireEvent.change(screen.getByPlaceholderText("Password"), { - target: { value: "123456" }, - }); - - // Clica em criar conta - fireEvent.click(screen.getByText("Criar conta")); - - await waitFor(() => { - expect(screen.getByText("Erro ao verificar e-mail.")).toBeInTheDocument(); - }); - }); - - test("mostra estado de carregamento durante operações", async () => { - (fetch as jest.Mock).mockImplementationOnce( - () => - new Promise((resolve) => - setTimeout( - () => - resolve({ - ok: true, - json: async () => ({ token: "fake-token" }), - }), - 100 - ) - ) - ); - - render(); - - // Preenche os campos - fireEvent.change(screen.getByPlaceholderText("Email"), { - target: { value: "teste@email.com" }, - }); - fireEvent.change(screen.getByPlaceholderText("Password"), { - target: { value: "123456" }, - }); - - // Clica em entrar - fireEvent.click(screen.getByText("Entrar")); - - // Verifica estado de carregamento - expect(screen.getByText("Entrando...")).toBeInTheDocument(); - - await waitFor(() => { - expect(screen.getByText("Login OK")).toBeInTheDocument(); - }); - }); - - test("não permite múltiplas submissões durante carregamento", async () => { - (fetch as jest.Mock).mockImplementationOnce( - () => - new Promise((resolve) => - setTimeout( - () => - resolve({ - ok: true, - json: async () => ({ token: "fake-token" }), - }), - 100 - ) - ) - ); - - render(); - - // Preenche os campos - fireEvent.change(screen.getByPlaceholderText("Email"), { - target: { value: "teste@email.com" }, - }); - fireEvent.change(screen.getByPlaceholderText("Password"), { - target: { value: "123456" }, - }); - - // Clica em entrar múltiplas vezes - fireEvent.click(screen.getByText("Entrar")); - - // Verifica que o botão está em estado de carregamento - const loadingButton = screen.getByText("Entrando..."); - expect(loadingButton).toBeDisabled(); - - // Tenta clicar novamente no botão desabilitado - fireEvent.click(loadingButton); - - // Fetch deve ser chamado apenas uma vez - await waitFor(() => { - expect(fetch).toHaveBeenCalledTimes(1); - }); - }); - -}); \ No newline at end of file diff --git a/saude_e_bem_estar/src/tests/ProductSearch.test.tsx b/saude_e_bem_estar/src/tests/ProductSearch.test.tsx deleted file mode 100644 index 8f13151..0000000 --- a/saude_e_bem_estar/src/tests/ProductSearch.test.tsx +++ /dev/null @@ -1,222 +0,0 @@ -import { render, screen, fireEvent, waitFor } from "@testing-library/react"; -import "@testing-library/jest-dom"; -import ProductSearchView from "../pages/ProductSearchView"; -import Papa from "papaparse"; - -jest.mock("papaparse"); - -const localStorageMock = { - getItem: jest.fn(), - setItem: jest.fn(), - removeItem: jest.fn(), - clear: jest.fn(), -}; -Object.defineProperty(window, 'localStorage', { value: localStorageMock }); - -describe("ProductSearchView", () => { - beforeEach(() => { - // Limpa os mocks antes de cada teste - // jest.clearAllMocks(); - localStorageMock.getItem.mockReturnValue(null); - - // Set up the mock implementation for Papa.parse - (Papa.parse as jest.Mock).mockImplementation((file: any, options: any) => { - // Simula dados do CSV de alimentos - const mockData = [ - { - nome: "Abacaxi, cozido, caramelado", - energia_kcal: 136, - carboidrato_total_g: 33.3, - proteina_g: 0.60, - lipidios_g: 0.26, - fibra_alimentar_g: 0.84 - }, - { - nome: "Abacate, polpa, in natura, Brasil", - energia_kcal: 76, - carboidrato_total_g: 5.84, - proteina_g: 1.15, - lipidios_g: 6.21, - fibra_alimentar_g: 4.03 - }, - { - nome: "Abóbora, moranga, refogada (c/ óleo, cebola e alho), s/ sal", - energia_kcal: 30, - carboidrato_total_g: 5.98, - proteina_g: 0.39, - lipidios_g: 0.80, - fibra_alimentar_g: 1.55 - }, - ]; - // Simula o comportamento assíncrono do Papa.parse - setTimeout(() => { - options.complete({ data: mockData }); - }, 0); - }); - }); - - test("mostra o título corretamente", () => { - render(); - expect(screen.getByText("Busca de Produtos")).toBeInTheDocument(); - }); - - test("pesquisa por um item específico", async () => { - render(); - - const searchInput = screen.getByPlaceholderText("Search for a product..."); - - // Digita "Abacaxi" no campo de busca - fireEvent.change(searchInput, { target: { value: "Aba" } }); - - // Aguarda as sugestões aparecerem - await waitFor(() => { - return expect(screen.getByText("Abacaxi, cozido, caramelado")).toBeInTheDocument(); - }); - }); - - test("limpa o histórico corretamente", async () => { - // Mock do localStorage com histórico existente - const mockHistory = JSON.stringify([ - { - name: "Arroz branco", - calories: 130, - carbs: 28.1, - protein: 2.5, - fat: 0.3, - fiber: 0.4 - } - ]); - localStorageMock.getItem.mockReturnValue(mockHistory); - - render(); - - // Verifica que o histórico está sendo exibido - await waitFor(() => { - expect(screen.getByText("Arroz branco")).toBeInTheDocument(); - }); - - // Clica no botão "Clear History" - const clearButton = screen.getByText("Clear History"); - fireEvent.click(clearButton); - - // Verifica que o histórico foi limpo - expect(screen.getByText("No products searched yet")).toBeInTheDocument(); - expect(screen.queryByText("Arroz branco")).not.toBeInTheDocument(); - }); - - test("apresenta macronutrientes corretamente", async () => { - render(); - - const searchInput = screen.getByPlaceholderText("Search for a product..."); - - // Busca por "Banana" - fireEvent.change(searchInput, { target: { value: "Abacate, polpa, in natura, Brasil" } }); - - // Aguarda e clica na sugestão - await waitFor(() => { - const abacateOption = screen.getByText("Abacate, polpa, in natura, Brasil"); - fireEvent.click(abacateOption); - }); - - // Verifica se os macronutrientes estão sendo exibidos corretamente - await waitFor(() => { - expect(screen.getByRole('heading', { level: 2, name: "Abacate, polpa, in natura, Brasil" })).toBeInTheDocument(); - expect(screen.getByText("Energia em kcal: 76")).toBeInTheDocument(); - expect(screen.getByText("Carboidratos: 5.84g")).toBeInTheDocument(); - expect(screen.getByText("Proteina: 1.15g")).toBeInTheDocument(); - expect(screen.getByText("Lipidios: 6.21g")).toBeInTheDocument(); - expect(screen.getByText("Fibras: 4.03g")).toBeInTheDocument(); - }); - }); - - test("aparece opções quando digita uma letra", async () => { - render(); - - const searchInput = screen.getByPlaceholderText("Search for a product..."); - - // Digita apenas "A" - fireEvent.change(searchInput, { target: { value: "Aba" } }); - - // Aguarda as sugestões que começam com "A" - await waitFor(() => { - expect(screen.getByText("Abacate, polpa, in natura, Brasil")).toBeInTheDocument(); - expect(screen.getByText("Abacaxi, cozido, caramelado")).toBeInTheDocument(); - }); - }); - - test("muda entre informações de alimentos corretamente", async () => { - render(); - - const searchInput = screen.getByPlaceholderText("Search for a product..."); - - // Primeira busca - Abacaxi - fireEvent.change(searchInput, { target: { value: "Abacaxi, cozido, caramelado" } }); - - await waitFor(() => { - const abacaxiOption = screen.getAllByText("Abacaxi, cozido, caramelado"); - fireEvent.click(abacaxiOption[0]); - }); - // Segunda busca - Feijão preto - fireEvent.change(searchInput, { target: { value: "Abóbora, moranga, refogada (c/ óleo, cebola e alho), s/ sal" } }); - - await waitFor(() => { - const aboboraOption = screen.getAllByText("Abóbora, moranga, refogada (c/ óleo, cebola e alho), s/ sal"); - fireEvent.click(aboboraOption[0]); - }); - - // Verifica que mudou para informações da abobora - await waitFor(() => { - expect(screen.getByRole("heading", { level: 2, name: "Abóbora, moranga, refogada (c/ óleo, cebola e alho), s/ sal" })).toBeInTheDocument(); - expect(screen.getByText("Energia em kcal: 30")).toBeInTheDocument(); - expect(screen.getByText("Carboidratos: 5.98g")).toBeInTheDocument(); - expect(screen.getByText("Proteina: 0.39g")).toBeInTheDocument(); - }); - - // Verifica que as informações antigas não estão mais visíveis - expect(screen.queryByText("Energia em kcal: 136")).not.toBeInTheDocument(); - }); - - test("renderiza lista de alimentos já pesquisados corretamente", async () => { - // Mock do localStorage com histórico de múltiplos produtos - const mockHistory = JSON.stringify([ - { - name: "Arroz branco", - calories: 130, - carbs: 28.1, - protein: 2.5, - fat: 0.3, - fiber: 0.4 - }, - { - name: "Feijão preto", - calories: 77, - carbs: 14.0, - protein: 4.5, - fat: 0.5, - fiber: 8.4 - } - ]); - localStorageMock.getItem.mockReturnValue(mockHistory); - - render(); - - // Verifica que o título do histórico está presente - expect(screen.getByText("Buscados Anteriormente")).toBeInTheDocument(); - - // Verifica que os itens do histórico estão sendo renderizados - await waitFor(() => { - expect(screen.getByText("Arroz branco")).toBeInTheDocument(); - expect(screen.getByText("Feijão preto")).toBeInTheDocument(); - }); - - // Testa clique em item do histórico - const historicoArroz = screen.getByText("Arroz branco"); - fireEvent.click(historicoArroz); - - // Verifica que as informações do arroz são exibidas - await waitFor(() => { - expect(screen.getByText("Energia em kcal: 130")).toBeInTheDocument(); - expect(screen.getByText("Carboidratos: 28.1g")).toBeInTheDocument(); - }); - }); -}); \ No newline at end of file diff --git a/saude_e_bem_estar/src/tests/RecipeSearch.test.tsx b/saude_e_bem_estar/src/tests/RecipeSearch.test.tsx new file mode 100644 index 0000000..29331cf --- /dev/null +++ b/saude_e_bem_estar/src/tests/RecipeSearch.test.tsx @@ -0,0 +1,110 @@ +import { render, screen, fireEvent, waitFor } from "@testing-library/react"; +import "@testing-library/jest-dom"; +import RecipeSearch from "../pages/RecipeSearch"; +import { searchRecipes } from "../services/RecipeService"; +import { FavoritesViewModel } from "../hooks/useFavorites"; +import { MealPlannerViewModel } from "../hooks/MealPlannerHook"; + +// === 1. Mocks de Componentes Visuais === +jest.mock("../components/PageHeader", () => ({ + PageHeader: () =>
Header Mock
+})); + +jest.mock("../components/AddRecipeToMealPlanModal", () => ({ + AddRecipeToMealPlanModal: () =>
Modal Mock
+})); + +jest.mock("../services/RecipeService"); +jest.mock("../hooks/useFavorites"); +jest.mock("../hooks/MealPlannerHook"); +jest.mock("../models/firebaseMealPlannerModel"); +jest.mock("../models/firebaseFavoritesModel"); + +// Mock do React Router +const mockNavigate = jest.fn(); +jest.mock("react-router-dom", () => ({ + useNavigate: () => mockNavigate, +})); + +// Mock do useAuth +jest.mock("../hooks/useAuth", () => ({ + useAuth: () => ({ currentUser: { uid: "user-123" } }), +})); + +describe("RecipeSearch", () => { + beforeEach(() => { + jest.clearAllMocks(); + // Definimos a implementação da classe FavoritesViewModel ANTES de renderizar + (FavoritesViewModel as jest.Mock).mockImplementation(() => ({ + // O componente chama isFavorite durante o render. Se não existir, quebra. + isFavorite: jest.fn().mockReturnValue(false), + toggleFavorite: jest.fn().mockResolvedValue(true) + })); + // Mock do MealPlannerViewModel (necessário pois é instanciado no componente) + (MealPlannerViewModel as jest.Mock).mockImplementation(() => ({ + })); + }); + const getInput = () => + screen.getByPlaceholderText("Digite o nome da receita (ex: bolo de cenoura)"); + + test("Busca com texto válido retorna receitas (Cenário das Imagens)", async () => { + // Configura o serviço para retornar os 5 bolos da imagem + (searchRecipes as jest.Mock).mockResolvedValue([ + { id: '1', title: 'Bolo de cenoura' }, + { id: '2', title: 'Bolo de fubá da vó Maria' }, + { id: '3', title: 'Bolo gelado' }, + { id: '4', title: 'Bolo prestígio' }, + { id: '5', title: 'Bolo simples' } + ]); + render(); + // 1. Simula a digitação "Bolo" + fireEvent.change(getInput(), { target: { value: "Bolo" } }); + // 2. Clica em Buscar + fireEvent.click(screen.getByRole("button", { name: "Buscar" })); + // 3. Verifica o resultado + await waitFor(() => { + expect(screen.getByText("Encontramos 5 receitas")).toBeInTheDocument(); + expect(screen.getByText("Bolo de cenoura")).toBeInTheDocument(); + expect(screen.getByText("Bolo de fubá da vó Maria")).toBeInTheDocument(); + expect(screen.getByText("Bolo gelado")).toBeInTheDocument(); + }); + }); + + test("Busca com texto vazio mostra erro", async () => { + render(); + fireEvent.change(getInput(), { target: { value: "" } }); + fireEvent.keyPress(getInput(), { key: 'Enter', code: 'Enter', charCode: 13 }); + fireEvent.click(screen.getByRole("button", { name: "Buscar" })); + await waitFor(() => { + expect(screen.getByText("Por favor, digite o nome de uma receita")).toBeInTheDocument(); + }); + }); + + test("Busca com letras, números e acentos retorna receitas", async () => { + (searchRecipes as jest.Mock).mockResolvedValue([ + { id: '10', title: 'Bolacha de trigo' }, + { id: '11', title: 'Bolo de Natal' }, + { id: '12', title: 'Milkshake de maracujá cremoso' }, + { id: '13', title: 'Bolo de caneca sabor chocolate diet, light, sem glútem e lactose' }, // Título longo da imagem + { id: '14', title: 'Boloarte da Titina- Bolo com Dripping' } + ]); + render(); + fireEvent.change(getInput(), { target: { value: "Bolo 70%" } }); + fireEvent.click(screen.getByRole("button", { name: "Buscar" })); + await waitFor(() => { + expect(screen.getByText("Encontramos 5 receitas")).toBeInTheDocument(); + expect(screen.getByText("Bolacha de trigo")).toBeInTheDocument(); + expect(screen.getByText("Bolo de Natal")).toBeInTheDocument(); + expect(screen.getByText(/Milkshake de maracujá/i)).toBeInTheDocument(); + }); + }); + + test("Busca com caracteres especiais mostra erro", async () => { + render(); + fireEvent.change(getInput(), { target: { value: "@#!@#$@" } }); + fireEvent.click(screen.getByRole("button", { name: "Buscar" })); + await waitFor(() => { + expect(screen.getByText("Por favor, digite apenas letras e números")).toBeInTheDocument(); + }); + }); +}); \ No newline at end of file diff --git a/saude_e_bem_estar/src/tests/ShoppingList.test.tsx b/saude_e_bem_estar/src/tests/ShoppingList.test.tsx deleted file mode 100644 index ed7e2d3..0000000 --- a/saude_e_bem_estar/src/tests/ShoppingList.test.tsx +++ /dev/null @@ -1,274 +0,0 @@ -import { render, screen, fireEvent } from "@testing-library/react"; -import "@testing-library/jest-dom"; -import { BrowserRouter } from "react-router-dom"; -import ShoppingListDetail from "../pages/ShoppingListDetail"; -import { useShoppingListDetailViewModel } from "../hooks/useShoppingListDetailHook"; - -// Mock do hook customizado -jest.mock("../hooks/useShoppingListDetailHook"); - -// Mock do useNavigate -const mockNavigate = jest.fn(); -jest.mock("react-router-dom", () => ({ - ...jest.requireActual("react-router-dom"), - useNavigate: () => mockNavigate, -})); - -// Helper para criar o mock padrão do hook -const createMockViewModel = (overrides = {}) => ({ - items: [], - isLoading: false, - error: null, - listName: "Lista de Compras", - stats: { total: 0, checked: 0, unchecked: 0, progress: 0 }, - addItem: jest.fn(), - toggleItem: jest.fn(), - deleteItem: jest.fn(), - clearCheckedItems: jest.fn(), - reloadList: jest.fn(), - ...overrides, -}); - -// Wrapper com Router para testes -const renderWithRouter = (component: React.ReactElement) => { - return render({component}); -}; - -describe("ShoppingListDetail", () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - test("mostra o título corretamente", () => { - (useShoppingListDetailViewModel as jest.Mock).mockReturnValue( - createMockViewModel() - ); - - renderWithRouter(); - expect(screen.getByText("Lista de Compras")).toBeInTheDocument(); - }); - - test("mostra estado de carregamento", () => { - (useShoppingListDetailViewModel as jest.Mock).mockReturnValue( - createMockViewModel({ isLoading: true }) - ); - - renderWithRouter(); - expect(screen.getByText("Carregando...")).toBeInTheDocument(); - }); - - test("mostra mensagem de erro quando há erro", () => { - (useShoppingListDetailViewModel as jest.Mock).mockReturnValue( - createMockViewModel({ - error: "Lista não encontrada", - isLoading: false - }) - ); - - renderWithRouter(); - expect(screen.getByText("Erro")).toBeInTheDocument(); - expect(screen.getByText("Lista não encontrada")).toBeInTheDocument(); - }); - - test("adiciona um item na lista", () => { - const mockAddItem = jest.fn(); - (useShoppingListDetailViewModel as jest.Mock).mockReturnValue( - createMockViewModel({ addItem: mockAddItem }) - ); - - renderWithRouter(); - - // digita no input - const input = screen.getByPlaceholderText("Adicione um item à lista"); - fireEvent.change(input, { target: { value: "Arroz" } }); - - // clica no botão de adicionar - fireEvent.click(screen.getByRole("button", { name: "+" })); - - // verifica se a função foi chamada - expect(mockAddItem).toHaveBeenCalledWith("Arroz"); - }); - - test("renderiza a lista com itens existentes", () => { - const mockItems = [ - { id: "1", text: "Arroz", checked: false }, - { id: "2", text: "Feijão", checked: false }, - ]; - - (useShoppingListDetailViewModel as jest.Mock).mockReturnValue( - createMockViewModel({ - items: mockItems, - stats: { total: 2, checked: 0, unchecked: 2, progress: 0 } - }) - ); - - renderWithRouter(); - - expect(screen.getByText("Arroz")).toBeInTheDocument(); - expect(screen.getByText("Feijão")).toBeInTheDocument(); - }); - - test("remove um item da lista quando clicado no botão de deletar", () => { - const mockDeleteItem = jest.fn(); - const mockItems = [ - { id: "1", text: "Pirulito", checked: false } - ]; - - (useShoppingListDetailViewModel as jest.Mock).mockReturnValue( - createMockViewModel({ - items: mockItems, - deleteItem: mockDeleteItem, - stats: { total: 1, checked: 0, unchecked: 1, progress: 0 } - }) - ); - - renderWithRouter(); - - // garante que está na tela - expect(screen.getByText("Pirulito")).toBeInTheDocument(); - - // clica no botão de deletar - fireEvent.click(screen.getByRole("button", { name: "🗑️" })); - - // verifica se a função foi chamada com o ID correto - expect(mockDeleteItem).toHaveBeenCalledWith("1"); - }); - - test("marca/desmarca um item quando clicado no checkbox", () => { - const mockToggleItem = jest.fn(); - const mockItems = [ - { id: "1", text: "Manteiga", checked: false } - ]; - - (useShoppingListDetailViewModel as jest.Mock).mockReturnValue( - createMockViewModel({ - items: mockItems, - toggleItem: mockToggleItem, - stats: { total: 1, checked: 0, unchecked: 1, progress: 0 } - }) - ); - - renderWithRouter(); - - // garante que está na tela - expect(screen.getByText("Manteiga")).toBeInTheDocument(); - - // clica no checkbox - const checkbox = screen.getByRole("checkbox"); - fireEvent.click(checkbox); - - // verifica se a função foi chamada - expect(mockToggleItem).toHaveBeenCalledWith("1", true); - }); - - test("exibe item com texto riscado quando marcado", () => { - const mockItems = [ - { id: "1", text: "Manteiga", checked: true } - ]; - - (useShoppingListDetailViewModel as jest.Mock).mockReturnValue( - createMockViewModel({ - items: mockItems, - stats: { total: 1, checked: 1, unchecked: 0, progress: 100 } - }) - ); - - renderWithRouter(); - - const itemText = screen.getByText("Manteiga"); - expect(itemText).toHaveStyle("text-decoration: line-through"); - }); - - test("mostra estatísticas quando há itens", () => { - const mockItems = [ - { id: "1", text: "Arroz", checked: true }, - { id: "2", text: "Feijão", checked: false }, - { id: "3", text: "Açúcar", checked: true }, - ]; - - (useShoppingListDetailViewModel as jest.Mock).mockReturnValue( - createMockViewModel({ - items: mockItems, - stats: { total: 3, checked: 2, unchecked: 1, progress: 66.67 } - }) - ); - - renderWithRouter(); - - expect(screen.getByText(/2 de 3 itens completos/i)).toBeInTheDocument(); - }); - - test("exibe botão de limpar marcados quando há itens marcados", () => { - const mockClearCheckedItems = jest.fn(); - const mockItems = [ - { id: "1", text: "Arroz", checked: true }, - { id: "2", text: "Feijão", checked: false }, - ]; - - (useShoppingListDetailViewModel as jest.Mock).mockReturnValue( - createMockViewModel({ - items: mockItems, - stats: { total: 2, checked: 1, unchecked: 1, progress: 50 }, - clearCheckedItems: mockClearCheckedItems - }) - ); - - renderWithRouter(); - - const clearButton = screen.getByText("Limpar marcados"); - expect(clearButton).toBeInTheDocument(); - - fireEvent.click(clearButton); - expect(mockClearCheckedItems).toHaveBeenCalled(); - }); - - test("não mostra seção de estatísticas quando não há itens", () => { - (useShoppingListDetailViewModel as jest.Mock).mockReturnValue( - createMockViewModel({ - items: [], - stats: { total: 0, checked: 0, unchecked: 0, progress: 0 } - }) - ); - - renderWithRouter(); - - expect(screen.queryByText(/itens completos/i)).not.toBeInTheDocument(); - }); - - test("chama onBack quando fornecido", () => { - const mockOnBack = jest.fn(); - (useShoppingListDetailViewModel as jest.Mock).mockReturnValue( - createMockViewModel() - ); - - renderWithRouter(); - - const backButton = screen.getByText("← Voltar"); - fireEvent.click(backButton); - - expect(mockOnBack).toHaveBeenCalled(); - expect(mockNavigate).not.toHaveBeenCalled(); - }); - - test("usa navigate quando onBack não é fornecido", () => { - (useShoppingListDetailViewModel as jest.Mock).mockReturnValue( - createMockViewModel() - ); - - renderWithRouter(); - - const backButton = screen.getByText("← Voltar"); - fireEvent.click(backButton); - - expect(mockNavigate).toHaveBeenCalledWith(-1); - }); - - test("passa listId para o hook quando fornecido", () => { - const mockUseViewModel = useShoppingListDetailViewModel as jest.Mock; - mockUseViewModel.mockReturnValue(createMockViewModel()); - - renderWithRouter(); - - expect(mockUseViewModel).toHaveBeenCalledWith({ listId: "lista-123" }); - }); -}); \ No newline at end of file diff --git a/saude_e_bem_estar/src/tests/SignUp.test.tsx b/saude_e_bem_estar/src/tests/SignUp.test.tsx index 3d4702b..d4e7a4e 100644 --- a/saude_e_bem_estar/src/tests/SignUp.test.tsx +++ b/saude_e_bem_estar/src/tests/SignUp.test.tsx @@ -1,19 +1,23 @@ import { render, screen, fireEvent, waitFor } from "@testing-library/react"; import "@testing-library/jest-dom"; import LoginSignUp from "../pages/LoginSignUp"; +import { FirebaseAuthModel } from "../models/firebaseAuthModel"; -// Mock mais simples do useNavigate -const mockNavigate = jest.fn(); +jest.mock("../components/PageHeader", () => ({ + PageHeader: () =>
Header Mock
+})); + +// Mockamos os modelos +jest.mock("../models/firebaseAuthModel"); +jest.mock("../hooks/useAuth"); -// Mock completo do react-router-dom +const mockNavigate = jest.fn(); jest.mock("react-router-dom", () => ({ useNavigate: () => mockNavigate, + Link: ({ children }: any) => {children} })); -// Mock do fetch -global.fetch = jest.fn(); - -// Mock do localStorage +// === 3. Mock do LocalStorage === const localStorageMock = (() => { let store: { [key: string]: string } = {}; return { @@ -29,339 +33,213 @@ const localStorageMock = (() => { }), }; })(); +Object.defineProperty(window, "localStorage", { value: localStorageMock }); -Object.defineProperty(window, "localStorage", { - value: localStorageMock, -}); +describe("SignUp", () => { + let mockSignUp: jest.Mock; + let mockLogin: jest.Mock; + let mockLoginWithGoogle: jest.Mock; -describe("LoginSignUp", () => { beforeEach(() => { jest.clearAllMocks(); - (fetch as jest.Mock).mockClear(); mockNavigate.mockClear(); - localStorageMock.setItem.mockClear(); - localStorageMock.getItem.mockClear(); - localStorageMock.removeItem.mockClear(); + + mockSignUp = jest.fn(); + mockLogin = jest.fn(); + mockLoginWithGoogle = jest.fn(); + + (FirebaseAuthModel as jest.Mock).mockImplementation(() => ({ + signUp: mockSignUp, + login: mockLogin, + loginWithGoogle: mockLoginWithGoogle, + })); + + const useAuthModule = require("../hooks/useAuth"); + useAuthModule.useAuth.mockReturnValue({ + currentUser: null, + loading: false, + isAuthenticated: false, + userId: null, + }); }); test("alterna para tela de cadastro quando clica em Sign Up", () => { render(); - fireEvent.click(screen.getByRole("button", { name: "Sign Up" })); - - expect(screen.getAllByText("Sign Up")).toHaveLength(2); + expect(screen.getAllByText(/Sign Up/i).length).toBeGreaterThanOrEqual(1); expect(screen.getByPlaceholderText("Name")).toBeInTheDocument(); expect(screen.getByPlaceholderText("Email")).toBeInTheDocument(); - expect(screen.getByPlaceholderText("Password")).toBeInTheDocument(); - expect(screen.getByText("Criar conta")).toBeInTheDocument(); - expect(screen.queryByText("Esqueceu a senha? Clique Aqui")).not.toBeInTheDocument(); }); test("preenche os campos no modo Sign Up", () => { render(); - // Alterna para Sign Up fireEvent.click(screen.getByRole("button", { name: "Sign Up" })); const nameInput = screen.getByPlaceholderText("Name"); const emailInput = screen.getByPlaceholderText("Email"); const passwordInput = screen.getByPlaceholderText("Password"); - - fireEvent.change(nameInput, { target: { value: "João Silva" } }); + fireEvent.change(nameInput, { target: { value: "JoaoSilva" } }); fireEvent.change(emailInput, { target: { value: "joao@email.com" } }); fireEvent.change(passwordInput, { target: { value: "123456" } }); - - expect(nameInput).toHaveValue("João Silva"); + expect(nameInput).toHaveValue("JoaoSilva"); expect(emailInput).toHaveValue("joao@email.com"); expect(passwordInput).toHaveValue("123456"); }); test("mostra erro quando nome está vazio no cadastro", async () => { render(); - // Alterna para Sign Up fireEvent.click(screen.getByRole("button", { name: "Sign Up" })); - // Preenche com nome vazio - fireEvent.change(screen.getByPlaceholderText("Name"), { - target: { value: "" }, - }); - fireEvent.change(screen.getByPlaceholderText("Email"), { - target: { value: "teste@email.com" }, - }); - fireEvent.change(screen.getByPlaceholderText("Password"), { - target: { value: "12345678" }, - }); - // Clica em criar conta - fireEvent.click(screen.getByText("Criar conta")); - + fireEvent.change(screen.getByPlaceholderText("Name"), { target: { value: "" } }); + fireEvent.change(screen.getByPlaceholderText("Email"), { target: { value: "teste@email.com" } }); + fireEvent.change(screen.getByPlaceholderText("Password"), { target: { value: "12345678A@" } }); + fireEvent.click(screen.getByText("Cadastrar")); await waitFor(() => { - expect(screen.getByText("O nome é obrigatório.")).toBeInTheDocument(); + expect(screen.getByText("Por favor, preencha o campo nome")).toBeInTheDocument(); }); }); test("mostra erro quando email está vazio no cadastro", async () => { render(); - // Alterna para Sign Up fireEvent.click(screen.getByRole("button", { name: "Sign Up" })); - // Preenche com e-mail vazio - fireEvent.change(screen.getByPlaceholderText("Name"), { - target: { value: "João" }, - }); - fireEvent.change(screen.getByPlaceholderText("Email"), { - target: { value: "" }, - }); - fireEvent.change(screen.getByPlaceholderText("Password"), { - target: { value: "12345678" }, - }); - // Clica em criar conta - fireEvent.click(screen.getByText("Criar conta")); - + fireEvent.change(screen.getByPlaceholderText("Name"), { target: { value: "Joao" } }); + fireEvent.change(screen.getByPlaceholderText("Email"), { target: { value: "" } }); + fireEvent.change(screen.getByPlaceholderText("Password"), { target: { value: "12345678A@" } }); + fireEvent.click(screen.getByText("Cadastrar")); await waitFor(() => { - expect(screen.getByText("O e-mail é obrigatório.")).toBeInTheDocument(); + expect(screen.getByText("Por favor, preencha o campo email")).toBeInTheDocument(); }); }); test("mostra erro quando senha está vazia no cadastro", async () => { render(); - // Alterna para Sign Up fireEvent.click(screen.getByRole("button", { name: "Sign Up" })); - - // Preenche com senha vazia - fireEvent.change(screen.getByPlaceholderText("Name"), { - target: { value: "João" }, - }); - fireEvent.change(screen.getByPlaceholderText("Email"), { - target: { value: "" }, - }); - fireEvent.change(screen.getByPlaceholderText("Password"), { - target: { value: "" }, - }); - // Clica em criar conta - fireEvent.click(screen.getByText("Criar conta")); - + fireEvent.change(screen.getByPlaceholderText("Name"), {target: { value: "João" },}); + fireEvent.change(screen.getByPlaceholderText("Email"), {target: { value: "joao@silva.com" },}); + fireEvent.change(screen.getByPlaceholderText("Password"), {target: { value: "" },}); + fireEvent.click(screen.getByText("Cadastrar")); await waitFor(() => { - expect(screen.getByText("A senha é obrigatória.")).toBeInTheDocument(); + expect(screen.getByText("Por favor, preencha o campo senha")).toBeInTheDocument(); }); }); test("mostra erro quando e-mail já existe no cadastro", async () => { - // Mock para verificação de e-mail já existente - (fetch as jest.Mock).mockResolvedValueOnce({ - ok: true, - json: async () => ({ exists: true }), - }); + mockSignUp.mockRejectedValueOnce(new Error("Este email já está vinculado a uma conta.")); render(); - - // Alterna para Sign Up fireEvent.click(screen.getByRole("button", { name: "Sign Up" })); - - // Preenche com e-mail válido - fireEvent.change(screen.getByPlaceholderText("Name"), { - target: { value: "João" }, - }); - const emailInput = screen.getByPlaceholderText("Email"); - const passwordInput = screen.getByPlaceholderText("Password"); - fireEvent.change(emailInput, { target: { value: "teste@email.com" } }); - fireEvent.change(passwordInput, { target: { value: "Invalid" } }); - // Clica em criar conta - fireEvent.click(screen.getByText("Criar conta")); + fireEvent.change(screen.getByPlaceholderText("Name"), {target: { value: "Joao" },}); + fireEvent.change(screen.getByPlaceholderText("Email"), { target: { value: "polas@mail.com" } }); + fireEvent.change(screen.getByPlaceholderText("Password"), { target: { value: "Invalid123*" } }); + fireEvent.click(screen.getByText("Cadastrar")); await waitFor(() => { - expect(screen.getByText(/senha ou email inválidos/i)).toBeInTheDocument(); + expect(screen.getByText("Este email já está vinculado a uma conta.")).toBeInTheDocument(); }); }); test("mostra erro para e-mail inválido no cadastro", async () => { + mockSignUp.mockRejectedValueOnce(new Error("Este email não é válido.")); render(); - - // Alterna para Sign Up fireEvent.click(screen.getByRole("button", { name: "Sign Up" })); - - // Preenche com e-mail inválido - fireEvent.change(screen.getByPlaceholderText("Name"), { - target: { value: "João" }, - }); - fireEvent.change(screen.getByPlaceholderText("Email"), { - target: { value: "email-invalido" }, - }); - fireEvent.change(screen.getByPlaceholderText("Password"), { - target: { value: "123456" }, - }); - - // Clica em criar conta - fireEvent.click(screen.getByText("Criar conta")); - + fireEvent.change(screen.getByPlaceholderText("Name"), {target: { value: "Joaoo" },}); + fireEvent.change(screen.getByPlaceholderText("Email"), {target: { value: "emailinvalido" },}); + fireEvent.change(screen.getByPlaceholderText("Password"), {target: { value: "1234Lucas&*" },}); + fireEvent.click(screen.getByText("Cadastrar")); await waitFor(() => { - expect(screen.getByText("E-mail inválido.")).toBeInTheDocument(); + expect(screen.getByText("Este email não é válido.")).toBeInTheDocument(); }); }); test("mostra erro de nome de usuário invalido no cadastro", async () => { render(); - - // Alterna para Sign Up - fireEvent.click(screen.getByRole("button", { name: "Sign Up" })); - // Preenche com nome inválido - fireEvent.change(screen.getByPlaceholderText("Name"), { - target: { value: "João123" }, - }); - fireEvent.change(screen.getByPlaceholderText("Email"), { - target: { value: "joao@email.com" }, - }); - fireEvent.change(screen.getByPlaceholderText("Password"), { - target: { value: "12345678" }, - }); - // Clica em criar conta - fireEvent.click(screen.getByText("Criar conta")); - - await waitFor(() => { - expect(screen.getByText("Nome de usuário inválido.")).toBeInTheDocument(); - }); - }); - - test("mostra erro quando verificação de e-mail falha", async () => { - // Mock para falha na verificação de e-mail - (fetch as jest.Mock).mockRejectedValueOnce(new Error("Erro de rede")); - - render(); - - // Alterna para Sign Up fireEvent.click(screen.getByRole("button", { name: "Sign Up" })); - - // Preenche com e-mail válido - fireEvent.change(screen.getByPlaceholderText("Name"), { - target: { value: "João" }, - }); - fireEvent.change(screen.getByPlaceholderText("Email"), { - target: { value: "joao@email.com" }, - }); - fireEvent.change(screen.getByPlaceholderText("Password"), { - target: { value: "123456" }, - }); - - // Clica em criar conta - fireEvent.click(screen.getByText("Criar conta")); - + fireEvent.change(screen.getByPlaceholderText("Name"), { target: { value: "João123" } }); + fireEvent.change(screen.getByPlaceholderText("Email"), { target: { value: "joao@email.com" } }); + fireEvent.change(screen.getByPlaceholderText("Password"), { target: { value: "12345678A@" } }); + fireEvent.click(screen.getByText("Cadastrar")); await waitFor(() => { - expect(screen.getByText("Erro ao verificar e-mail.")).toBeInTheDocument(); + expect(screen.getByText("Nome inválido, por favor, não coloque caracteres especiais nem espaços!")).toBeInTheDocument(); }); }); -// Senhas válidas - test("Senha válida: 6 caracteres(testando limite inferior), 1 maiúscula, 1 dígito, só letras e números", async () => { + test("Senha válida: 6 caracteres, 1 maiúscula, 1 dígito, 1 caractere especial", async () => { + mockSignUp.mockResolvedValueOnce({}); render(); - // Alterna para Sign Up fireEvent.click(screen.getByRole("button", { name: "Sign Up" })); - // Preenche com e-mail válido - fireEvent.change(screen.getByPlaceholderText("Name"), {target: { value: "João" },}); - const emailInput = screen.getByPlaceholderText("Email"); - const passwordInput = screen.getByPlaceholderText("Password"); - fireEvent.change(emailInput, { target: { value: "teste@email.com" } }); - fireEvent.change(passwordInput, { target: { value: "asBnm1" } }); - // Clica em criar conta - fireEvent.click(screen.getByText("Criar conta")); - await waitFor(() => { - // Espera que não apareça mensagem de erro de senha - expect(screen.queryByText(/senha ou email inválidos/i)).not.toBeInTheDocument(); - }); + fireEvent.change(screen.getByPlaceholderText("Name"), { target: { value: "Joao" } }); + fireEvent.change(screen.getByPlaceholderText("Email"), { target: { value: "polas10@gmail.com" } }); + fireEvent.change(screen.getByPlaceholderText("Password"), { target: { value: "a*Bnm1" } }); + fireEvent.click(screen.getByText("Cadastrar")); + await screen.findByText("Conta criada com sucesso! Você já pode fazer login."); + expect(mockSignUp).toHaveBeenCalledWith("polas10@gmail.com", "a*Bnm1"); }); - test("Senha válida: 20 caracteres(testando limite superior), 1 maiúscula, 1 dígito, só letras e números", async () => { + test("Senha válida: 20 caracteres, 1 maiúscula, 1 dígito, 1 caractere especial", async () => { + mockSignUp.mockResolvedValueOnce({}); render(); - // Alterna para Sign Up fireEvent.click(screen.getByRole("button", { name: "Sign Up" })); - // Preenche com e-mail válido - fireEvent.change(screen.getByPlaceholderText("Name"), {target: { value: "João" },}); - const emailInput = screen.getByPlaceholderText("Email"); - const passwordInput = screen.getByPlaceholderText("Password"); - fireEvent.change(emailInput, { target: { value: "teste@email.com" } }); - fireEvent.change(passwordInput, { target: { value: "ExemploDeSenhaValid1" } }); - // Clica em criar conta - fireEvent.click(screen.getByText("Criar conta")); - await waitFor(() => { - expect(screen.queryByText(/senha ou email inválidos/i)).not.toBeInTheDocument(); - }); + fireEvent.change(screen.getByPlaceholderText("Name"), { target: { value: "Joao" } }); + fireEvent.change(screen.getByPlaceholderText("Email"), { target: { value: "pola11@gmail.com" } }); + fireEvent.change(screen.getByPlaceholderText("Password"), { target: { value: "ExampleofValiddd1%Pa" } }); + fireEvent.click(screen.getByText("Cadastrar")); + await screen.findByText("Conta criada com sucesso! Você já pode fazer login."); + expect(mockSignUp).toHaveBeenCalledWith("pola11@gmail.com", "ExampleofValiddd1%Pa"); }); - // Senhas inválidas - test("Senha inválida: não contém nenhuma letra maiúscula", async () => { + test("Senha inválida: não contém letra maiúscula", async () => { render(); - // Alterna para Sign Up fireEvent.click(screen.getByRole("button", { name: "Sign Up" })); - // Preenche com e-mail válido - fireEvent.change(screen.getByPlaceholderText("Name"), {target: { value: "João" },}); - const emailInput = screen.getByPlaceholderText("Email"); - const passwordInput = screen.getByPlaceholderText("Password"); - fireEvent.change(emailInput, { target: { value: "teste@email.com" } }); - fireEvent.change(passwordInput, { target: { value: "invalid1" } }); - // Clica em criar conta - fireEvent.click(screen.getByText("Criar conta")); + fireEvent.change(screen.getByPlaceholderText("Name"), { target: { value: "Joao" } }); + fireEvent.change(screen.getByPlaceholderText("Email"), { target: { value: "teste@email.com" } }); + fireEvent.change(screen.getByPlaceholderText("Password"), { target: { value: "invalid1&*" } }); + fireEvent.click(screen.getByText("Cadastrar")); await waitFor(() => { - expect(screen.getByText(/senha ou email inválidos/i)).toBeInTheDocument(); + expect(screen.getByText("Senha inválida")).toBeInTheDocument(); }); }); - test("Senha inválida: não contém nenhum número", async () => { + test("Senha inválida: não contém número", async () => { render(); - // Alterna para Sign Up fireEvent.click(screen.getByRole("button", { name: "Sign Up" })); - // Preenche com e-mail válido - fireEvent.change(screen.getByPlaceholderText("Name"), {target: { value: "João" },}); - const emailInput = screen.getByPlaceholderText("Email"); - const passwordInput = screen.getByPlaceholderText("Password"); - fireEvent.change(emailInput, { target: { value: "teste@email.com" } }); - fireEvent.change(passwordInput, { target: { value: "Invalid*" } }); - // Clica em criar conta - fireEvent.click(screen.getByText("Criar conta")); + fireEvent.change(screen.getByPlaceholderText("Name"), { target: { value: "Joao" } }); + fireEvent.change(screen.getByPlaceholderText("Email"), { target: { value: "teste@email.com" } }); + fireEvent.change(screen.getByPlaceholderText("Password"), { target: { value: "Invaliddd*&*" } }); + fireEvent.click(screen.getByText("Cadastrar")); await waitFor(() => { - expect(screen.getByText(/senha ou email inválidos/i)).toBeInTheDocument(); + expect(screen.getByText("Senha inválida")).toBeInTheDocument(); }); }); - test("Senha inválida: contém caractere especial", async () => { + test("Senha inválida: não contém caractere especial", async () => { render(); - // Alterna para Sign Up fireEvent.click(screen.getByRole("button", { name: "Sign Up" })); - // Preenche com e-mail válido - fireEvent.change(screen.getByPlaceholderText("Name"), {target: { value: "João" },}); - const emailInput = screen.getByPlaceholderText("Email"); - const passwordInput = screen.getByPlaceholderText("Password"); - fireEvent.change(emailInput, { target: { value: "teste@email.com" } }); - fireEvent.change(passwordInput, { target: { value: "Invalid1*" } }); - // Clica em criar conta - fireEvent.click(screen.getByText("Criar conta")); + fireEvent.change(screen.getByPlaceholderText("Name"), { target: { value: "Joao" } }); + fireEvent.change(screen.getByPlaceholderText("Email"), { target: { value: "teste@email.com" } }); + fireEvent.change(screen.getByPlaceholderText("Password"), { target: { value: "Invalid112312" } }); + fireEvent.click(screen.getByText("Cadastrar")); await waitFor(() => { - expect(screen.getByText(/senha ou email inválidos/i)).toBeInTheDocument(); + expect(screen.getByText("Senha inválida")).toBeInTheDocument(); }); }); test("Senha inválida: 5 caracteres (abaixo do limite inferior)", async () => { render(); - // Alterna para Sign Up fireEvent.click(screen.getByRole("button", { name: "Sign Up" })); - // Preenche com e-mail válido - fireEvent.change(screen.getByPlaceholderText("Name"), {target: { value: "João" },}); - const emailInput = screen.getByPlaceholderText("Email"); - const passwordInput = screen.getByPlaceholderText("Password"); - fireEvent.change(emailInput, { target: { value: "teste@email.com" } }); - fireEvent.change(passwordInput, { target: { value: "A1bcd" } }); - // Clica em criar conta - fireEvent.click(screen.getByText("Criar conta")); + fireEvent.change(screen.getByPlaceholderText("Name"), { target: { value: "Joao" } }); + fireEvent.change(screen.getByPlaceholderText("Email"), { target: { value: "teste@email.com" } }); + fireEvent.change(screen.getByPlaceholderText("Password"), { target: { value: "A1&*a" } }); + fireEvent.click(screen.getByText("Cadastrar")); await waitFor(() => { - expect(screen.getByText(/senha ou email inválidos/i)).toBeInTheDocument(); + expect(screen.getByText("Senha inválida")).toBeInTheDocument(); }); }); test("Senha inválida: 21 caracteres (acima do limite superior)", async () => { render(); - // Alterna para Sign Up fireEvent.click(screen.getByRole("button", { name: "Sign Up" })); - // Preenche com e-mail válido - fireEvent.change(screen.getByPlaceholderText("Name"), {target: { value: "João" },}); - const emailInput = screen.getByPlaceholderText("Email"); - const passwordInput = screen.getByPlaceholderText("Password"); - fireEvent.change(emailInput, { target: { value: "teste@email.com" } }); - fireEvent.change(passwordInput, { target: { value: "InvalidPassword123abcdefghijkl" } }); - // Clica em criar conta - fireEvent.click(screen.getByText("Criar conta")); + fireEvent.change(screen.getByPlaceholderText("Name"), { target: { value: "Joao" } }); + fireEvent.change(screen.getByPlaceholderText("Email"), { target: { value: "teste@email.com" } }); + fireEvent.change(screen.getByPlaceholderText("Password"), { target: { value: "InvalidPassword123abcdefghijkl&*%" } }); + fireEvent.click(screen.getByText("Cadastrar")); await waitFor(() => { - expect(screen.getByText(/senha ou inválidos/i)).toBeInTheDocument(); + expect(screen.getByText("Senha inválida")).toBeInTheDocument(); }); }); }); \ No newline at end of file