diff --git a/__mocks__/file-mock.ts b/__mocks__/file-mock.ts new file mode 100644 index 0000000..3a8edd0 --- /dev/null +++ b/__mocks__/file-mock.ts @@ -0,0 +1 @@ +module.exports = 'file'; diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 0000000..0ab57b5 --- /dev/null +++ b/babel.config.js @@ -0,0 +1,7 @@ +module.exports = { + presets: [ + '@babel/preset-env', + '@babel/preset-typescript', + ['@babel/preset-react', { runtime: 'automatic' }], + ], +}; diff --git a/eslint.config.mjs b/eslint.config.mjs index 6ceacd6..90143cb 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,12 +1,13 @@ -import globals from "globals"; -import pluginJs from "@eslint/js"; -import tseslint from "typescript-eslint"; -import pluginReact from "eslint-plugin-react"; +import globals from 'globals'; +import pluginJs from '@eslint/js'; +import tseslint from 'typescript-eslint'; +import pluginReact from 'eslint-plugin-react'; import stylistic from '@stylistic/eslint-plugin'; import pluginImport from 'eslint-plugin-import'; export default [ - { files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"] }, + { files: ['**/*.{js,mjs,cjs,ts,jsx,tsx}'] }, + { languageOptions: { globals: globals.browser } }, pluginJs.configs.recommended, ...tseslint.configs.recommended, @@ -14,35 +15,38 @@ export default [ { plugins: { '@stylistic': stylistic, - 'import': pluginImport, + import: pluginImport, }, - "rules": { - "no-unused-vars": "off", - "@typescript-eslint/no-unused-vars": [ - "warn", // or "error" + rules: { + 'no-unused-vars': 'off', + '@typescript-eslint/no-unused-vars': [ + 'warn', // or "error" { - "argsIgnorePattern": "^_", - "varsIgnorePattern": "^_", - "caughtErrorsIgnorePattern": "^_" - } + argsIgnorePattern: '^_', + varsIgnorePattern: '^_', + caughtErrorsIgnorePattern: '^_', + }, ], - "sort-imports": ["off"], - "import/order": [ - "error", + 'sort-imports': ['off'], + 'import/order': [ + 'error', { - "groups": [ - "builtin", - "external", - "internal", - "parent", - ["sibling", "index"] + groups: [ + 'builtin', + 'external', + 'internal', + 'parent', + ['sibling', 'index'], ], - "newlines-between": "always", - } + 'newlines-between': 'always', + }, ], - semi: ["error", "always"], + semi: ['error', 'always'], '@stylistic/indent': ['error', 2], - 'react/prop-types': 'off' + 'react/prop-types': 'off', }, - } + }, + { + ignores: ['babel.config.js'], + }, ]; diff --git a/jest-preset-it/jest-preset.ts b/jest-preset-it/jest-preset.ts new file mode 100644 index 0000000..b633b57 --- /dev/null +++ b/jest-preset-it/jest-preset.ts @@ -0,0 +1,17 @@ +module.exports = { + transform: { + '^.+\\.tsx?$': 'babel-jest', + }, + coverageProvider: 'v8', + coverageDirectory: 'coverage', + collectCoverageFrom: ['**/src/**/*.{ts,tsx}', '!**/src/app.tsx'], + collectCoverage: true, + clearMocks: true, + moduleNameMapper: { + '\\.(svg|webp)$': '/__mocks__/file', + }, + testEnvironmentOptions: { + customExportConditions: [''], + }, + testEnvironment: 'jest-fixed-jsdom', +}; diff --git a/package.json b/package.json index 2f13505..e95516f 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "", "main": "./src/index.tsx", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", + "test": "jest -u", "start": "brojs server --port=8099 --with-open-browser", "build": "npm run clean && brojs build --dev", "build:prod": "npm run clean && brojs build", @@ -17,6 +17,10 @@ "author": "", "license": "ISC", "dependencies": { + "@babel/core": "^7.26.7", + "@babel/preset-env": "^7.26.7", + "@babel/preset-react": "^7.26.3", + "@babel/preset-typescript": "^7.26.0", "@brojs/cli": "^1.8.4", "@chakra-ui/icons": "^2.2.4", "@chakra-ui/react": "^2.10.5", @@ -26,12 +30,19 @@ "@lottiefiles/react-lottie-player": "^3.5.4", "@pbe/react-yandex-maps": "^1.2.5", "@reduxjs/toolkit": "^2.5.0", + "@testing-library/dom": "^10.4.0", + "@testing-library/react": "^16.2.0", "@types/react": "^18.3.12", + "babel-jest": "^29.7.0", "dayjs": "^1.11.13", "express": "^4.21.1", "framer-motion": "^6.2.8", "i18next": "^23.16.4", + "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", + "jest-fixed-jsdom": "^0.0.9", "keycloak-js": "^23.0.7", + "msw": "^2.7.0", "react": "^18.3.1", "react-dom": "^18.3.1", "react-hook-form": "^7.53.2", @@ -39,7 +50,9 @@ "react-icons": "^5.3.0", "react-phone-number-input": "^3.4.9", "react-redux": "^9.2.0", - "react-router-dom": "^6.27.0" + "react-router-dom": "^6.27.0", + "ts-jest": "^29.2.5", + "ts-node": "^10.9.2" }, "devDependencies": { "@eslint/js": "^9.14.0", @@ -50,6 +63,10 @@ "eslint-plugin-react": "^7.37.2", "globals": "^15.11.0", "prettier": "3.3.3", + "typescript": "^5.7.3", "typescript-eslint": "^8.12.2" + }, + "jest": { + "preset": "./jest-preset-it/jest-preset.ts" } } diff --git a/src/pages/__tests__/__snapshots__/arm.test.tsx.snap b/src/pages/__tests__/__snapshots__/arm.test.tsx.snap new file mode 100644 index 0000000..62efc6d --- /dev/null +++ b/src/pages/__tests__/__snapshots__/arm.test.tsx.snap @@ -0,0 +1,325 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Arm Page render 1`] = ` +
+
+
+

+ title +

+ +
+
+
+

+ title +

+
+ +

+ 2/2/2025 +

+ +
+ + + + + + + + + + + + + + + + +
+ table.header.carNumber + + table.header.orderDate + + table.header.status + + table.header.masters + + table.header.telephone + + table.header.location +
+
+ + Loading... + +
+
+
+
+
+
+`; + +exports[`Arm Page render 2`] = ` +
+
+
+

+ title +

+ +
+
+
+

+ title +

+
+ +

+ 2/2/2025 +

+ +
+ + + + + + + + + + + + + + + + +
+ table.header.carNumber + + table.header.orderDate + + table.header.status + + table.header.masters + + table.header.telephone + + table.header.location +
+
+ + Loading... + +
+
+
+
+
+
+`; diff --git a/src/pages/__tests__/arm.test.tsx b/src/pages/__tests__/arm.test.tsx new file mode 100644 index 0000000..2db3ef6 --- /dev/null +++ b/src/pages/__tests__/arm.test.tsx @@ -0,0 +1,130 @@ +import React from 'react'; +import { + describe, + it, + expect, + jest, + beforeAll, + afterEach, + afterAll, +} from '@jest/globals'; +import { render, screen, waitFor } from '@testing-library/react'; +import { http, HttpResponse } from 'msw'; +import { setupServer } from 'msw/node'; +import { BrowserRouter } from 'react-router-dom'; + +import Page from '../arm'; + +const server = setupServer( + http.post('/api/arm/orders', () => { + return HttpResponse.json({ + success: true, + body: [ + { + id: 'order1', + carNumber: 'A123BC', + startWashTime: '2024-11-24T10:30:00.000Z', + endWashTime: '2024-11-24T16:30:00.000Z', + orderDate: '2024-11-24T08:41:46.366Z', + status: 'pending', + phone: '79001234563', + location: 'Казань, ул. Баумана, 1', + master: { + name: 'Олег Макаров', + phone: '79001234567', + id: '23423442', + }, + notes: '', + }, + { + id: 'order2', + carNumber: 'A245BC', + startWashTime: '2024-11-24T11:30:00.000Z', + endWashTime: '2024-11-24T17:30:00.000Z', + orderDate: '2024-11-24T07:40:46.366Z', + status: 'progress', + phone: '79001234567', + location: 'Казань, ул. Баумана, 43', + master: [], + notes: '', + }, + ], + }); + }), + http.get('/api/arm/masters', () => { + return HttpResponse.json({ + success: true, + body: [ + { + id: '4545423234', + name: 'Иван Иванов', + phone: '+7 900 123 45 67', + }, + { + name: 'Олег Макаров', + phone: '79001234567', + id: '23423442', + }, + { + id: '345354234', + name: 'Иван Галкин', + schedule: [ + { + id: 'order1', + startWashTime: '2024-11-24T10:30:00.000Z', + endWashTime: '2024-11-24T16:30:00.000Z', + }, + { + id: 'order2', + startWashTime: '2024-11-24T11:30:00.000Z', + endWashTime: '2024-11-24T17:30:00.000Z', + }, + ], + phone: '+7 900 123 45 67', + }, + ], + }); + }), +); + +jest.mock('react-i18next', () => { + return { + useTranslation: () => { + return { + t: (key: never) => `${key}`, + i18n: {}, + }; + }, + }; +}); + +jest.mock('@brojs/cli', () => { + return { + getNavigationValue: () => '/auth/login', + getConfigValue: () => '/api', + }; +}); + +describe('Arm Page', () => { + beforeAll(() => server.listen()); + afterEach(() => server.resetHandlers()); + afterAll(() => server.close()); + + it('render ', async () => { + server.events.on('request:start', ({ request }) => { + console.log('Outgoing:', request.method, request.url); + }); + + const { container } = render( + + + , + ); + + expect(container).toMatchSnapshot(); + + await waitFor(() => screen.getByText('A123BC')); + + expect(container).toMatchSnapshot(); + }); +}); diff --git a/src/pages/arm/index.tsx b/src/pages/arm/index.tsx index 6c1a376..300fe61 100644 --- a/src/pages/arm/index.tsx +++ b/src/pages/arm/index.tsx @@ -6,8 +6,12 @@ import LayoutArm from '../../components/LayoutArm'; import authLogin from '../../keycloak'; import { URLs } from '../../__data__/urls'; -const Page = () => { - const [user, setUser] = useState(null); +interface PageProps { + mockUser?: { name: string }; +} + +const Page = ({ mockUser }: PageProps) => { + const [user, setUser] = useState(mockUser || null); const navigate = useNavigate();