From dcf57270d5c737563b6a74635038606b48c95de0 Mon Sep 17 00:00:00 2001 From: Kirill Zaytsev Date: Sat, 19 Oct 2024 14:22:00 +0400 Subject: [PATCH] test: Missing tests for predicates, eslint adjustments --- .gitignore | 1 + codecov.yml | 11 +++++ eslint.config.mjs | 1 + package.json | 4 +- src/predicates/isNumber.ts | 2 +- src/predicates/isNumeric.ts | 6 ++- tests/predicates/hasProperty.test.ts | 26 +++++++++++ tests/predicates/isNull.test.ts | 19 ++++++++ tests/predicates/isNumber.test.ts | 20 +++++++++ tests/predicates/isNumeric.test.ts | 31 +++++++++++++ tests/predicates/isUndefined.test.ts | 19 ++++++++ types/index.d.ts | 4 +- yarn.lock | 65 ++++++++++++++++++++++++++++ 13 files changed, 204 insertions(+), 5 deletions(-) create mode 100644 codecov.yml create mode 100644 tests/predicates/hasProperty.test.ts create mode 100644 tests/predicates/isNull.test.ts create mode 100644 tests/predicates/isNumber.test.ts create mode 100644 tests/predicates/isNumeric.test.ts create mode 100644 tests/predicates/isUndefined.test.ts diff --git a/.gitignore b/.gitignore index cd0ece4..ac99e84 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,6 @@ coverage/ dist/ node_modules/ +reports/ .npmrc \ No newline at end of file diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..c1936fa --- /dev/null +++ b/codecov.yml @@ -0,0 +1,11 @@ +coverage: + status: + patch: + default: + target: 90% + threshold: 5% + + project: + default: + target: 90% + threshold: 1% diff --git a/eslint.config.mjs b/eslint.config.mjs index 2df080a..2a5a37a 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -28,6 +28,7 @@ export default [ 'no-debugger': 'error', 'object-curly-spacing': ['error', 'always'], 'quotes': ['error', 'single'], + 'semi': ['error', 'never'], }, }, ] diff --git a/package.json b/package.json index 125ddc6..417c29d 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,8 @@ "release:major": "standard-version --release-as major", "stats": "gzip -c ./dist/index.mjs | wc -c", "test": "vitest run", - "test:coverage": "vitest run --coverage" + "test:coverage": "vitest run --coverage", + "test:coverage:html": "vitest run --coverage --reporter=html --outputFile.html=./reports/html/report.html" }, "devDependencies": { "@commitlint/cli": "^17.7.1", @@ -26,6 +27,7 @@ "@rollup/plugin-typescript": "^11.1.6", "@types/node": "^18.15 || ^20.11", "@vitest/coverage-istanbul": "2.1.3", + "@vitest/ui": "2.1.3", "eslint": "^9.13.0", "globals": "^15.11.0", "husky": "^9.0.10", diff --git a/src/predicates/isNumber.ts b/src/predicates/isNumber.ts index 0c0e6dc..ab4ec2f 100644 --- a/src/predicates/isNumber.ts +++ b/src/predicates/isNumber.ts @@ -1,2 +1,2 @@ /** Checks if a value is a number */ -export default (value: unknown): value is number => typeof value === 'number' +export default (value: unknown): value is number => typeof value === 'number' && !isNaN(value) diff --git a/src/predicates/isNumeric.ts b/src/predicates/isNumeric.ts index cea2142..07c1853 100644 --- a/src/predicates/isNumeric.ts +++ b/src/predicates/isNumeric.ts @@ -3,5 +3,9 @@ import isString from '@/predicates/isString' /** Checks if a value is a number or a numeric string */ export default (value: unknown, integer = false): value is number | string => { - return isNumber(value) || isString(value) && !isNaN(integer ? parseInt(value) : parseFloat(value)) + const valid = (value: number) => { + return !isNaN(value) && (!integer || Number.isInteger(value)) + } + + return (isNumber(value) || isString(value)) && valid(Number(value)) } diff --git a/tests/predicates/hasProperty.test.ts b/tests/predicates/hasProperty.test.ts new file mode 100644 index 0000000..98f4339 --- /dev/null +++ b/tests/predicates/hasProperty.test.ts @@ -0,0 +1,26 @@ +import { + describe, + expect, + test, +} from 'vitest' + +import hasProperty from '@/predicates/hasProperty' + +const property = Symbol('property') + +describe('predicates/hasProperty', () => { + test('returns true when property exists', () => { + expect(hasProperty({ a: 1 }, 'a')).toBe(true) + expect(hasProperty({ 0: 1 }, 0)).toBe(true) + expect(hasProperty({ [property]: 1 }, property)).toBe(true) + expect(hasProperty([], 'length')).toBe(true) + }) + + test('returns false when property does not exist', () => { + expect(hasProperty({}, 'a')).toBe(false) + expect(hasProperty({}, 0)).toBe(false) + expect(hasProperty({}, property)).toBe(false) + expect(hasProperty(null, property)).toBe(false) + expect(hasProperty([], 'does_not_exists')).toBe(false) + }) +}) diff --git a/tests/predicates/isNull.test.ts b/tests/predicates/isNull.test.ts new file mode 100644 index 0000000..83a3e04 --- /dev/null +++ b/tests/predicates/isNull.test.ts @@ -0,0 +1,19 @@ +import { + describe, + expect, + test, +} from 'vitest' + +import isNull from '@/predicates/isNull' + +describe('predicates/isNull', () => { + test('returns true when a value is equal to undefined', () => { + expect(isNull(null)).toBe(true) + }) + + test('returns false when a value is not equal to undefined', () => { + expect(isNull({})).toBe(false) + expect(isNull(2)).toBe(false) + expect(isNull(Symbol('2'))).toBe(false) + }) +}) diff --git a/tests/predicates/isNumber.test.ts b/tests/predicates/isNumber.test.ts new file mode 100644 index 0000000..af56e5a --- /dev/null +++ b/tests/predicates/isNumber.test.ts @@ -0,0 +1,20 @@ +import { + describe, + expect, + test, +} from 'vitest' + +import isNumber from '@/predicates/isNumber' + +describe('predicates/isNumber', () => { + test('returns true when a value\'s type is a number', () => { + expect(isNumber(1)).toBe(true) + expect(isNumber(1.5)).toBe(true) + }) + + test('returns false when a value\'s type is not a number', () => { + expect(isNumber({})).toBe(false) + expect(isNumber('1')).toBe(false) + expect(isNumber('1.5')).toBe(false) + }) +}) diff --git a/tests/predicates/isNumeric.test.ts b/tests/predicates/isNumeric.test.ts new file mode 100644 index 0000000..260e9be --- /dev/null +++ b/tests/predicates/isNumeric.test.ts @@ -0,0 +1,31 @@ +import { + describe, + expect, + test, +} from 'vitest' + +import isNumeric from '@/predicates/isNumeric' + +describe('predicates/isNumeric', () => { + test('returns true when a value is a number or a numeric string', () => { + expect(isNumeric(1)).toBe(true) + expect(isNumeric(1.5)).toBe(true) + expect(isNumeric('1')).toBe(true) + expect(isNumeric('1.5')).toBe(true) + }) + + test('returns false when a value is not a number or a numeric string', () => { + expect(isNumeric({})).toBe(false) + expect(isNumeric('a1')).toBe(false) + expect(isNumeric('1.3y5')).toBe(false) + expect(isNumeric('1.3y5', true)).toBe(false) + expect(isNumeric(null)).toBe(false) + expect(isNumeric(undefined)).toBe(false) + expect(isNumeric(Symbol('2'))).toBe(false) + }) + + test('returns false when a value is a number or a numeric string, but not integer when flag is set to true', () => { + expect(isNumeric(1.5, true)).toBe(false) + expect(isNumeric('1.5', true)).toBe(false) + }) +}) diff --git a/tests/predicates/isUndefined.test.ts b/tests/predicates/isUndefined.test.ts new file mode 100644 index 0000000..20a2839 --- /dev/null +++ b/tests/predicates/isUndefined.test.ts @@ -0,0 +1,19 @@ +import { + describe, + expect, + test, +} from 'vitest' + +import isUndefined from '@/predicates/isUndefined' + +describe('predicates/isUndefined', () => { + test('returns true when a value is equal to undefined', () => { + expect(isUndefined(undefined)).toBe(true) + }) + + test('returns false when a value is not equal to undefined', () => { + expect(isUndefined({})).toBe(false) + expect(isUndefined(2)).toBe(false) + expect(isUndefined(Symbol('2'))).toBe(false) + }) +}) diff --git a/types/index.d.ts b/types/index.d.ts index e4d5bb1..536f692 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -125,6 +125,6 @@ export declare class ProviderChain implements Provider { override (provider: Provider): Provider; } -export declare const createValidator: (provider?: Provider | null) => Validator; +export declare const createValidator: (provider?: Provider | null) => Validator -export declare const validate: FunctionalValidator; \ No newline at end of file +export declare const validate: FunctionalValidator \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 1783912..9175f7c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -694,6 +694,11 @@ resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== +"@polka/url@^1.0.0-next.24": + version "1.0.0-next.28" + resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.28.tgz#d45e01c4a56f143ee69c54dd6b12eade9e270a73" + integrity sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw== + "@rollup/plugin-alias@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@rollup/plugin-alias/-/plugin-alias-5.1.0.tgz#99a94accc4ff9a3483be5baeedd5d7da3b597e93" @@ -1132,6 +1137,19 @@ dependencies: tinyspy "^3.0.0" +"@vitest/ui@2.1.3": + version "2.1.3" + resolved "https://registry.yarnpkg.com/@vitest/ui/-/ui-2.1.3.tgz#2c191e55fd169d4aea853dfbc74a0790e8d5bf87" + integrity sha512-2XwTrHVJw3t9NYES26LQUYy51ZB8W4bRPgqUH2Eyda3kIuOlYw1ZdPNU22qcVlUVx4WKgECFQOSXuopsczuVjQ== + dependencies: + "@vitest/utils" "2.1.3" + fflate "^0.8.2" + flatted "^3.3.1" + pathe "^1.1.2" + sirv "^2.0.4" + tinyglobby "^0.2.6" + tinyrainbow "^1.2.0" + "@vitest/utils@2.1.3": version "2.1.3" resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-2.1.3.tgz#e52aa5745384091b151cbdf79bb5a3ad2bea88d2" @@ -2123,6 +2141,16 @@ fastq@^1.6.0: dependencies: reusify "^1.0.4" +fdir@^6.4.0: + version "6.4.2" + resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.4.2.tgz#ddaa7ce1831b161bc3657bb99cb36e1622702689" + integrity sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ== + +fflate@^0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.8.2.tgz#fc8631f5347812ad6028bbe4a2308b2792aa1dea" + integrity sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A== + figures@^3.1.0: version "3.2.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" @@ -2187,6 +2215,11 @@ flatted@^3.2.9: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf" integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ== +flatted@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" + integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== + foreground-child@^3.1.0: version "3.3.0" resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.0.tgz#0ac8644c06e431439f8561db8ecf29a7b5519c77" @@ -2998,6 +3031,11 @@ modify-values@^1.0.0: resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== +mrmime@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-2.0.0.tgz#151082a6e06e59a9a39b46b3e14d5cfe92b3abb4" + integrity sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw== + ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" @@ -3252,6 +3290,11 @@ picomatch@^2.3.1: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== +picomatch@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.2.tgz#77c742931e8f3b8820946c76cd0c1f13730d1dab" + integrity sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg== + pify@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -3544,6 +3587,15 @@ signal-exit@^4.0.1: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== +sirv@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/sirv/-/sirv-2.0.4.tgz#5dd9a725c578e34e449f332703eb2a74e46a29b0" + integrity sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ== + dependencies: + "@polka/url" "^1.0.0-next.24" + mrmime "^2.0.0" + totalist "^3.0.0" + slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" @@ -3808,6 +3860,14 @@ tinyexec@^0.3.0: resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-0.3.1.tgz#0ab0daf93b43e2c211212396bdb836b468c97c98" integrity sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ== +tinyglobby@^0.2.6: + version "0.2.9" + resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.9.tgz#6baddd1b0fe416403efb0dd40442c7d7c03c1c66" + integrity sha512-8or1+BGEdk1Zkkw2ii16qSS7uVrQJPre5A9o/XkWPATkk23FZh/15BKFxPnlTy6vkljZxLqYCzzBMj30ZrSvjw== + dependencies: + fdir "^6.4.0" + picomatch "^4.0.2" + tinypool@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-1.0.1.tgz#c64233c4fac4304e109a64340178760116dbe1fe" @@ -3835,6 +3895,11 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +totalist@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/totalist/-/totalist-3.0.1.tgz#ba3a3d600c915b1a97872348f79c127475f6acf8" + integrity sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ== + trim-newlines@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144"