From 54457c9198fa2da15c52b521c5f6f8eff9be3c40 Mon Sep 17 00:00:00 2001 From: Askhat Tassybayev Date: Fri, 7 Nov 2025 12:05:45 +0600 Subject: [PATCH] add: tests implementations --- .eslintrc.js | 2 +- .prettierrc | 2 +- README.md | 14 ++- babel.config.js | 10 +- package-lock.json | 83 +++++--------- src/01-simple-tests/index.test.ts | 30 +++-- src/02-table-tests/index.test.ts | 36 ++++-- src/03-error-handling-async/index.test.ts | 30 ++++- src/04-test-class/index.test.ts | 76 +++++++++++-- src/05-partial-mocking/index.test.ts | 35 +++++- src/06-mocking-node-api/index.test.ts | 106 ++++++++++++++++-- src/07-mocking-lib-api/index.test.ts | 52 ++++++++- .../__snapshots__/index.test.ts.snap | 17 +++ src/08-snapshot-testing/index.test.ts | 28 ++++- tsconfig.json | 2 +- 15 files changed, 393 insertions(+), 130 deletions(-) create mode 100644 src/08-snapshot-testing/__snapshots__/index.test.ts.snap diff --git a/.eslintrc.js b/.eslintrc.js index 55cf7d9f5..f6bcc7b7c 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -2,7 +2,7 @@ module.exports = { parser: '@typescript-eslint/parser', parserOptions: { project: 'tsconfig.json', - tsconfigRootDir : __dirname, + tsconfigRootDir: __dirname, sourceType: 'module', }, plugins: ['@typescript-eslint/eslint-plugin'], diff --git a/.prettierrc b/.prettierrc index dcb72794f..a20502b7f 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,4 +1,4 @@ { "singleQuote": true, "trailingComma": "all" -} \ No newline at end of file +} diff --git a/README.md b/README.md index 63d302ca8..0f896cf34 100644 --- a/README.md +++ b/README.md @@ -5,11 +5,12 @@ --- ### Prerequisites -1. Install [Node.js](https://nodejs.org/en/download/) + +1. Install [Node.js](https://nodejs.org/en/download/) 2. Fork this repository: https://github.com/AlreadyBored/basic-testing -3. Clone your newly created repo locally: https://github.com/<%your_github_username%>/basic-testing/ -4. Go to folder `basic-testing` -5. To install all dependencies use [`npm install`](https://docs.npmjs.com/cli/install) +3. Clone your newly created repo locally: https://github.com/<%your_github_username%>/basic-testing/ +4. Go to folder `basic-testing` +5. To install all dependencies use [`npm install`](https://docs.npmjs.com/cli/install) 6. Run **test scripts** in command line. 7. You will see the number of skipped, passing and failing tests. @@ -28,6 +29,7 @@ $ npm run test:verbose --- #### Notes + 1. We recommend you to use Node.js of version 24.x.x (24.10.0 or upper) LTS. If you use any of features, that does not supported by Node.js 24, there may be problems with task submit. 2. Please, be sure that each of your tests is limited to 30 sec. 3. Please, be sure you don't have any linter/TS compiler errors. @@ -35,7 +37,8 @@ $ npm run test:verbose --- ## General task description -Your task is to write unit tests for code, provided in file `index.ts`. + +Your task is to write unit tests for code, provided in file `index.ts`. --- @@ -55,7 +58,6 @@ Write your tests in `src/02-table-tests/index.test.ts`. --- - ### **Error handling & async** Your task is to test functions that work asynchronously/throw/reject exceptions.. diff --git a/babel.config.js b/babel.config.js index f98cbe437..8165fe455 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,6 +1,6 @@ module.exports = { - presets: [ - ['@babel/preset-env', { targets: { node: 'current' } }], - '@babel/preset-typescript', - ], - }; \ No newline at end of file + presets: [ + ['@babel/preset-env', { targets: { node: 'current' } }], + '@babel/preset-typescript', + ], +}; diff --git a/package-lock.json b/package-lock.json index 2bfc1ab15..d0a46ed4a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -80,6 +80,7 @@ "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.26.2", @@ -2388,7 +2389,6 @@ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.3.tgz", "integrity": "sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==", "dev": true, - "peer": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" @@ -2575,7 +2575,6 @@ "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", "dev": true, - "peer": true, "dependencies": { "@types/eslint": "*", "@types/estree": "*" @@ -2585,8 +2584,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz", "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==", - "dev": true, - "peer": true + "dev": true }, "node_modules/@types/graceful-fs": { "version": "4.1.9", @@ -2637,8 +2635,7 @@ "version": "7.0.12", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", - "dev": true, - "peer": true + "dev": true }, "node_modules/@types/lodash": { "version": "4.17.16", @@ -2653,6 +2650,7 @@ "integrity": "sha512-7zf4YyHA+jvBNfVrk2Gtvs6x7E8V+YDW05bNfG2XkWDJfYRXrTiP/DsB2zSYTaHX0bGIujTBQdMVAhb+j7mwpg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~6.19.2" } @@ -2718,6 +2716,7 @@ "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "7.18.0", "@typescript-eslint/types": "7.18.0", @@ -2922,7 +2921,6 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", "dev": true, - "peer": true, "dependencies": { "@webassemblyjs/helper-numbers": "1.11.6", "@webassemblyjs/helper-wasm-bytecode": "1.11.6" @@ -2932,29 +2930,25 @@ "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", - "dev": true, - "peer": true + "dev": true }, "node_modules/@webassemblyjs/helper-api-error": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", - "dev": true, - "peer": true + "dev": true }, "node_modules/@webassemblyjs/helper-buffer": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==", - "dev": true, - "peer": true + "dev": true }, "node_modules/@webassemblyjs/helper-numbers": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", "dev": true, - "peer": true, "dependencies": { "@webassemblyjs/floating-point-hex-parser": "1.11.6", "@webassemblyjs/helper-api-error": "1.11.6", @@ -2965,15 +2959,13 @@ "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", - "dev": true, - "peer": true + "dev": true }, "node_modules/@webassemblyjs/helper-wasm-section": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", "dev": true, - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.11.6", "@webassemblyjs/helper-buffer": "1.11.6", @@ -2986,7 +2978,6 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", "dev": true, - "peer": true, "dependencies": { "@xtuc/ieee754": "^1.2.0" } @@ -2996,7 +2987,6 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", "dev": true, - "peer": true, "dependencies": { "@xtuc/long": "4.2.2" } @@ -3005,15 +2995,13 @@ "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", - "dev": true, - "peer": true + "dev": true }, "node_modules/@webassemblyjs/wasm-edit": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", "dev": true, - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.11.6", "@webassemblyjs/helper-buffer": "1.11.6", @@ -3030,7 +3018,6 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", "dev": true, - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.11.6", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", @@ -3044,7 +3031,6 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", "dev": true, - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.11.6", "@webassemblyjs/helper-buffer": "1.11.6", @@ -3057,7 +3043,6 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", "dev": true, - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.11.6", "@webassemblyjs/helper-api-error": "1.11.6", @@ -3072,7 +3057,6 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", "dev": true, - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.11.6", "@xtuc/long": "4.2.2" @@ -3082,15 +3066,13 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true, - "peer": true + "dev": true }, "node_modules/@xtuc/long": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true, - "peer": true + "dev": true }, "node_modules/acorn": { "version": "8.14.1", @@ -3098,6 +3080,7 @@ "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3110,7 +3093,6 @@ "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", "dev": true, - "peer": true, "peerDependencies": { "acorn": "^8" } @@ -3143,6 +3125,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -3159,7 +3142,6 @@ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "dev": true, - "peer": true, "peerDependencies": { "ajv": "^6.9.1" } @@ -3451,6 +3433,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001688", "electron-to-chromium": "^1.5.73", @@ -3564,7 +3547,6 @@ "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", "dev": true, - "peer": true, "engines": { "node": ">=6.0" } @@ -3659,8 +3641,7 @@ "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "peer": true + "dev": true }, "node_modules/concat-map": { "version": "0.0.1", @@ -3914,8 +3895,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.0.tgz", "integrity": "sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA==", - "dev": true, - "peer": true + "dev": true }, "node_modules/escalade": { "version": "3.2.0", @@ -3934,6 +3914,7 @@ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -3990,6 +3971,7 @@ "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", "dev": true, "license": "MIT", + "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -4033,7 +4015,6 @@ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, - "peer": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" @@ -4199,7 +4180,6 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, - "peer": true, "engines": { "node": ">=4.0" } @@ -4218,7 +4198,6 @@ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true, - "peer": true, "engines": { "node": ">=0.8.x" } @@ -4581,8 +4560,7 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true, - "peer": true + "dev": true }, "node_modules/globals": { "version": "11.12.0", @@ -4934,6 +4912,7 @@ "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", @@ -5633,7 +5612,6 @@ "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", "dev": true, - "peer": true, "engines": { "node": ">=6.11.5" } @@ -5827,8 +5805,7 @@ "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, - "peer": true + "dev": true }, "node_modules/node-int64": { "version": "0.4.0", @@ -6138,6 +6115,7 @@ "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -6257,7 +6235,6 @@ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, - "peer": true, "dependencies": { "safe-buffer": "^5.1.0" } @@ -6496,15 +6473,13 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "peer": true + ] }, "node_modules/schema-utils": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, - "peer": true, "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", @@ -6533,7 +6508,6 @@ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", "dev": true, - "peer": true, "dependencies": { "randombytes": "^2.1.0" } @@ -6759,7 +6733,6 @@ "resolved": "https://registry.npmjs.org/terser/-/terser-5.18.1.tgz", "integrity": "sha512-j1n0Ao919h/Ai5r43VAnfV/7azUYW43GPxK7qSATzrsERfW7+y2QW9Cp9ufnRF5CQUWbnLSo7UJokSWCqg4tsQ==", "dev": true, - "peer": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", @@ -6778,7 +6751,6 @@ "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz", "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==", "dev": true, - "peer": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.17", "jest-worker": "^27.4.5", @@ -6813,7 +6785,6 @@ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "dev": true, - "peer": true, "dependencies": { "@types/node": "*", "merge-stream": "^2.0.0", @@ -6828,7 +6799,6 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, - "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -6844,7 +6814,6 @@ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, - "peer": true, "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -7049,6 +7018,7 @@ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -7159,6 +7129,7 @@ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -7295,7 +7266,6 @@ "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", "dev": true, - "peer": true, "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -7357,7 +7327,6 @@ "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", "dev": true, - "peer": true, "engines": { "node": ">=10.13.0" } diff --git a/src/01-simple-tests/index.test.ts b/src/01-simple-tests/index.test.ts index fbbea85de..f6337e54e 100644 --- a/src/01-simple-tests/index.test.ts +++ b/src/01-simple-tests/index.test.ts @@ -1,32 +1,46 @@ // Uncomment the code below and write your tests -// import { simpleCalculator, Action } from './index'; +import { simpleCalculator, Action } from './index'; describe('simpleCalculator tests', () => { test('should add two numbers', () => { - // Write your test here + const input = { a: 2, b: 3, action: Action.Add }; + const result = simpleCalculator(input); + expect(result).toBe(5); }); test('should subtract two numbers', () => { - // Write your test here + const input = { a: 10, b: 4, action: Action.Subtract }; + const result = simpleCalculator(input); + expect(result).toBe(6); }); test('should multiply two numbers', () => { - // Write your test here + const input = { a: 3, b: 3, action: Action.Multiply }; + const result = simpleCalculator(input); + expect(result).toBe(9); }); test('should divide two numbers', () => { - // Write your test here + const input = { a: 10, b: 5, action: Action.Divide }; + const result = simpleCalculator(input); + expect(result).toBe(2); }); test('should exponentiate two numbers', () => { - // Write your test here + const input = { a: 2, b: 3, action: Action.Exponentiate }; + const result = simpleCalculator(input); + expect(result).toBe(8); }); test('should return null for invalid action', () => { - // Write your test here + const input = { a: 2, b: 3, action: '%' as unknown as Action }; + const result = simpleCalculator(input); + expect(result).toBeNull(); }); test('should return null for invalid arguments', () => { - // Write your test here + const input = { a: 2, b: '3', action: Action.Add }; + const result = simpleCalculator(input); + expect(result).toBeNull; }); }); diff --git a/src/02-table-tests/index.test.ts b/src/02-table-tests/index.test.ts index 4f36e892e..7edf04626 100644 --- a/src/02-table-tests/index.test.ts +++ b/src/02-table-tests/index.test.ts @@ -1,17 +1,33 @@ +/* eslint-disable prettier/prettier */ // Uncomment the code below and write your tests -/* import { simpleCalculator, Action } from './index'; +import { simpleCalculator, Action } from './index'; const testCases = [ - { a: 1, b: 2, action: Action.Add, expected: 3 }, - { a: 2, b: 2, action: Action.Add, expected: 4 }, - { a: 3, b: 2, action: Action.Add, expected: 5 }, - // continue cases for other actions -]; */ + { name: 'Add', a: 1, b: 2, action: Action.Add, expected: 3 }, + { name: 'Add', a: 2, b: 2, action: Action.Add, expected: 4 }, + { name: 'Subtract', a: 10, b: 5, action: Action.Subtract, expected: 5 }, + { name: 'Multiply', a: 4, b: 5, action: Action.Multiply, expected: 20 }, + { name: 'Divide', a: 10, b: 2, action: Action.Divide, expected: 5 }, + // eslint-disable-next-line prettier/prettier + { name: 'Exponentiate', a: 2, b: 3, action: Action.Exponentiate, expected: 8}, +]; -describe('simpleCalculator', () => { - // This test case is just to run this test suite, remove it when you write your own tests - test('should blah-blah', () => { - expect(true).toBe(true); +describe('simpleCalculator (table tests)', () => { + test.each(testCases)('$name', ({ a, b, action, expected }) => { + const result = simpleCalculator({ a, b, action }); + expect(result).toBe(expected); }); // Consider to use Jest table tests API to test all cases above }); + +const invalidCases = [ + { name: 'invalid action returns null', a: 2, b: 3, action: '%' }, + { name: 'non-number a returns null', a: '2', b: 3, action: Action.Add }, + { name: 'non-number b returns null', a: 2, b: '3', action: Action.Add }, + { name: 'both args invalid return null', a: 'x', b: 'y', action: Action.Multiply }, +]; + +test.each(invalidCases)('$name', ({ a, b, action }) => { + const result = simpleCalculator({ a, b, action }); + expect(result).toBeNull(); +}); diff --git a/src/03-error-handling-async/index.test.ts b/src/03-error-handling-async/index.test.ts index 6e106a6d6..bcdc749cf 100644 --- a/src/03-error-handling-async/index.test.ts +++ b/src/03-error-handling-async/index.test.ts @@ -1,30 +1,48 @@ // Uncomment the code below and write your tests -// import { throwError, throwCustomError, resolveValue, MyAwesomeError, rejectCustomError } from './index'; +import { + throwError, + throwCustomError, + resolveValue, + MyAwesomeError, + rejectCustomError, +} from './index'; describe('resolveValue', () => { test('should resolve provided value', async () => { - // Write your test here + const value = 5; + + await expect(resolveValue(value)).resolves.toBe(5); }); }); describe('throwError', () => { test('should throw error with provided message', () => { - // Write your test here + const msg = 'text'; + + expect(() => throwError(msg)).toThrow(msg); }); test('should throw error with default message if message is not provided', () => { - // Write your test here + try { + throwError(); + } catch (e: unknown) { + if (e instanceof Error) { + console.log(e.message); + } else { + console.log(String(e)); + } + } }); }); describe('throwCustomError', () => { test('should throw custom error', () => { - // Write your test here + expect(() => throwCustomError()).toThrow(MyAwesomeError); }); }); describe('rejectCustomError', () => { test('should reject custom error', async () => { - // Write your test here + await expect(rejectCustomError()).rejects.toBeInstanceOf(MyAwesomeError); }); }); diff --git a/src/04-test-class/index.test.ts b/src/04-test-class/index.test.ts index 937490d82..ff294fbf9 100644 --- a/src/04-test-class/index.test.ts +++ b/src/04-test-class/index.test.ts @@ -1,44 +1,98 @@ // Uncomment the code below and write your tests -// import { getBankAccount } from '.'; +import { + BankAccount, + getBankAccount, + SynchronizationFailedError, + TransferFailedError, +} from '.'; +import { random } from 'lodash'; +import { InsufficientFundsError } from './index'; + +jest.mock('lodash', () => ({ + random: jest.fn(), +})); + +const mockedRandom = random as unknown as jest.Mock; + +afterEach(() => { + jest.restoreAllMocks(); + mockedRandom.mockReset(); +}); describe('BankAccount', () => { test('should create account with initial balance', () => { - // Write your test here + const account = getBankAccount(100); + expect(account.getBalance()).toBe(100); }); test('should throw InsufficientFundsError error when withdrawing more than balance', () => { - // Write your test here + const account = getBankAccount(100); + expect(() => account.withdraw(150)).toThrow(InsufficientFundsError); }); test('should throw error when transferring more than balance', () => { - // Write your test here + const from = getBankAccount(50); + const to = getBankAccount(0); + expect(() => from.transfer(60, to)).toThrow(InsufficientFundsError); }); test('should throw error when transferring to the same account', () => { - // Write your test here + const account = getBankAccount(50); + expect(() => account.transfer(10, account)).toThrow(TransferFailedError); + expect(() => account.transfer(10, account)).toThrow('Transfer failed'); }); test('should deposit money', () => { - // Write your test here + const account = getBankAccount(100); + const returned = account.deposit(30); + expect(account.getBalance()).toBe(130); + expect(returned).toBe(account); }); test('should withdraw money', () => { - // Write your test here + const account = getBankAccount(100); + const returned = account.withdraw(40); + expect(account.getBalance()).toBe(60); + expect(returned).toBe(account); }); test('should transfer money', () => { - // Write your test here + const from = getBankAccount(100); + const to = getBankAccount(10); + + const returned = from.transfer(35, to); + + expect(from.getBalance()).toBe(65); + expect(to.getBalance()).toBe(45); + expect(returned).toBe(from); }); test('fetchBalance should return number in case if request did not failed', async () => { - // Write your tests here + const account = getBankAccount(0); + + mockedRandom.mockReturnValueOnce(45).mockReturnValueOnce(1); + const result = await account.fetchBalance(); + expect(result).toBe(45); }); test('should set new balance if fetchBalance returned number', async () => { - // Write your tests here + const account = getBankAccount(10); + + jest.spyOn(BankAccount.prototype, 'fetchBalance').mockResolvedValue(77); + await account.synchronizeBalance(); + expect(account.getBalance()).toBe(77); }); test('should throw SynchronizationFailedError if fetchBalance returned null', async () => { - // Write your tests here + const account = getBankAccount(10); + + jest.spyOn(BankAccount.prototype, 'fetchBalance').mockResolvedValue(null); + + await expect(account.synchronizeBalance()).rejects.toBeInstanceOf( + SynchronizationFailedError, + ); + await expect(account.synchronizeBalance()).rejects.toThrow( + 'Synchronization failed', + ); }); }); diff --git a/src/05-partial-mocking/index.test.ts b/src/05-partial-mocking/index.test.ts index 9d8a66cbd..18ad21dcd 100644 --- a/src/05-partial-mocking/index.test.ts +++ b/src/05-partial-mocking/index.test.ts @@ -1,20 +1,45 @@ // Uncomment the code below and write your tests -// import { mockOne, mockTwo, mockThree, unmockedFunction } from './index'; - jest.mock('./index', () => { - // const originalModule = jest.requireActual('./index'); + const originalModule = + jest.requireActual('./index'); + + return { + __esModule: true, + ...originalModule, + mockOne: jest.fn(), + mockTwo: jest.fn(), + mockThree: jest.fn(), + }; }); +import { mockOne, mockTwo, mockThree, unmockedFunction } from './index'; + describe('partial mocking', () => { + let consoleSpy: jest.SpyInstance; + + beforeEach(() => { + consoleSpy = jest.spyOn(console, 'log').mockImplementation(() => {}); + }); + + afterEach(() => { + consoleSpy.mockRestore(); + jest.clearAllMocks(); + }); + afterAll(() => { jest.unmock('./index'); }); test('mockOne, mockTwo, mockThree should not log into console', () => { - // Write your test here + mockOne(); + mockTwo(); + mockThree(); + + expect(consoleSpy).not.toHaveBeenCalled(); }); test('unmockedFunction should log into console', () => { - // Write your test here + unmockedFunction(); + expect(consoleSpy).toHaveBeenCalledWith('I am not mocked'); }); }); diff --git a/src/06-mocking-node-api/index.test.ts b/src/06-mocking-node-api/index.test.ts index 8dc3afd79..d55be70db 100644 --- a/src/06-mocking-node-api/index.test.ts +++ b/src/06-mocking-node-api/index.test.ts @@ -1,5 +1,20 @@ // Uncomment the code below and write your tests -// import { readFileAsynchronously, doStuffByTimeout, doStuffByInterval } from '.'; +jest.mock('fs', () => ({ + existsSync: jest.fn(), +})); + +jest.mock('fs/promises', () => ({ + readFile: jest.fn(), +})); + +jest.mock('path', () => ({ + join: jest.fn(), +})); + +import { existsSync } from 'fs'; +import { readFile } from 'fs/promises'; +import { join } from 'path'; +import { readFileAsynchronously, doStuffByTimeout, doStuffByInterval } from '.'; describe('doStuffByTimeout', () => { beforeAll(() => { @@ -10,12 +25,36 @@ describe('doStuffByTimeout', () => { jest.useRealTimers(); }); + afterEach(() => { + jest.clearAllTimers(); + jest.clearAllMocks(); + }); + test('should set timeout with provided callback and timeout', () => { - // Write your test here + const cb = jest.fn(); + const timeout = 250; + + const timeoutSpy = jest.spyOn(global, 'setTimeout'); + + doStuffByTimeout(cb, timeout); + + expect(timeoutSpy).toHaveBeenCalledTimes(1); + expect(timeoutSpy).toHaveBeenCalledWith(cb, timeout); }); test('should call callback only after timeout', () => { - // Write your test here + const cb = jest.fn(); + const timeout = 300; + + doStuffByTimeout(cb, timeout); + + expect(cb).not.toHaveBeenCalled(); + + jest.advanceTimersByTime(299); + expect(cb).not.toHaveBeenCalled(); + + jest.advanceTimersByTime(1); + expect(cb).toHaveBeenCalledTimes(1); }); }); @@ -23,30 +62,79 @@ describe('doStuffByInterval', () => { beforeAll(() => { jest.useFakeTimers(); }); - afterAll(() => { jest.useRealTimers(); }); + afterEach(() => { + jest.clearAllTimers(); + jest.clearAllMocks(); + }); test('should set interval with provided callback and timeout', () => { - // Write your test here + const cb = jest.fn(); + const every = 500; + + const intervalSpy = jest.spyOn(global, 'setInterval'); + + doStuffByInterval(cb, every); + + expect(intervalSpy).toHaveBeenCalledTimes(1); + expect(intervalSpy).toHaveBeenCalledWith(cb, every); }); test('should call callback multiple times after multiple intervals', () => { - // Write your test here + const cb = jest.fn(); + const every = 200; + + doStuffByInterval(cb, every); + + expect(cb).not.toHaveBeenCalled(); + + jest.advanceTimersByTime(200); + expect(cb).toHaveBeenCalledTimes(1); + + jest.advanceTimersByTime(200); + expect(cb).toHaveBeenCalledTimes(2); + + jest.advanceTimersByTime(600); + expect(cb).toHaveBeenCalledTimes(5); }); }); describe('readFileAsynchronously', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + test('should call join with pathToFile', async () => { - // Write your test here + (join as jest.Mock).mockReturnValue('/fake/full/path'); + (existsSync as jest.Mock).mockReturnValue(false); + + const rel = 'data.txt'; + await readFileAsynchronously(rel); + + expect(join).toHaveBeenCalledTimes(1); + expect(join).toHaveBeenCalledWith(expect.any(String), rel); }); test('should return null if file does not exist', async () => { - // Write your test here + (join as jest.Mock).mockReturnValue('/fake/full/path'); + (existsSync as jest.Mock).mockReturnValue(false); + + const result = await readFileAsynchronously('missing.txt'); + + expect(result).toBeNull(); + expect(readFile).not.toHaveBeenCalled(); }); test('should return file content if file exists', async () => { - // Write your test here + (join as jest.Mock).mockReturnValue('/fake/full/path'); + (existsSync as jest.Mock).mockReturnValue(true); + (readFile as jest.Mock).mockResolvedValue(Buffer.from('HELLO')); + + const result = await readFileAsynchronously('ok.txt'); + + expect(readFile).toHaveBeenCalledWith('/fake/full/path'); + expect(result).toBe('HELLO'); }); }); diff --git a/src/07-mocking-lib-api/index.test.ts b/src/07-mocking-lib-api/index.test.ts index e1dd001ef..42a1c3a9c 100644 --- a/src/07-mocking-lib-api/index.test.ts +++ b/src/07-mocking-lib-api/index.test.ts @@ -1,17 +1,59 @@ // Uncomment the code below and write your tests -/* import axios from 'axios'; -import { throttledGetDataFromApi } from './index'; */ +jest.mock('axios', () => ({ + __esModule: true, + default: { + create: jest.fn(), + }, +})); + +jest.mock('lodash', () => ({ + __esModule: true, + throttle: jest.fn((fn: unknown) => fn), +})); + +import axios from 'axios'; +import { throttledGetDataFromApi } from './index'; + +const mockedAxios = axios as unknown as { + create: jest.Mock; +}; describe('throttledGetDataFromApi', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + test('should create instance with provided base url', async () => { - // Write your test here + const get = jest.fn().mockResolvedValue({ data: { ok: true } }); + mockedAxios.create.mockReturnValue({ get }); + + await throttledGetDataFromApi('/posts'); + + expect(mockedAxios.create).toHaveBeenCalledTimes(1); + expect(mockedAxios.create).toHaveBeenCalledWith({ + baseURL: 'https://jsonplaceholder.typicode.com', + }); }); test('should perform request to correct provided url', async () => { - // Write your test here + const get = jest.fn().mockResolvedValue({ data: {} }); + mockedAxios.create.mockReturnValue({ get }); + + const path = '/todos/3'; + + await throttledGetDataFromApi(path); + + expect(get).toHaveBeenCalledTimes(1); + expect(get).toHaveBeenCalledWith(path); }); test('should return response data', async () => { - // Write your test here + const payload = { id: 1, title: 'hello' }; + const get = jest.fn().mockResolvedValue({ data: payload }); + mockedAxios.create.mockReturnValue({ get }); + + const result = await throttledGetDataFromApi('/posts/1'); + + expect(result).toEqual(payload); }); }); diff --git a/src/08-snapshot-testing/__snapshots__/index.test.ts.snap b/src/08-snapshot-testing/__snapshots__/index.test.ts.snap new file mode 100644 index 000000000..b686a2d72 --- /dev/null +++ b/src/08-snapshot-testing/__snapshots__/index.test.ts.snap @@ -0,0 +1,17 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`generateLinkedList should generate linked list from values 2 1`] = ` +{ + "next": { + "next": { + "next": { + "next": null, + "value": null, + }, + "value": null, + }, + "value": "b", + }, + "value": "a", +} +`; diff --git a/src/08-snapshot-testing/index.test.ts b/src/08-snapshot-testing/index.test.ts index 67c345706..5fdf29fdf 100644 --- a/src/08-snapshot-testing/index.test.ts +++ b/src/08-snapshot-testing/index.test.ts @@ -1,14 +1,32 @@ // Uncomment the code below and write your tests -// import { generateLinkedList } from './index'; +import { generateLinkedList } from './index'; describe('generateLinkedList', () => { - // Check match by expect(...).toStrictEqual(...) test('should generate linked list from values 1', () => { - // Write your test here + const values = [1, 2, 3]; + + const list = generateLinkedList(values); + + expect(list).toStrictEqual({ + value: 1, + next: { + value: 2, + next: { + value: 3, + next: { + value: null, + next: null, + }, + }, + }, + }); }); - // Check match by comparison with snapshot test('should generate linked list from values 2', () => { - // Write your test here + const values = ['a', 'b', undefined]; + + const list = generateLinkedList(values); + + expect(list).toMatchSnapshot(); }); }); diff --git a/tsconfig.json b/tsconfig.json index 75e87e7f9..00f644b0a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -19,5 +19,5 @@ "strict": true, "strictPropertyInitialization": false, "target": "ES2020" - }, + } }