From 79ab798f12af1e59ff60fd51875bc2f10c0ab946 Mon Sep 17 00:00:00 2001 From: Roman Timashev Date: Sun, 8 Feb 2026 03:49:06 +0700 Subject: [PATCH] UI Tests (#246) * adds components test setup * add some ui tests --- package-lock.json | 345 ++++++++++++++++++ package.json | 3 + .../Breadcrumbs/Breadcrumbs.test.tsx | 34 ++ src/ui/Box/Box.test.tsx | 44 +++ src/ui/Button/Button.test.tsx | 32 ++ src/ui/Panel/Panel.test.tsx | 23 ++ vitest.config.ts | 6 + vitest.setup.tsx | 9 + 8 files changed, 496 insertions(+) create mode 100644 src/components/Breadcrumbs/Breadcrumbs.test.tsx create mode 100644 src/ui/Box/Box.test.tsx create mode 100644 src/ui/Button/Button.test.tsx create mode 100644 src/ui/Panel/Panel.test.tsx create mode 100644 vitest.setup.tsx diff --git a/package-lock.json b/package-lock.json index 4e8b9ccc..a7a284ac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,6 +29,8 @@ }, "devDependencies": { "@eslint/eslintrc": "^3.3.1", + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.3.2", "@types/classnames": "^2.2.10", "@types/markdown-it": "^12.2.3", "@types/node": "^22.19.9", @@ -40,10 +42,45 @@ "eslint-config-next": "^15.5.0", "feed": "^4.1.0", "globals": "^16.0.0", + "happy-dom": "^20.5.0", "typescript": "^5.9.0", "vitest": "^4.0.18" } }, + "node_modules/@adobe/css-tools": { + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.4.tgz", + "integrity": "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/runtime": { "version": "7.27.0", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", @@ -1677,6 +1714,101 @@ "tslib": "^2.8.0" } }, + "node_modules/@testing-library/dom": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", + "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "picocolors": "1.1.1", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/dom/node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/@testing-library/jest-dom": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz", + "integrity": "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@adobe/css-tools": "^4.4.0", + "aria-query": "^5.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.6.3", + "picocolors": "^1.1.1", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", + "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@testing-library/react": { + "version": "16.3.2", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.2.tgz", + "integrity": "sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@testing-library/dom": "^10.0.0", + "@types/react": "^18.0.0 || ^19.0.0", + "@types/react-dom": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/@types/chai": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", @@ -1797,6 +1929,23 @@ "@types/node": "*" } }, + "node_modules/@types/whatwg-mimetype": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/whatwg-mimetype/-/whatwg-mimetype-3.0.2.tgz", + "integrity": "sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.54.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.54.0.tgz", @@ -2222,6 +2371,31 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -2790,6 +2964,13 @@ "node": ">=8.0.0" } }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true, + "license": "MIT" + }, "node_modules/csstype": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz", @@ -2969,6 +3150,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, "node_modules/detect-libc": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", @@ -2990,6 +3182,14 @@ "node": ">=8" } }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", @@ -4178,6 +4378,37 @@ "dev": true, "license": "ISC" }, + "node_modules/happy-dom": { + "version": "20.5.0", + "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-20.5.0.tgz", + "integrity": "sha512-VQe+Q5CYiGOgcCERXhcfNsbnrN92FDEKciMH/x6LppU9dd0j4aTjCTlqONFOIMcAm/5JxS3+utowbXV1OoFr+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": ">=20.0.0", + "@types/whatwg-mimetype": "^3.0.2", + "@types/ws": "^8.18.1", + "entities": "^4.5.0", + "whatwg-mimetype": "^3.0.0", + "ws": "^8.18.3" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/happy-dom/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -4316,6 +4547,16 @@ "node": ">=0.8.19" } }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -4940,6 +5181,17 @@ "loose-envify": "cli.js" } }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "lz-string": "bin/bin.js" + } + }, "node_modules/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", @@ -5022,6 +5274,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -5569,6 +5831,30 @@ "node": ">= 0.8.0" } }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -5735,6 +6021,20 @@ "node": ">= 6" } }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.7.tgz", @@ -6399,6 +6699,19 @@ "node": ">=4" } }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -7052,6 +7365,16 @@ } } }, + "node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -7183,6 +7506,28 @@ "resolved": "http://storage.mds.yandex.net/get-npm/69187/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, + "node_modules/ws": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/xml-js": { "version": "1.6.11", "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", diff --git a/package.json b/package.json index 04f99b38..0e4d3874 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,8 @@ }, "devDependencies": { "@eslint/eslintrc": "^3.3.1", + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.3.2", "@types/classnames": "^2.2.10", "@types/markdown-it": "^12.2.3", "@types/node": "^22.19.9", @@ -52,6 +54,7 @@ "eslint-config-next": "^15.5.0", "feed": "^4.1.0", "globals": "^16.0.0", + "happy-dom": "^20.5.0", "typescript": "^5.9.0", "vitest": "^4.0.18" } diff --git a/src/components/Breadcrumbs/Breadcrumbs.test.tsx b/src/components/Breadcrumbs/Breadcrumbs.test.tsx new file mode 100644 index 00000000..17396c5d --- /dev/null +++ b/src/components/Breadcrumbs/Breadcrumbs.test.tsx @@ -0,0 +1,34 @@ +import { render, within } from "@testing-library/react" +import { describe, it, expect } from "vitest" +import { Breadcrumbs } from "." + +describe("Breadcrumbs", () => { + const items = [ + { name: "Home", href: "/" }, + { name: "About", href: "/about" }, + { name: "Team", href: "/about/team" }, + ] + + it("renders breadcrumb items with separators", () => { + const { container } = render() + const nav = within(container.querySelector("nav")!) + + expect(nav.getByText("Home")).toBeInTheDocument() + expect(nav.getByText("About")).toBeInTheDocument() + expect(nav.getByText("Team")).toBeInTheDocument() + + const separators = nav.getAllByText("/") + expect(separators).toHaveLength(2) + }) + + it("disables the button matching the current path", () => { + const { container } = render() + const nav = within(container.querySelector("nav")!) + + const aboutLink = nav.getByText("About").closest("a")! + expect(aboutLink.className).toMatch(/disabled/) + + const homeLink = nav.getByText("Home").closest("a")! + expect(homeLink.className).not.toMatch(/disabled/) + }) +}) diff --git a/src/ui/Box/Box.test.tsx b/src/ui/Box/Box.test.tsx new file mode 100644 index 00000000..cfb01995 --- /dev/null +++ b/src/ui/Box/Box.test.tsx @@ -0,0 +1,44 @@ +import { render, screen } from "@testing-library/react" +import { describe, it, expect } from "vitest" +import { Box } from "." + +describe("Box", () => { + it("renders children", () => { + render(hello) + expect(screen.getByText("hello")).toBeInTheDocument() + }) + + it("defaults to a div tag", () => { + render(content) + expect(screen.getByText("content").tagName).toBe("DIV") + }) + + it("renders as a custom tag via the as prop", () => { + render(section content) + expect(screen.getByText("section content").tagName).toBe("SECTION") + }) + + it("applies vertical class", () => { + const { container } = render(v) + const el = container.firstElementChild! + expect(el.className).toMatch(/vertical/) + }) + + it("applies center class", () => { + const { container } = render(c) + const el = container.firstElementChild! + expect(el.className).toMatch(/center/) + }) + + it("applies wrap class", () => { + const { container } = render(w) + const el = container.firstElementChild! + expect(el.className).toMatch(/wrap/) + }) + + it("passes gap to inline style", () => { + const { container } = render(g) + const el = container.firstElementChild! + expect(el).toHaveStyle({ gap: "8px" }) + }) +}) diff --git a/src/ui/Button/Button.test.tsx b/src/ui/Button/Button.test.tsx new file mode 100644 index 00000000..85a1e33e --- /dev/null +++ b/src/ui/Button/Button.test.tsx @@ -0,0 +1,32 @@ +import { render, screen, fireEvent } from "@testing-library/react" +import { describe, it, expect, vi } from "vitest" +import { Button } from "." + +describe("Button", () => { + it("renders children in a button element", () => { + render() + const button = screen.getByRole("button", { name: "Click me" }) + expect(button).toBeInTheDocument() + expect(button.tagName).toBe("BUTTON") + }) + + it("renders as a link when href is provided", () => { + render() + const link = screen.getByRole("link", { name: "About" }) + expect(link).toBeInTheDocument() + expect(link).toHaveAttribute("href", "/about") + }) + + it("calls onClick with value when clicked", () => { + const onClick = vi.fn() + render() + fireEvent.click(screen.getByRole("button", { name: "Click" })) + expect(onClick).toHaveBeenCalledOnce() + expect(onClick).toHaveBeenCalledWith("test-value", expect.anything()) + }) + + it("applies disabled class when disabled is true", () => { + const { container } = render() + expect(container.firstElementChild!.className).toMatch(/disabled/) + }) +}) diff --git a/src/ui/Panel/Panel.test.tsx b/src/ui/Panel/Panel.test.tsx new file mode 100644 index 00000000..d0c9829f --- /dev/null +++ b/src/ui/Panel/Panel.test.tsx @@ -0,0 +1,23 @@ +import { render } from "@testing-library/react" +import { describe, it, expect } from "vitest" +import { Panel } from "." + +describe("Panel", () => { + it("renders children", () => { + const { container } = render(hello) + expect(container).toHaveTextContent("hello") + }) + + it("applies the default variant class", () => { + const { container } = render(content) + const el = container.firstElementChild! + expect(el.className).toMatch(/default/) + }) + + it("applies the ghost variant class when ghost is true", () => { + const { container } = render(content) + const el = container.firstElementChild! + expect(el.className).toMatch(/ghost/) + expect(el.className).not.toMatch(/default/) + }) +}) diff --git a/vitest.config.ts b/vitest.config.ts index 2afc56f9..b0229e80 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -2,6 +2,9 @@ import { defineConfig } from "vitest/config" import path from "path" export default defineConfig({ + esbuild: { + jsx: "automatic", + }, resolve: { alias: { "@": path.resolve(__dirname, "src"), @@ -10,5 +13,8 @@ export default defineConfig({ }, test: { include: ["src/**/*.test.{ts,tsx}"], + environment: "happy-dom", + setupFiles: ["./vitest.setup.tsx"], + css: true, }, }) diff --git a/vitest.setup.tsx b/vitest.setup.tsx new file mode 100644 index 00000000..f2895e39 --- /dev/null +++ b/vitest.setup.tsx @@ -0,0 +1,9 @@ +import "@testing-library/jest-dom/vitest" +import { vi } from "vitest" + +vi.mock("next/link", () => ({ + __esModule: true, + default: ({ href, children, ...props }: any) => ( + {children} + ), +}))