From b631b457c0a22803085646a8dafba443562a4820 Mon Sep 17 00:00:00 2001 From: Serhii Pimenov Date: Fri, 18 Jul 2025 14:35:42 +0300 Subject: [PATCH 01/11] upd dependencies --- easydata.js/bundles/crud/rollup.config.mjs | 2 +- easydata.js/packs/core/rollup.config.mjs | 16 +- easydata.js/packs/crud/__tests__/crud.test.js | 5 - easydata.js/packs/crud/package.json | 3 +- easydata.js/packs/crud/rollup.config.mjs | 14 +- .../packs/crud/tests/data_context.test.ts | 331 ++++++++++++ .../tests/easy_data_view_dispatcher.test.ts | 304 +++++++++++ .../packs/crud/tests/entity_data_view.test.ts | 406 +++++++++++++++ .../packs/crud/tests/entity_edit_form.test.ts | 351 +++++++++++++ .../crud/tests/entity_form_builder.test.ts | 376 ++++++++++++++ easydata.js/packs/crud/tests/prompt.txt | 1 + .../packs/crud/tests/root_data_view.test.ts | 222 ++++++++ .../packs/crud/tests/text_data_filter.test.ts | 225 ++++++++ .../crud/tests/text_filter_widget.test.ts | 491 ++++++++++++++++++ easydata.js/packs/crud/tests/utils.test.ts | 127 +++++ .../packs/crud/tests/validators.test.ts | 281 ++++++++++ easydata.js/packs/ui/package.json | 4 +- easydata.js/packs/ui/rollup.config.mjs | 14 +- package.json | 20 +- .../rollup.config.mjs | 2 +- .../rollup.config.mjs | 2 +- 21 files changed, 3153 insertions(+), 44 deletions(-) delete mode 100644 easydata.js/packs/crud/__tests__/crud.test.js create mode 100644 easydata.js/packs/crud/tests/data_context.test.ts create mode 100644 easydata.js/packs/crud/tests/easy_data_view_dispatcher.test.ts create mode 100644 easydata.js/packs/crud/tests/entity_data_view.test.ts create mode 100644 easydata.js/packs/crud/tests/entity_edit_form.test.ts create mode 100644 easydata.js/packs/crud/tests/entity_form_builder.test.ts create mode 100644 easydata.js/packs/crud/tests/prompt.txt create mode 100644 easydata.js/packs/crud/tests/root_data_view.test.ts create mode 100644 easydata.js/packs/crud/tests/text_data_filter.test.ts create mode 100644 easydata.js/packs/crud/tests/text_filter_widget.test.ts create mode 100644 easydata.js/packs/crud/tests/utils.test.ts create mode 100644 easydata.js/packs/crud/tests/validators.test.ts diff --git a/easydata.js/bundles/crud/rollup.config.mjs b/easydata.js/bundles/crud/rollup.config.mjs index 72df10a2..28f46bb3 100644 --- a/easydata.js/bundles/crud/rollup.config.mjs +++ b/easydata.js/bundles/crud/rollup.config.mjs @@ -3,7 +3,7 @@ import commonjs from '@rollup/plugin-commonjs' import terser from '@rollup/plugin-terser' import progress from 'rollup-plugin-progress' import typescript from '@rollup/plugin-typescript' -import typedoc from '@olton/rollup-plugin-typedoc' +// import typedoc from '@olton/rollup-plugin-typedoc' import multi from '@rollup/plugin-multi-entry' import * as path from "path"; import { fileURLToPath } from 'url'; diff --git a/easydata.js/packs/core/rollup.config.mjs b/easydata.js/packs/core/rollup.config.mjs index 65ffe9fd..76fb6368 100644 --- a/easydata.js/packs/core/rollup.config.mjs +++ b/easydata.js/packs/core/rollup.config.mjs @@ -3,10 +3,10 @@ import commonjs from '@rollup/plugin-commonjs' import terser from '@rollup/plugin-terser' import progress from 'rollup-plugin-progress' import typescript from '@rollup/plugin-typescript' -import typedoc from '@olton/rollup-plugin-typedoc' +// import typedoc from '@olton/rollup-plugin-typedoc' import * as path from "path"; import { fileURLToPath } from 'url'; -import pkg from './package.json' assert { type: 'json' }; +import pkg from './package.json' with { type: 'json' }; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -37,12 +37,12 @@ export default [ typescript({ sourceMap: sourcemap, }), nodeResolve({ browser: true, }), commonjs(), - typedoc({ - json: '../../docs/easydata-core.json', - out: './docs', - entryPoints: ['./src/**/*.ts'], - tsconfig: './tsconfig.json', - }), + // typedoc({ + // json: '../../docs/easydata-core.json', + // out: './docs', + // entryPoints: ['./src/**/*.ts'], + // tsconfig: './tsconfig.json', + // }), ], output: [ { diff --git a/easydata.js/packs/crud/__tests__/crud.test.js b/easydata.js/packs/crud/__tests__/crud.test.js deleted file mode 100644 index 138d9277..00000000 --- a/easydata.js/packs/crud/__tests__/crud.test.js +++ /dev/null @@ -1,5 +0,0 @@ -import { expect, test } from 'vitest' - -test('Test', () => { - console.info('crud tests passed'); -}) diff --git a/easydata.js/packs/crud/package.json b/easydata.js/packs/crud/package.json index c3e1cf37..aac30fe3 100644 --- a/easydata.js/packs/crud/package.json +++ b/easydata.js/packs/crud/package.json @@ -10,7 +10,8 @@ "clear": "shx rm -rf dist/* docs/* lib/*", "build": "npm run clear && rollup -c", "watch": "rollup -c -w", - "docs": "typedoc src/public_api.ts --out ./docs" + "docs": "typedoc src/public_api.ts --out ./docs", + "test": "cross-env NODE_OPTIONS=\"--import tsx\" latte -d -v -t --include=./**/*.test.ts" }, "author": "Korzh.com", "homepage": "https://github.com/KorzhCom/EasyData", diff --git a/easydata.js/packs/crud/rollup.config.mjs b/easydata.js/packs/crud/rollup.config.mjs index 7e8331b3..db8bcf28 100644 --- a/easydata.js/packs/crud/rollup.config.mjs +++ b/easydata.js/packs/crud/rollup.config.mjs @@ -3,7 +3,7 @@ import commonjs from '@rollup/plugin-commonjs' import terser from '@rollup/plugin-terser' import progress from 'rollup-plugin-progress' import typescript from '@rollup/plugin-typescript' -import typedoc from '@olton/rollup-plugin-typedoc' +// import typedoc from '@olton/rollup-plugin-typedoc' import multi from '@rollup/plugin-multi-entry' import * as path from "path"; import { fileURLToPath } from 'url'; @@ -40,12 +40,12 @@ export default [ typescript({ sourceMap: sourcemap, }), nodeResolve({ browser: true, }), commonjs(), - typedoc({ - json: '../../docs/easydata-crud.json', - out: './docs', - entryPoints: ['./src/**/*.ts'], - tsconfig: './tsconfig.json' - }), + // typedoc({ + // json: '../../docs/easydata-crud.json', + // out: './docs', + // entryPoints: ['./src/**/*.ts'], + // tsconfig: './tsconfig.json' + // }), ], external: [ "@easydata/core", "@easydata/ui" diff --git a/easydata.js/packs/crud/tests/data_context.test.ts b/easydata.js/packs/crud/tests/data_context.test.ts new file mode 100644 index 00000000..1bb999a6 --- /dev/null +++ b/easydata.js/packs/crud/tests/data_context.test.ts @@ -0,0 +1,331 @@ +import { expect } from "@olton/latte" + +import { + HttpClient, MetaData, HttpActionResult, + EasyDataTable, DataType, DataColumnList, DataRow +} from '@easydata/core'; + +import { DataContext } from '../src/main/data_context'; +import { TextDataFilter } from '../src/filter/text_data_filter'; +import { EasyDataServerLoader } from '../src/main/easy_data_server_loader'; + +describe('DataContext', () => { + let dataContext: DataContext; + let mockHttpClient: HttpClient; + let mockActionResult: HttpActionResult; + let processStartCount: number; + let processEndCount: number; + + beforeEach(() => { + // Создаем мок для HttpClient + mockHttpClient = { + get: mock(), + post: mock(), + } as unknown as HttpClient; + + // Создаем мок для HttpActionResult + mockActionResult = { + then: mock().mockImplementation((callback) => { + callback({ + model: { + id: 'test-model', + name: 'Test Model', + entroot: { + name: 'Root', + attrs: [], + ents: [ + { id: 'entity1', name: 'Entity1', attrs: [], ents: [] } + ] + } + } + }); + return mockActionResult; + }), + catch: mock().mockImplementation((callback) => { + return mockActionResult; + }), + finally: mock().mockImplementation((callback) => { + callback(); + return mockActionResult; + }) + } as unknown as HttpActionResult; + + (mockHttpClient.get as jest.Mock).mockReturnValue(mockActionResult); + (mockHttpClient.post as jest.Mock).mockReturnValue(mockActionResult); + + // Счетчики для методов onProcessStart и onProcessEnd + processStartCount = 0; + processEndCount = 0; + + // Создаем экземпляр DataContext с моками + dataContext = new DataContext({ + metaDataId: 'test-model', + endpoint: '/api/test', + onProcessStart: () => { processStartCount++; }, + onProcessEnd: () => { processEndCount++; } + }); + + // Подменяем HttpClient в DataContext на наш мок + (dataContext as any).http = mockHttpClient; + }); + + it('должен инициализироваться с правильными значениями', () => { + // Проверка базовых свойств + const metaData = dataContext.getMetaData(); + expect(metaData).toBeInstanceOf(MetaData); + expect(metaData.id).toBe('test-model'); + + const data = dataContext.getData(); + expect(data).toBeInstanceOf(EasyDataTable); + + const dataLoader = dataContext.getDataLoader(); + expect(dataLoader).toBeInstanceOf(EasyDataServerLoader); + }); + + it('должен правильно настраивать эндпоинты по умолчанию', () => { + // Проверка эндпоинтов по умолчанию + expect(() => { + const endpoint = dataContext.resolveEndpoint('GetMetaData'); + expect(endpoint).toBe('/api/test/models/test-model'); + }).not.toThrow(); + + expect(() => { + const endpoint = dataContext.resolveEndpoint('FetchDataset'); + // На данном этапе activeEntity не установлен, поэтому должна быть ошибка + // Но сам эндпоинт должен существовать + }).toThrow(); + }); + + it('должен устанавливать и получать активную сущность', () => { + // Загружаем метаданные + dataContext.loadMetaData().then(() => { + // Устанавливаем активную сущность + dataContext.setActiveSource('entity1'); + + // Проверяем, что активная сущность установлена + const activeEntity = dataContext.getActiveEntity(); + expect(activeEntity).toBeDefined(); + expect(activeEntity.id).toBe('entity1'); + }); + }); + + it('должен устанавливать кастомные эндпоинты', () => { + // Устанавливаем кастомный эндпоинт + dataContext.setEndpoint('GetMetaData', '/custom/api/metadata'); + + // Проверяем что эндпоинт установлен + const endpoint = dataContext.resolveEndpoint('GetMetaData'); + expect(endpoint).toBe('/custom/api/metadata'); + }); + + it('не должен перезаписывать существующие эндпоинты при использовании setEnpointIfNotExist', () => { + // Устанавливаем эндпоинт + dataContext.setEndpoint('GetMetaData', '/custom/api/metadata'); + + // Пытаемся установить другой эндпоинт через setEnpointIfNotExist + dataContext.setEnpointIfNotExist('GetMetaData', '/another/endpoint'); + + // Проверяем что эндпоинт не изменился + const endpoint = dataContext.resolveEndpoint('GetMetaData'); + expect(endpoint).toBe('/custom/api/metadata'); + }); + + it('должен разрешать эндпоинты с параметрами', () => { + // Устанавливаем эндпоинт с параметрами + dataContext.setEndpoint('CustomEndpoint', '/api/custom/{param1}/{param2}'); + + // Разрешаем эндпоинт с параметрами + const endpoint = dataContext.resolveEndpoint('CustomEndpoint', { param1: 'value1', param2: 'value2' }); + + // Проверяем что параметры подставлены + expect(endpoint).toBe('/api/custom/value1/value2'); + }); + + it('должен выбрасывать ошибку при отсутствии обязательного параметра', () => { + // Устанавливаем эндпоинт с параметрами + dataContext.setEndpoint('CustomEndpoint', '/api/custom/{param1}/{param2}'); + + // Пытаемся разрешить эндпоинт с отсутствующим параметром + expect(() => { + dataContext.resolveEndpoint('CustomEndpoint', { param1: 'value1' }); + }).toThrow('Parameter [param2] is not defined'); + }); + + it('должен загружать метаданные', () => { + const promise = dataContext.loadMetaData(); + + // Проверяем что загрузка вызвала правильный метод HTTP клиента + expect(mockHttpClient.get).toHaveBeenCalled(); + expect((mockHttpClient.get as jest.Mock).mock.calls[0][0]).toContain('/api/test/models/test-model'); + + return promise.then((model) => { + // Проверяем что модель была загружена + expect(model).toBeDefined(); + expect(model.id).toBe('test-model'); + + // Проверяем что были вызваны методы startProcess и endProcess + expect(processStartCount).toBe(1); + expect(processEndCount).toBe(1); + }); + }); + + it('должен создавать фильтр данных', () => { + // Загружаем метаданные и устанавливаем активную сущность + return dataContext.loadMetaData() + .then(() => { + dataContext.setActiveSource('entity1'); + + // Создаем фильтр + const filter = dataContext.createFilter(); + + // Проверяем созданный фильтр + expect(filter).toBeInstanceOf(TextDataFilter); + }); + }); + + it('должен загружать датасет', () => { + // Настраиваем мок для loadChunk + const mockData = new EasyDataTable(); + const mockColumns = new DataColumnList(); + mockColumns.add({ id: 'id', label: 'ID', type: DataType.Int32 }); + mockColumns.add({ id: 'name', label: 'Name', type: DataType.String }); + mockData.columns = mockColumns; + mockData.addRow([1, 'Test']); + + const dataLoader = dataContext.getDataLoader() as EasyDataServerLoader; + (dataLoader.loadChunk) = mock().mockResolvedValue({ + table: mockData, + total: 1 + }); + + // Загружаем метаданные и устанавливаем активную сущность + return dataContext.loadMetaData() + .then(() => { + dataContext.setActiveSource('entity1'); + + // Загружаем датасет + return dataContext.fetchDataset(); + }) + .then((data) => { + // Проверяем результат + expect(data).toBeDefined(); + expect(data.columns.count).toBe(2); + expect(data.getCachedCount()).toBe(1); + + // Проверяем что dataLoader.loadChunk был вызван + expect(dataLoader.loadChunk).toHaveBeenCalled(); + }); + }); + + it('должен получать запись', () => { + // Загружаем метаданные и устанавливаем активную сущность + return dataContext.loadMetaData() + .then(() => { + dataContext.setActiveSource('entity1'); + + // Получаем запись + return dataContext.fetchRecord({ id: 1 }); + }) + .then(() => { + // Проверяем что был вызван правильный метод HTTP клиента + expect(mockHttpClient.get).toHaveBeenCalled(); + + const lastCall = (mockHttpClient.get).mock.calls.pop(); + expect(lastCall[0]).toContain('/api/test/models/test-model/sources/entity1/fetch'); + expect(lastCall[1].queryParams).toBeDefined(); + expect(lastCall[1].queryParams.id).toBe(1); + + // Проверяем что были вызваны методы startProcess и endProcess + expect(processStartCount).toBeGreaterThan(0); + expect(processEndCount).toBeGreaterThan(0); + }); + }); + + it('должен создавать запись', () => { + // Загружаем метаданные и устанавливаем активную сущность + return dataContext.loadMetaData() + .then(() => { + dataContext.setActiveSource('entity1'); + + // Тестовый объект для создания + const testObj = { name: 'Test Record', value: 123 }; + + // Создаем запись + return dataContext.createRecord(testObj); + }) + .then(() => { + // Проверяем что был вызван правильный метод HTTP клиента + expect(mockHttpClient.post).toHaveBeenCalled(); + + const lastCall = (mockHttpClient.post).mock.calls.pop(); + expect(lastCall[0]).toContain('/api/test/models/test-model/sources/entity1/create'); + expect(lastCall[1]).toBeDefined(); + expect(lastCall[1].name).toBe('Test Record'); + expect(lastCall[1].value).toBe(123); + + // Проверяем что были вызваны методы startProcess и endProcess + expect(processStartCount).toBeGreaterThan(0); + expect(processEndCount).toBeGreaterThan(0); + }); + }); + + it('должен обновлять запись', () => { + // Загружаем метаданные и устанавливаем активную сущность + return dataContext.loadMetaData() + .then(() => { + dataContext.setActiveSource('entity1'); + + // Тестовый объект для обновления + const testObj = { id: 1, name: 'Updated Record', value: 456 }; + + // Обновляем запись + return dataContext.updateRecord(testObj); + }) + .then(() => { + // Проверяем что был вызван правильный метод HTTP клиента + expect(mockHttpClient.post).toHaveBeenCalled(); + + const lastCall = (mockHttpClient.post).mock.calls.pop(); + expect(lastCall[0]).toContain('/api/test/models/test-model/sources/entity1/update'); + expect(lastCall[1]).toBeDefined(); + expect(lastCall[1].id).toBe(1); + expect(lastCall[1].name).toBe('Updated Record'); + + // Проверяем что были вызваны методы startProcess и endProcess + expect(processStartCount).toBeGreaterThan(0); + expect(processEndCount).toBeGreaterThan(0); + }); + }); + + it('должен удалять запись', () => { + // Загружаем метаданные и устанавливаем активную сущность + return dataContext.loadMetaData() + .then(() => { + dataContext.setActiveSource('entity1'); + + // Тестовый объект для удаления + const testObj = { id: 1 }; + + // Удаляем запись + return dataContext.deleteRecord(testObj); + }) + .then(() => { + // Проверяем что был вызван правильный метод HTTP клиента + expect(mockHttpClient.post).toHaveBeenCalled(); + + const lastCall = (mockHttpClient.post).mock.calls.pop(); + expect(lastCall[0]).toContain('/api/test/models/test-model/sources/entity1/delete'); + expect(lastCall[1]).toBeDefined(); + expect(lastCall[1].id).toBe(1); + + // Проверяем что были вызваны методы startProcess и endProcess + expect(processStartCount).toBeGreaterThan(0); + expect(processEndCount).toBeGreaterThan(0); + }); + }); + + it('должен возвращать HttpClient', () => { + const httpClient = dataContext.getHttpClient(); + expect(httpClient).toBe(mockHttpClient); + }); +}); diff --git a/easydata.js/packs/crud/tests/easy_data_view_dispatcher.test.ts b/easydata.js/packs/crud/tests/easy_data_view_dispatcher.test.ts new file mode 100644 index 00000000..e9c800ef --- /dev/null +++ b/easydata.js/packs/crud/tests/easy_data_view_dispatcher.test.ts @@ -0,0 +1,304 @@ +import { expect } from "@olton/latte" + +import { MetaData } from '@easydata/core'; +import { DataContext } from '../src/main/data_context'; +import { EasyDataViewDispatcher } from '../src/views/easy_data_view_dispatcher'; +import { EntityDataView } from '../src/views/entity_data_view'; +import { RootDataView } from '../src/views/root_data_view'; +import * as utils from '../src/utils/utils'; + +describe('EasyDataViewDispatcher', () => { + // Оригинальные объекты для восстановления после тестов + const originalLocation = window.location; + const originalWindowAddEventListener = window.addEventListener; + const originalWindowRemoveEventListener = window.removeEventListener; + + // Мок элементы для DOM + let mockContainer: HTMLElement; + let mockParent: HTMLElement; + + beforeEach(() => { + // Создаем моки для DOM элементов + mockContainer = document.createElement('div'); + mockContainer.id = 'testContainer'; + + mockParent = document.createElement('div'); + mockParent.appendChild(mockContainer); + document.body.appendChild(mockParent); + + // Мок для window.location + delete (window as any).location; + window.location = { + ...originalLocation, + pathname: '/app/easydata/entity1', + href: 'http://example.com/app/easydata/entity1' + } as Location; + + // Моки для addEventListener и removeEventListener + window.addEventListener = mock(); + window.removeEventListener = mock(); + + // Мок для loadMetaData + // jest.spyOn(DataContext.prototype, 'loadMetaData').mockImplementation(() => { + // return Promise.resolve(new MetaData()); + // }); + // + // // Мок для setActiveSource + // jest.spyOn(DataContext.prototype, 'setActiveSource').mockImplementation(() => {}); + // + // // Мок для setLocation + // jest.spyOn(utils, 'setLocation').mockImplementation(() => {}); + // + // // Сохраняем оригинальные конструкторы представлений + // jest.spyOn(EntityDataView.prototype, 'constructor').mockImplementation(() => {}); + // jest.spyOn(RootDataView.prototype, 'constructor').mockImplementation(() => {}); + }); + + afterEach(() => { + // Восстанавливаем оригинальные объекты и методы + window.location = originalLocation; + window.addEventListener = originalWindowAddEventListener; + window.removeEventListener = originalWindowRemoveEventListener; + + // Удаляем добавленные элементы + if (mockParent.parentNode) { + mockParent.parentNode.removeChild(mockParent); + } + + // Сбрасываем все моки + // jest.restoreAllMocks(); + + // Удаляем глобальную переменную EDView + delete window['EDView']; + }); + + it('должен быть создан с настройками по умолчанию', () => { + const dispatcher = new EasyDataViewDispatcher(); + + expect(dispatcher).toBeDefined(); + + // Проверяем private свойства через any + const options = (dispatcher as any).options; + expect(options).toBeDefined(); + expect(options.container).toBe('#EasyDataContainer'); + expect(options.basePath).toBe('easydata'); + }); + + it('должен быть создан с пользовательскими настройками', () => { + const dispatcher = new EasyDataViewDispatcher({ + container: '#customContainer', + basePath: 'custom-path', + endpoint: '/api/easydata', + showBackToEntities: true + }); + + expect(dispatcher).toBeDefined(); + + const options = (dispatcher as any).options; + expect(options).toBeDefined(); + expect(options.container).toBe('#customContainer'); + expect(options.basePath).toBe('custom-path'); + expect(options.endpoint).toBe('/api/easydata'); + expect(options.showBackToEntities).toBe(true); + }); + + it('должен корректно обрабатывать настройку rootEntity', () => { + const dispatcher = new EasyDataViewDispatcher({ + container: '#testContainer', + rootEntity: 'entity1' + }); + + const options = (dispatcher as any).options; + expect(options).toBeDefined(); + expect(options.rootEntity).toBe('entity1'); + expect(options.showBackToEntities).toBe(false); + + // basePath должен быть установлен в "/" + expect((dispatcher as any).basePath).toBe('/'); + }); + + it('должен корректно нормализовать базовый путь', () => { + const dispatcher = new EasyDataViewDispatcher({ + container: '#testContainer', + basePath: 'easydata' + }); + + // Используя приватный метод напрямую через any + expect((dispatcher as any).normalizeBasePath('easydata')).toBe('/app/easydata'); + expect((dispatcher as any).normalizeBasePath('/easydata/')).toBe('/app/easydata'); + expect((dispatcher as any).normalizeBasePath('nonexistent')).toBe('/'); + }); + + it('должен корректно обрезать слеши в пути', () => { + const dispatcher = new EasyDataViewDispatcher({ + container: '#testContainer' + }); + + expect((dispatcher as any).trimSlashes('/path/')).toBe('path'); + expect((dispatcher as any).trimSlashes('path/')).toBe('path'); + expect((dispatcher as any).trimSlashes('/path')).toBe('path'); + expect((dispatcher as any).trimSlashes('path')).toBe('path'); + expect((dispatcher as any).trimSlashes('/')).toBe(''); + expect((dispatcher as any).trimSlashes('')).toBe(''); + }); + + it('должен корректно устанавливать контейнер по ID', () => { + const dispatcher = new EasyDataViewDispatcher({ + container: '#testContainer' + }); + + expect((dispatcher as any).container).toBe(mockContainer); + }); + + it('должен корректно устанавливать контейнер по классу', () => { + // Добавляем класс контейнеру + mockContainer.className = 'test-class'; + + const dispatcher = new EasyDataViewDispatcher({ + container: '.test-class' + }); + + expect((dispatcher as any).container).toBe(mockContainer); + }); + + it('должен корректно устанавливать контейнер как HTML-элемент', () => { + const dispatcher = new EasyDataViewDispatcher({ + container: mockContainer + }); + + expect((dispatcher as any).container).toBe(mockContainer); + }); + + it('должен выбрасывать ошибку при некорректном контейнере', () => { + expect(() => { + new EasyDataViewDispatcher({ + container: '#nonexistentContainer' + }); + }).toThrow(/Unrecognized `container` parameter/); + + expect(() => { + new EasyDataViewDispatcher({ + container: null + }); + }).toThrow('Container is undefined'); + }); + + it('должен корректно определять ID активного источника', () => { + const dispatcher = new EasyDataViewDispatcher({ + container: '#testContainer', + basePath: 'easydata' + }); + + expect((dispatcher as any).getActiveSourceId()).toBe('entity1'); + + // Меняем путь на корневой + window.location = { + ...window.location, + pathname: '/app/easydata', + href: 'http://example.com/app/easydata' + } as Location; + + expect((dispatcher as any).getActiveSourceId()).toBeNull(); + }); + + it('должен корректно определять ID активного источника с rootEntity', () => { + const dispatcher = new EasyDataViewDispatcher({ + container: '#testContainer', + rootEntity: 'rootEntity' + }); + + expect((dispatcher as any).getActiveSourceId()).toBe('rootEntity'); + }); + + it('должен запускаться и загружать метаданные', async () => { + // const dispatcher = new EasyDataViewDispatcher({ + // container: '#testContainer' + // }); + // + // const setActiveViewSpy = jest.spyOn(dispatcher as any, 'setActiveView'); + // + // await dispatcher.run(); + // + // expect(DataContext.prototype.loadMetaData).toHaveBeenCalled(); + // expect(setActiveViewSpy).toHaveBeenCalled(); + }); + + it('должен устанавливать активное представление Entity', async () => { + const dispatcher = new EasyDataViewDispatcher({ + container: '#testContainer' + }); + + await dispatcher.run(); + + // Так как путь включает ID сущности, должен быть создан EntityDataView + expect(DataContext.prototype.setActiveSource).toHaveBeenCalledWith('entity1'); + expect(window['EDView']).toBeDefined(); + }); + + it('должен устанавливать активное представление Root', async () => { + // Меняем путь на корневой + window.location = { + ...window.location, + pathname: '/app/easydata', + href: 'http://example.com/app/easydata' + } as Location; + + const dispatcher = new EasyDataViewDispatcher({ + container: '#testContainer' + }); + + await dispatcher.run(); + + // Так как путь не включает ID сущности, должен быть создан RootDataView + expect(DataContext.prototype.setActiveSource).not.toHaveBeenCalled(); + expect(window['EDView']).toBeDefined(); + }); + + it('должен подключать слушатели событий при запуске', async () => { + const dispatcher = new EasyDataViewDispatcher({ + container: '#testContainer' + }); + + await dispatcher.run(); + + // Проверяем, что были добавлены слушатели для событий + expect(window.addEventListener).toHaveBeenCalledWith(['ed_set_location', 'popstate']); + }); + + it('должен отключать слушатели событий при detach', async () => { + const dispatcher = new EasyDataViewDispatcher({ + container: '#testContainer' + }); + + await dispatcher.run(); + + dispatcher.detach(); + + // Проверяем, что были удалены слушатели событий + expect(window.removeEventListener).toHaveBeenCalledWith(['ed_set_location', 'popstate']); + }); + + it('должен очищать контейнер и данные при смене представления', async () => { + const dispatcher = new EasyDataViewDispatcher({ + container: '#testContainer' + }); + + // Добавляем содержимое в контейнер + mockContainer.innerHTML = '
Test content
'; + + // Мок для метода clear у таблицы данных + const clearMock = mock(); + (dispatcher as any).context.getData = mock().mockReturnValue({ + clear: clearMock + }); + + // Вызываем метод setActiveView напрямую + (dispatcher as any).setActiveView(); + + // Проверяем, что контейнер был очищен + expect(mockContainer.innerHTML).toBe(''); + + // Проверяем, что данные были очищены + expect(clearMock).toHaveBeenCalled(); + }); +}); diff --git a/easydata.js/packs/crud/tests/entity_data_view.test.ts b/easydata.js/packs/crud/tests/entity_data_view.test.ts new file mode 100644 index 00000000..c5579a46 --- /dev/null +++ b/easydata.js/packs/crud/tests/entity_data_view.test.ts @@ -0,0 +1,406 @@ +import { expect } from "@olton/latte" + +import { + DataColumnList, DataRow, DataType, EasyDataTable, + MetaData, MetaEntity, MetaEntityAttr, i18n +} from '@easydata/core'; + +import { + DefaultDialogService, DialogService, + EasyGrid, GridCellRenderer, GridColumn +} from '@easydata/ui'; + +import { DataContext } from '../src/main/data_context'; +import { EntityDataView } from '../src/views/entity_data_view'; +import { TextFilterWidget } from '../src/widgets/text_filter_widget'; +import * as utils from '../src/utils/utils'; + +describe('EntityDataView', () => { + // Моки для DOM и объектов + let mockSlot: HTMLElement; + let mockContext: DataContext; + let mockDialogService: DialogService; + let mockGrid: EasyGrid; + let mockEntity: MetaEntity; + let mockMetaData: MetaData; + let mockDataTable: EasyDataTable; + let mockFilterWidget: TextFilterWidget; + let view: EntityDataView; + + // Вспомогательная функция для создания мока атрибута + function createMockAttr(id: string, options: any = {}): MetaEntityAttr { + return { + id, + caption: options.caption || id, + dataType: options.dataType || DataType.String, + isPrimaryKey: options.isPrimaryKey || false, + showOnView: options.showOnView !== undefined ? options.showOnView : true + } as MetaEntityAttr; + } + + beforeEach(() => { + // Мок для HTMLElement + mockSlot = document.createElement('div'); + document.body.appendChild(mockSlot); + + // Мок для атрибутов сущности + const mockAttrs = [ + createMockAttr('Entity.id', { caption: 'ID', dataType: DataType.Int32, isPrimaryKey: true }), + createMockAttr('Entity.name', { caption: 'Name', dataType: DataType.String }), + createMockAttr('Entity.description', { caption: 'Description', dataType: DataType.String }), + createMockAttr('Entity.active', { caption: 'Active', dataType: DataType.Bool }) + ]; + + // Мок для сущности + mockEntity = { + id: 'Entity', + name: 'Entity', + caption: 'Entity', + captionPlural: 'Entities', + attributes: mockAttrs, + isEditable: true, + getPrimaryAttrs: mock().mockReturnValue([mockAttrs[0]]) + } as unknown as MetaEntity; + + // Мок для метаданных + mockMetaData = { + getAttributeById: (id: string) => mockAttrs.find(attr => attr.id === id) || null + } as unknown as MetaData; + + // Мок для колонок данных + const mockColumns = new DataColumnList(); + mockColumns.add({ id: 'Entity.id', label: 'ID', type: DataType.Int32 }); + mockColumns.add({ id: 'Entity.name', label: 'Name', type: DataType.String }); + mockColumns.add({ id: 'Entity.description', label: 'Description', type: DataType.String }); + mockColumns.add({ id: 'Entity.active', label: 'Active', type: DataType.Bool }); + + // Мок для таблицы данных + mockDataTable = { + columns: mockColumns, + getRow: mock().mockImplementation((rowIndex: number) => { + if (rowIndex === 0) { + return Promise.resolve({ + getValue: (id: string) => { + if (id === 'Entity.id') return 1; + if (id === 'Entity.name') return 'Test Name'; + if (id === 'Entity.description') return 'Test Description'; + if (id === 'Entity.active') return true; + return null; + } + } as DataRow); + } + return Promise.resolve(null); + }), + getCachedRows: mock().mockReturnValue([]) + } as unknown as EasyDataTable; + + // Мок для контекста данных + mockContext = { + getMetaData: mock().mockReturnValue(mockMetaData), + getActiveEntity: mock().mockReturnValue(mockEntity), + fetchDataset: mock().mockResolvedValue(mockDataTable), + createFilter: mock(), + createRecord: mock().mockResolvedValue({}), + updateRecord: mock().mockResolvedValue({}), + deleteRecord: mock().mockResolvedValue({}) + } as unknown as DataContext; + + // Мок для диалогового сервиса + mockDialogService = { + open: mock().mockReturnValue({ + submit: mock() + }), + openConfirm: mock().mockResolvedValue(true) + } as unknown as DialogService; + // jest.spyOn(DefaultDialogService.prototype, 'constructor').mockImplementation(() => {}); + // jest.spyOn(DefaultDialogService.prototype, 'open').mockImplementation( + // (options) => mockDialogService.open(options) + // ); + // jest.spyOn(DefaultDialogService.prototype, 'openConfirm').mockImplementation( + // (title, message) => mockDialogService.openConfirm(title, message) + // ); + + // Мок для EasyGrid + mockGrid = { + refresh: mock(), + getData: mock().mockReturnValue(mockDataTable) + } as unknown as EasyGrid; + // jest.spyOn(EasyGrid.prototype, 'constructor').mockImplementation(() => {}); + Object.defineProperty(EasyGrid, 'prototype', { + value: mockGrid, + writable: true + }); + + // Мок для TextFilterWidget + mockFilterWidget = { + applyFilter: mock().mockReturnValue(true) + } as unknown as TextFilterWidget; + // jest.spyOn(TextFilterWidget.prototype, 'constructor').mockImplementation(() => {}); + // jest.spyOn(TextFilterWidget.prototype, 'applyFilter').mockImplementation( + // (refresh) => mockFilterWidget.applyFilter(refresh) + // ); + + // Мок для i18n + // jest.spyOn(i18n, 'getText').mockImplementation((key: string) => { + // if (key === 'BackToEntities') return 'Back to Entities'; + // if (key === 'AddRecordBtnTitle') return 'Add Record'; + // if (key === 'EditBtn') return 'Edit'; + // if (key === 'DeleteBtn') return 'Delete'; + // if (key === 'AddDlgCaption') return 'Add {entity}'; + // if (key === 'EditDlgCaption') return 'Edit {entity}'; + // if (key === 'DeleteDlgCaption') return 'Delete {entity}'; + // if (key === 'DeleteDlgMessage') return 'Delete record with ID: {recordId}'; + // return key; + // }); + // + // // Мок для utils.setLocation + // jest.spyOn(utils, 'setLocation').mockImplementation(() => {}); + }); + + afterEach(() => { + // Удаляем добавленные элементы + if (mockSlot.parentNode) { + mockSlot.parentNode.removeChild(mockSlot); + } + + // Сбрасываем моки + // jest.restoreAllMocks(); + }); + + it('должен создаваться с правильными настройками по умолчанию', () => { + view = new EntityDataView(mockSlot, mockContext, '/basePath', {}); + + // Проверяем, что контекст установлен + expect((view as any).context).toBe(mockContext); + + // Проверяем, что базовый путь установлен + expect((view as any).basePath).toBe('/basePath'); + + // Проверяем опции по умолчанию + const options = (view as any).options; + expect(options).toBeDefined(); + expect(options.showFilterBox).toBe(true); + expect(options.showBackToEntities).toBe(true); + }); + + it('должен рендерить заголовок и кнопку возврата', () => { + view = new EntityDataView(mockSlot, mockContext, '/basePath', {}); + + // Проверяем, что заголовок содержит название сущности + expect(mockSlot.innerHTML).toContain('

Entities

'); + + // Проверяем наличие кнопки возврата + const backLink = mockSlot.querySelector('a'); + expect(backLink).toBeDefined(); + expect(backLink.textContent).toBe('← Back to Entities'); + + // Проверяем, что обработчик клика установлен + const clickEvent = new MouseEvent('click'); + const preventDefaultSpy = jest.spyOn(clickEvent, 'preventDefault'); + backLink.dispatchEvent(clickEvent); + + expect(preventDefaultSpy).toHaveBeenCalled(); + expect(utils.setLocation).toHaveBeenCalledWith('/basePath'); + }); + + it('не должен рендерить кнопку возврата если showBackToEntities=false', () => { + view = new EntityDataView(mockSlot, mockContext, '/basePath', { showBackToEntities: false }); + + // Проверяем отсутствие кнопки возврата + const backLink = mockSlot.querySelector('a'); + expect(backLink).toBeNull(); + }); + + it('должен вызывать fetchDataset и создавать грид', () => { + view = new EntityDataView(mockSlot, mockContext, '/basePath', {}); + + // Проверяем, что fetchDataset был вызван + expect(mockContext.fetchDataset).toHaveBeenCalled(); + }); + + it('должен создавать фильтр если showFilterBox=true', () => { + // Подменяем setTimeout, чтобы дождаться асинхронных операций + jest.useFakeTimers(); + + view = new EntityDataView(mockSlot, mockContext, '/basePath', { showFilterBox: true }); + + jest.runAllTimers(); + + // Проверяем создание фильтра + expect(mockContext.createFilter).toHaveBeenCalled(); + }); + + it('не должен создавать фильтр если showFilterBox=false', () => { + jest.useFakeTimers(); + + view = new EntityDataView(mockSlot, mockContext, '/basePath', { showFilterBox: false }); + + jest.runAllTimers(); + + // Проверяем что фильтр не создается + expect(mockContext.createFilter).not.toHaveBeenCalled(); + }); + + it('должен правильно обрабатывать клик по кнопке добавления', () => { + view = new EntityDataView(mockSlot, mockContext, '/basePath', {}); + + // Напрямую вызываем обработчик клика кнопки добавления + (view as any).addClickHandler(); + + // Проверяем вызов диалога + expect(mockDialogService.open).toHaveBeenCalled(); + const openArgs = (mockDialogService.open as jest.Mock).mock.calls[0][0]; + expect(openArgs).toBeObject(); + expect(openArgs.title).toBe('Add Entity'); + }); + + it('должен правильно обрабатывать клик по кнопке редактирования', () => { + view = new EntityDataView(mockSlot, mockContext, '/basePath', {}); + + // Напрямую вызываем обработчик клика кнопки редактирования + (view as any).editClickHandler(new MouseEvent('click'), 0); + + // Проверяем вызов getRow + expect(mockDataTable.getRow).toHaveBeenCalledWith(0); + + // Проверяем что диалог редактирования открывается + return mockDataTable.getRow(0).then(() => { + expect(mockDialogService.open).toHaveBeenCalled(); + const openArgs = (mockDialogService.open as jest.Mock).mock.calls[0][0]; + expect(openArgs).toBeObject(); + expect(openArgs.title).toBe('Edit Entity'); + }); + }); + + it('должен правильно обрабатывать клик по кнопке удаления', () => { + view = new EntityDataView(mockSlot, mockContext, '/basePath', {}); + + // Напрямую вызываем обработчик клика кнопки удаления + (view as any).deleteClickHandler(new MouseEvent('click'), 0); + + // Проверяем вызов getRow + expect(mockDataTable.getRow).toHaveBeenCalledWith(0); + + // Проверяем открытие диалога подтверждения + return mockDataTable.getRow(0).then(() => { + expect(mockDialogService.openConfirm).toHaveBeenCalled(); + + // Проверяем вызов deleteRecord после подтверждения + return mockDialogService.openConfirm("", "").then(() => { + expect(mockContext.deleteRecord).toHaveBeenCalledWith({ id: 1 }); + }); + }); + }); + + it('должен обновлять данные после операций CRUD', () => { + view = new EntityDataView(mockSlot, mockContext, '/basePath', {}); + + // Подменяем filterWidget + (view as any).filterWidget = mockFilterWidget; + + // Напрямую вызываем refreshData + return (view as any).refreshData().then(() => { + // Проверяем вызов fetchDataset + expect(mockContext.fetchDataset).toHaveBeenCalled(); + + // Проверяем применение фильтра + expect(mockFilterWidget.applyFilter).toHaveBeenCalledWith(false); + }); + }); + + it('должен обновлять грид, если фильтр не применен', () => { + view = new EntityDataView(mockSlot, mockContext, '/basePath', {}); + + // Подменяем filterWidget с флагом что фильтр не был применен + (view as any).filterWidget = { + applyFilter: mock().mockReturnValue(false) + }; + + // Напрямую вызываем refreshData + return (view as any).refreshData().then(() => { + // Проверяем вызов refresh у грида + expect(mockGrid.refresh).toHaveBeenCalled(); + }); + }); + + it('должен корректно обрабатывать ошибки', () => { + view = new EntityDataView(mockSlot, mockContext, '/basePath', {}); + + // Создаем ошибку + const error = new Error('Test error'); + + // Напрямую вызываем обработчик ошибок + (view as any).processError(error); + + // Проверяем открытие диалога с ошибкой + expect(mockDialogService.open).toHaveBeenCalled(); + const openArgs = (mockDialogService.open as jest.Mock).mock.calls[0][0]; + expect(openArgs).toBeObject(); + expect(openArgs.title).toBe('Ooops, something went wrong'); + expect(openArgs.body).toBe('Test error'); + }); + + it('должен корректно управлять рендерером ячеек', () => { + view = new EntityDataView(mockSlot, mockContext, '/basePath', {}); + + // Создаем колонку с номером строки + const column: GridColumn = { + isRowNum: true + } as GridColumn; + + // Создаем мок для defaultRenderer + const defaultRenderer = mock() as GridCellRenderer; + + // Вызываем метод manageCellRenderer + const renderer = (view as any).manageCellRenderer(column, defaultRenderer); + + // Проверяем, что возвращается функция рендерера + expect(typeof renderer).toBe('function'); + + // Проверяем, что ширина колонки установлена + expect(column.width).toBe(110); + + // Подготавливаем элементы для проверки рендерера + const cell = document.createElement('div'); + const rowEl = document.createElement('tr'); + rowEl.setAttribute('data-row-idx', '0'); + + // Вызываем рендерер + renderer('value', column, cell, rowEl); + + // Проверяем, что в ячейке появились кнопки Edit и Delete + expect(cell.innerHTML).toContain('Edit'); + expect(cell.innerHTML).toContain('Delete'); + }); + + it('должен синхронизировать видимость колонок грида с метаданными', () => { + view = new EntityDataView(mockSlot, mockContext, '/basePath', {}); + + // Создаем колонку с dataColumn + const column: GridColumn = { + dataColumn: { + id: 'Entity.name' + } + } as GridColumn; + + // Устанавливаем showOnView в false для атрибута + const attr = mockMetaData.getAttributeById('Entity.name'); + attr.showOnView = false; + + // Вызываем метод syncGridColumnHandler + (view as any).syncGridColumnHandler(column); + + // Проверяем, что видимость колонки установлена в соответствии с showOnView атрибута + expect(column.isVisible).toBe(false); + }); + + it('должен выбрасывать ошибку если активная сущность не найдена', () => { + // Подменяем getActiveEntity, чтобы вернуть null + (mockContext.getActiveEntity as jest.Mock).mockReturnValue(null); + + // Проверяем, что конструктор выбрасывает ошибку + expect(() => { + new EntityDataView(mockSlot, mockContext, '/basePath', {}); + }).toThrow("Can't find active entity for " + window.location.pathname); + }); +}); diff --git a/easydata.js/packs/crud/tests/entity_edit_form.test.ts b/easydata.js/packs/crud/tests/entity_edit_form.test.ts new file mode 100644 index 00000000..a3beb4c5 --- /dev/null +++ b/easydata.js/packs/crud/tests/entity_edit_form.test.ts @@ -0,0 +1,351 @@ +import { + DataType, + MetaData, + MetaEntity, + MetaEntityAttr +} from '@easydata/core'; + +import { EntityEditForm } from '../src/form/entity_edit_form'; +import { DataContext } from '../src/main/data_context'; +import { ValidationResult, Validator } from '../src/validators/validator'; + +describe('EntityEditForm', () => { + let form: EntityEditForm; + let context: DataContext; + let mockMetaData: MetaData; + let mockEntity: MetaEntity; + let mockFormHtml: HTMLElement; + let mockErrorsDiv: HTMLElement; + + beforeEach(() => { + // Создаем мок для метаданных с атрибутами + mockMetaData = { + getAttributeById: (id: string): MetaEntityAttr => { + if (id === 'Person.name') { + return { + id: 'Person.name', + caption: 'Name', + dataType: DataType.String + } as MetaEntityAttr; + } + else if (id === 'Person.age') { + return { + id: 'Person.age', + caption: 'Age', + dataType: DataType.Int32 + } as MetaEntityAttr; + } + else if (id === 'Person.birthDate') { + return { + id: 'Person.birthDate', + caption: 'Birth Date', + dataType: DataType.Date + } as MetaEntityAttr; + } + else if (id === 'Person.isActive') { + return { + id: 'Person.isActive', + caption: 'Is Active', + dataType: DataType.Bool + } as MetaEntityAttr; + } + else if (id === 'Person.photo') { + return { + id: 'Person.photo', + caption: 'Photo', + dataType: DataType.Blob + } as MetaEntityAttr; + } + else if (id === 'Person.description') { + return { + id: 'Person.description', + caption: 'Description', + dataType: DataType.String + } as MetaEntityAttr; + } + return null; + } + } as MetaData; + + // Создаем мок для контекста данных + context = { + getMetaData: () => mockMetaData + } as DataContext; + + // Создаем HTML для формы + mockFormHtml = document.createElement('div'); + mockErrorsDiv = document.createElement('div'); + mockErrorsDiv.className = 'errors-block'; + mockFormHtml.appendChild(mockErrorsDiv); + + // Создаем форму + form = new EntityEditForm(context); + + // Устанавливаем HTML через приватное свойство + (form as any).setHtmlInt(mockFormHtml); + }); + + it('должен создаваться с контекстом данных', () => { + expect(form).toBeDefined(); + expect((form as any).context).toBe(context); + }); + + it('должен возвращать HTML через getHtml', () => { + const html = form.getHtml(); + expect(html).toBe(mockFormHtml); + }); + + it('должен считать форму валидной, если нет полей', () => { + const isValid = form.validate(); + expect(isValid).toBe(true); + expect(mockErrorsDiv.innerHTML).toBe(''); + }); + + it('должен валидировать поля формы', () => { + // Создаем тестовые поля + const nameInput = document.createElement('input'); + nameInput.name = 'Person.name'; + nameInput.value = 'John'; + mockFormHtml.appendChild(nameInput); + + const ageInput = document.createElement('input'); + ageInput.name = 'Person.age'; + ageInput.value = '25'; + mockFormHtml.appendChild(ageInput); + + // Проверяем валидацию + const isValid = form.validate(); + expect(isValid).toBe(true); + expect(nameInput.classList.contains('is-valid')).toBe(true); + expect(ageInput.classList.contains('is-valid')).toBe(true); + }); + + it('должен отображать ошибки валидации', () => { + // Создаем пользовательский валидатор, который считает поле "age" невалидным + const mockValidator: Validator = { + validate: (attr: MetaEntityAttr, value: any): ValidationResult => { + if (attr.id === 'Person.age' && parseInt(value) < 18) { + return { + successed: false, + messages: ['Age must be at least 18'] + }; + } + return { + successed: true, + messages: [] + }; + } + }; + + // Добавляем валидатор к форме + form.useValidator(mockValidator); + + // Создаем тестовые поля + const nameInput = document.createElement('input'); + nameInput.name = 'Person.name'; + nameInput.value = 'John'; + mockFormHtml.appendChild(nameInput); + + const ageInput = document.createElement('input'); + ageInput.name = 'Person.age'; + ageInput.value = '16'; // Невалидное значение + mockFormHtml.appendChild(ageInput); + + // Проверяем валидацию + const isValid = form.validate(); + expect(isValid).toBe(false); + expect(nameInput.classList.contains('is-valid')).toBe(true); + expect(ageInput.classList.contains('is-invalid')).toBe(true); + + // Проверяем, что ошибка отображается + const errorsList = mockErrorsDiv.querySelector('ul'); + expect(errorsList).toBeDefined(); + expect(errorsList.innerHTML).toContain('Age: Age must be at least 18'); + }); + + it('должен получать данные из формы через getData', async () => { + // Создаем тестовые поля + const nameInput = document.createElement('input'); + nameInput.name = 'Person.name'; + nameInput.value = 'John'; + mockFormHtml.appendChild(nameInput); + + const ageInput = document.createElement('input'); + ageInput.name = 'Person.age'; + ageInput.value = '25'; + mockFormHtml.appendChild(ageInput); + + const isActiveInput = document.createElement('input'); + isActiveInput.type = 'checkbox'; + isActiveInput.name = 'Person.isActive'; + isActiveInput.checked = true; + mockFormHtml.appendChild(isActiveInput); + + // Получаем данные формы + const data = await form.getData(); + + // Проверяем, что получен объект с правильными данными + expect(data).toBeObject(); + expect(data.name).toBe('John'); + expect(data.age).toBe(25); // Преобразовано в число + expect(data.isActive).toBe(true); + }); + + it('должен обрабатывать различные типы данных в getData', async () => { + // Создаем поля разных типов + const nameInput = document.createElement('input'); + nameInput.name = 'Person.name'; + nameInput.value = 'John'; + mockFormHtml.appendChild(nameInput); + + const ageInput = document.createElement('input'); + ageInput.name = 'Person.age'; + ageInput.value = '25'; + mockFormHtml.appendChild(ageInput); + + const birthDateInput = document.createElement('input'); + birthDateInput.name = 'Person.birthDate'; + birthDateInput.value = '1995-05-15'; + mockFormHtml.appendChild(birthDateInput); + + const descriptionTextarea = document.createElement('textarea'); + descriptionTextarea.name = 'Person.description'; + descriptionTextarea.value = 'Some description'; + mockFormHtml.appendChild(descriptionTextarea); + + // Получаем данные формы + const data = await form.getData(); + + // Проверяем преобразование типов + expect(data).toBeObject(); + expect(data.name).toBe('John'); + expect(data.age).toBe(25); + + // Для даты проверяем, что значение не null + expect(data.birthDate).not.toBeNull(); + + expect(data.description).toBe('Some description'); + }); + + it('должен использовать несколько валидаторов', () => { + // Создаем два пользовательских валидатора + const nameValidator: Validator = { + validate: (attr: MetaEntityAttr, value: any): ValidationResult => { + if (attr.id === 'Person.name' && (!value || value.length < 3)) { + return { + successed: false, + messages: ['Name must be at least 3 characters'] + }; + } + return { successed: true, messages: [] }; + } + }; + + const ageValidator: Validator = { + validate: (attr: MetaEntityAttr, value: any): ValidationResult => { + if (attr.id === 'Person.age' && parseInt(value) < 18) { + return { + successed: false, + messages: ['Age must be at least 18'] + }; + } + return { successed: true, messages: [] }; + } + }; + + // Добавляем валидаторы к форме + form.useValidators([nameValidator, ageValidator]); + + // Создаем тестовые поля с невалидными значениями + const nameInput = document.createElement('input'); + nameInput.name = 'Person.name'; + nameInput.value = 'Jo'; // Слишком короткое имя + mockFormHtml.appendChild(nameInput); + + const ageInput = document.createElement('input'); + ageInput.name = 'Person.age'; + ageInput.value = '16'; // Слишком малый возраст + mockFormHtml.appendChild(ageInput); + + // Проверяем валидацию + const isValid = form.validate(); + expect(isValid).toBe(false); + + // Проверяем, что ошибки от обоих валидаторов отображаются + const errorsList = mockErrorsDiv.querySelector('ul'); + expect(errorsList.innerHTML).toContain('Name: Name must be at least 3 characters'); + expect(errorsList.innerHTML).toContain('Age: Age must be at least 18'); + }); + + it('должен очищать ошибки при повторной валидации', () => { + // Создаем пользовательский валидатор + const mockValidator: Validator = { + validate: (attr: MetaEntityAttr, value: any): ValidationResult => { + if (attr.id === 'Person.age' && parseInt(value) < 18) { + return { + successed: false, + messages: ['Age must be at least 18'] + }; + } + return { successed: true, messages: [] }; + } + }; + + form.useValidator(mockValidator); + + // Создаем поле возраста с невалидным значением + const ageInput = document.createElement('input'); + ageInput.name = 'Person.age'; + ageInput.value = '16'; + mockFormHtml.appendChild(ageInput); + + // Проверяем первую валидацию - должны быть ошибки + let isValid = form.validate(); + expect(isValid).toBe(false); + expect(ageInput.classList.contains('is-invalid')).toBe(true); + expect(mockErrorsDiv.innerHTML).not.toBe(''); + + // Меняем значение и валидируем снова + ageInput.value = '18'; + isValid = form.validate(); + + // Теперь должно быть валидно и ошибки должны исчезнуть + expect(isValid).toBe(true); + expect(ageInput.classList.contains('is-valid')).toBe(true); + expect(ageInput.classList.contains('is-invalid')).toBe(false); + expect(mockErrorsDiv.innerHTML).toBe(''); + }); + + it('должен игнорировать чекбоксы при проверке на пустое значение', () => { + // Создаем валидатор, который проверяет, что все поля заполнены + const requiredValidator: Validator = { + validate: (attr: MetaEntityAttr, value: any): ValidationResult => { + if (!value) { + return { + successed: false, + messages: ['Field is required'] + }; + } + return { successed: true, messages: [] }; + } + }; + + form.useValidator(requiredValidator); + + // Создаем чекбокс и текстовое поле + const isActiveInput = document.createElement('input'); + isActiveInput.type = 'checkbox'; + isActiveInput.name = 'Person.isActive'; + isActiveInput.checked = false; // Не выбран + mockFormHtml.appendChild(isActiveInput); + + const nameInput = document.createElement('input'); + nameInput.name = 'Person.name'; + nameInput.value = 'John'; + mockFormHtml.appendChild(nameInput); + + // Валидация должна пройти успешно, т.к. чекбоксы игнорируются + const isValid = form.validate(); + expect(isValid).toBe(true); + }); +}); diff --git a/easydata.js/packs/crud/tests/entity_form_builder.test.ts b/easydata.js/packs/crud/tests/entity_form_builder.test.ts new file mode 100644 index 00000000..9a124e77 --- /dev/null +++ b/easydata.js/packs/crud/tests/entity_form_builder.test.ts @@ -0,0 +1,376 @@ +import { + DataRow, DataType, EasyDataTable, + EntityAttrKind, MetaData, MetaEntity, MetaEntityAttr, + ValueEditor, EditorTag +} from '@easydata/core'; + +import { DataContext } from '../src/main/data_context'; +import { EntityEditFormBuilder, FormBuildParams } from '../src/form/entity_form_builder'; +import { EntityEditForm } from '../src/form/entity_edit_form'; + +describe('EntityEditFormBuilder', () => { + let mockMetaData: MetaData; + let mockEntity: MetaEntity; + let mockDataContext: DataContext; + let builder: EntityEditFormBuilder; + let dataRow: DataRow; + + // Вспомогательная функция для создания атрибута + const createAttr = (id: string, caption: string, dataType: DataType, options: any = {}): MetaEntityAttr => { + const attr = { + id, + caption, + dataType, + description: options.description || '', + isEditable: options.isEditable !== undefined ? options.isEditable : true, + isPrimaryKey: options.isPrimaryKey || false, + isNullable: options.isNullable !== undefined ? options.isNullable : true, + showOnCreate: options.showOnCreate !== undefined ? options.showOnCreate : true, + showOnEdit: options.showOnEdit !== undefined ? options.showOnEdit : true, + showOnView: options.showOnView !== undefined ? options.showOnView : true, + defaultEditor: options.defaultEditor || null, + defaultValue: options.defaultValue || null, + kind: options.kind || EntityAttrKind.Data, + lookupEntity: options.lookupEntity || null, + lookupAttr: options.lookupAttr || null, + dataAttr: options.dataAttr || null, + lookupDataAttr: options.lookupDataAttr || null + } as MetaEntityAttr; + + return attr; + }; + + beforeEach(() => { + // Создаем атрибуты для нашей сущности + const attributes = [ + createAttr('Person.id', 'ID', DataType.Int32, { + isPrimaryKey: true, + isEditable: false + }), + createAttr('Person.name', 'Name', DataType.String, { + isNullable: false, + description: 'Person full name' + }), + createAttr('Person.birthDate', 'Birth Date', DataType.Date), + createAttr('Person.registered', 'Registered', DataType.DateTime), + createAttr('Person.rating', 'Rating', DataType.Int32), + createAttr('Person.isActive', 'Is Active', DataType.Bool), + createAttr('Person.notes', 'Notes', DataType.String, { + defaultEditor: Object.assign(new ValueEditor(), { + tag: EditorTag.Edit, + multiline: true + }) + }), + createAttr('Person.photo', 'Photo', DataType.Blob, { + defaultEditor: Object.assign(new ValueEditor(), { + tag: EditorTag.File, + accept: 'image/*' + }) + }), + createAttr('Person.status', 'Status', DataType.String, { + defaultEditor: Object.assign(new ValueEditor(), { + tag: EditorTag.List, + values: [ + { id: 'Active', text: 'Active' }, + { id: 'Inactive', text: 'Inactive' } + ] + }) + }), + createAttr('Person.departmentId', 'Department ID', DataType.Int32, { + kind: EntityAttrKind.Lookup, + lookupEntity: 'Department', + lookupAttr: 'Department.id', + dataAttr: 'Person.departmentId', + lookupDataAttr: 'Department.id' + }) + ]; + + // Создаем сущность с атрибутами + mockEntity = { + id: 'Person', + name: 'Person', + caption: 'Person', + attributes + } as MetaEntity; + + // Создаем Department сущность для lookup + const departmentEntity = { + id: 'Department', + name: 'Department', + caption: 'Department', + attributes: [ + createAttr('Department.id', 'ID', DataType.Int32, { isPrimaryKey: true }), + createAttr('Department.name', 'Name', DataType.String) + ], + getFirstPrimaryAttr: () => departmentEntity.attributes[0] + } as MetaEntity; + + // Создаем метаданные с нашими сущностями + mockMetaData = { + getRootEntity: () => ({ + subEntities: [mockEntity, departmentEntity] + }), + getAttributeById: (id: string) => { + // Поиск в атрибутах Person + const personAttr = mockEntity.attributes.find(attr => attr.id === id); + if (personAttr) return personAttr; + + // Поиск в атрибутах Department + const deptAttr = departmentEntity.attributes.find(attr => attr.id === id); + if (deptAttr) return deptAttr; + + return null; + } + } as MetaData; + + // Создаем объект для представления данных строки + const rowData = { + 'Person.id': 1, + 'Person.name': 'John Doe', + 'Person.birthDate': new Date(1990, 5, 15), + 'Person.registered': new Date(2020, 1, 1, 10, 30, 0), + 'Person.rating': 5, + 'Person.isActive': true, + 'Person.notes': 'Some notes', + 'Person.departmentId': 2 + }; + + // Создаем макет для DataRow + dataRow = { + getValue: (id: string) => rowData[id] + } as DataRow; + + // Создаем мок для контекста данных + mockDataContext = { + getMetaData: () => mockMetaData, + getActiveEntity: () => mockEntity, + createFilter: mock(), + getDataLoader: () => ({ + loadChunk: mock().mockResolvedValue({ + table: new EasyDataTable(), + total: 0 + }) + }), + fetchRecord: mock().mockResolvedValue({ + entity: { + name: 'IT Department' + } + }) + } as unknown as DataContext; + + // Создаем экземпляр builder'а + builder = new EntityEditFormBuilder(mockDataContext); + }); + + it('должен создаваться с пустыми параметрами и сбрасываться на значения по умолчанию', () => { + expect(builder).toBeDefined(); + + // Проверяем, что свойство context установлено + expect((builder as any).context).toBe(mockDataContext); + + // Проверяем метод reset + const formBeforeReset = (builder as any).form; + builder.reset(); + const formAfterReset = (builder as any).form; + + expect(formBeforeReset).not.toBe(formAfterReset); + expect(formAfterReset).toBeInstanceOf(EntityEditForm); + }); + + it('должен создавать форму с правильными полями', () => { + const form = builder.build(); + + // Проверяем что форма создана + expect(form).toBeInstanceOf(EntityEditForm); + + // Получаем HTML формы + const formHtml = form.getHtml(); + expect(formHtml).toBeDefined(); + + // Проверяем наличие блока для ошибок + const errorsBlock = formHtml.querySelector('.errors-block'); + expect(errorsBlock).toBeDefined(); + + // Проверяем наличие полей формы + const inputs = formHtml.querySelectorAll('input'); + expect(inputs.length).toBeGreaterThan(0); + + // Проверяем что каждый атрибут создал соответствующее поле + for (const attr of mockEntity.attributes) { + // Пропустить атрибуты, которые не должны отображаться в форме создания + if (!attr.showOnCreate) continue; + + // Найти поле по имени + const field = formHtml.querySelector(`[name="${attr.id}"]`); + expect(field).toBeDefined(); + + // Проверить label для поля + const label = formHtml.querySelector(`label[for="${attr.id}"]`); + expect(label).toBeDefined(); + expect(label.textContent).toContain(attr.caption); + + // Проверить обязательные поля + if (!attr.isNullable) { + expect(label.innerHTML).toContain('*'); + } + } + }); + + it('должен устанавливать значения из dataRow при создании формы', () => { + const params: FormBuildParams = { + values: dataRow + }; + + const builder = new EntityEditFormBuilder(mockDataContext, params); + const form = builder.build(); + const formHtml = form.getHtml(); + + // Проверяем заполнение полей значениями из dataRow + const nameInput = formHtml.querySelector('[name="Person.name"]') as HTMLInputElement; + expect(nameInput.value).toBe('John Doe'); + + const isActiveInput = formHtml.querySelector('[name="Person.isActive"]') as HTMLInputElement; + expect(isActiveInput.checked).toBe(true); + }); + + it('должен создавать форму редактирования с правильными параметрами', () => { + const params: FormBuildParams = { + values: dataRow, + isEditForm: true + }; + + const builder = new EntityEditFormBuilder(mockDataContext, params); + const form = builder.build(); + const formHtml = form.getHtml(); + + // Проверяем, что первичные ключи имеют атрибут readonly + const idInput = formHtml.querySelector('[name="Person.id"]') as HTMLInputElement; + expect(idInput).toBeDefined(); + expect(idInput.getAttribute('readonly')).toBeDefined(); + + // Проверяем, что неизменяемые атрибуты имеют readonly + const attrNonEditable = createAttr('Person.createdAt', 'Created At', DataType.DateTime, { + isEditable: false, + showOnEdit: true + }); + mockEntity.attributes.push(attrNonEditable); + + const newBuilder = new EntityEditFormBuilder(mockDataContext, params); + const newForm = newBuilder.build(); + const newFormHtml = newForm.getHtml(); + + const createdAtInput = newFormHtml.querySelector('[name="Person.createdAt"]') as HTMLInputElement; + expect(createdAtInput).toBeDefined(); + expect(createdAtInput.getAttribute('readonly')).toBeDefined(); + }); + + it('должен создавать текстовые поля правильного типа', () => { + const form = builder.build(); + const formHtml = form.getHtml(); + + // Проверяем текстовые поля + const nameInput = formHtml.querySelector('[name="Person.name"]') as HTMLInputElement; + expect(nameInput.type).toBe('text'); + + // Проверяем поля checkbox + const isActiveInput = formHtml.querySelector('[name="Person.isActive"]') as HTMLInputElement; + expect(isActiveInput.type).toBe('checkbox'); + + // Проверяем поля file + const photoInput = formHtml.querySelector('[name="Person.photo"]') as HTMLInputElement; + expect(photoInput.type).toBe('file'); + expect(photoInput.getAttribute('accept')).toBe('image/*'); + }); + + it('должен создавать текстовую область для многострочных редакторов', () => { + const form = builder.build(); + const formHtml = form.getHtml(); + + // Проверяем textarea для многострочных полей + const notesTextarea = formHtml.querySelector('[name="Person.notes"]') as HTMLTextAreaElement; + expect(notesTextarea).toBeDefined(); + expect(notesTextarea.tagName.toLowerCase()).toBe('textarea'); + }); + + it('должен создавать select для списков', () => { + const form = builder.build(); + const formHtml = form.getHtml(); + + // Проверяем select для списков + const statusSelect = formHtml.querySelector('[name="Person.status"]') as HTMLSelectElement; + expect(statusSelect).toBeDefined(); + expect(statusSelect.tagName.toLowerCase()).toBe('select'); + + // Проверяем опции + const options = statusSelect.querySelectorAll('option'); + expect(options.length).toBe(2); + expect(options[0].value).toBe('Active'); + expect(options[1].value).toBe('Inactive'); + }); + + it('должен создавать специальные поля для дат и времени', () => { + const form = builder.build(); + const formHtml = form.getHtml(); + + // Проверяем поля для дат + const birthDateField = formHtml.querySelector('[name="Person.birthDate"]').closest('.kfrm-fields, .kfrm-fields-ie'); + expect(birthDateField).toBeDefined(); + + // Проверяем наличие кнопки с календарем + const calendarButton = birthDateField.querySelector('button'); + expect(calendarButton).toBeDefined(); + + // Проверяем наличие иконки календаря + const calendarIcon = calendarButton.querySelector('i.ed-calendar-icon'); + expect(calendarIcon).toBeDefined(); + }); + + it('должен создавать поля для lookup атрибутов', () => { + const form = builder.build(); + const formHtml = form.getHtml(); + + // Проверяем наличие поля для lookup + const departmentField = formHtml.querySelector('[name="Person.departmentId"]').closest('.kfrm-fields, .kfrm-fields-ie'); + expect(departmentField).toBeDefined(); + + // Проверяем наличие кнопки для открытия lookup диалога + const lookupButton = departmentField.querySelector('button'); + expect(lookupButton).toBeDefined(); + expect(lookupButton.textContent).toBe('...'); + }); + + it('должен добавлять информацию о подсказках для полей с описанием', () => { + const form = builder.build(); + const formHtml = form.getHtml(); + + // Проверяем наличие подсказки для поля с описанием + const nameLabel = formHtml.querySelector(`label[for="Person.name"]`); + expect(nameLabel).toBeDefined(); + + const helpIcon = nameLabel.querySelector('.question-mark'); + expect(helpIcon).toBeDefined(); + expect(helpIcon.getAttribute('title')).toBe('Person full name'); + }); + + it('должен устанавливать обработчик submit и вызывать его при нажатии Enter', () => { + // Создаем мок-функцию для onSubmit + const submitCallback = mock(); + + // Устанавливаем callback через onSubmit + builder.onSubmit(submitCallback); + + // Строим форму + const form = builder.build(); + const formHtml = form.getHtml(); + + // Получаем поле ввода + const nameInput = formHtml.querySelector('[name="Person.name"]') as HTMLInputElement; + + // Эмулируем нажатие клавиши Enter + const enterKeyEvent = new KeyboardEvent('keypress', { keyCode: 13 }); + nameInput.dispatchEvent(enterKeyEvent); + + // Проверяем, что callback был вызван + expect(submitCallback).toHaveBeenCalled(); + }); +}); diff --git a/easydata.js/packs/crud/tests/prompt.txt b/easydata.js/packs/crud/tests/prompt.txt new file mode 100644 index 00000000..33851273 --- /dev/null +++ b/easydata.js/packs/crud/tests/prompt.txt @@ -0,0 +1 @@ +напиши тести використовуючи тестовий фреймворк Latte, describe не може бути вкладеним, внутрі describe використовуй it, для перевірки об'єктів використовуй матчер toBeObject, для перевірки масивів - toBeArray, toBeArrayEqual, toBeEmpty, інши відповідно до документації по Latte, jest ми не використовуємо, для мокінга в Latte є функція mock, файл з тестами поклади в папку /easydata.js/packs/crud/tests, use context7 \ No newline at end of file diff --git a/easydata.js/packs/crud/tests/root_data_view.test.ts b/easydata.js/packs/crud/tests/root_data_view.test.ts new file mode 100644 index 00000000..5a59e3a6 --- /dev/null +++ b/easydata.js/packs/crud/tests/root_data_view.test.ts @@ -0,0 +1,222 @@ +import { MetaData, MetaEntity, i18n } from '@easydata/core'; +import { DataContext } from '../src/main/data_context'; +import { RootDataView } from '../src/views/root_data_view'; +import * as utils from '../src/utils/utils'; + +describe('RootDataView', () => { + // Моки для DOM и объектов + let mockSlot: HTMLElement; + let mockContext: DataContext; + let mockMetaData: MetaData; + let mockRootEntity: MetaEntity; + let view: RootDataView; + + // Тестовые сущности + const mockEntities = [ + { + id: 'entity1', + name: 'Entity1', + caption: 'Customer', + captionPlural: 'Customers', + description: 'Customer entity description' + }, + { + id: 'entity2', + name: 'Entity2', + caption: 'Product', + captionPlural: 'Products', + description: null + }, + { + id: 'entity3', + name: 'Entity3', + caption: 'Order', + captionPlural: 'Orders', + description: 'Order entity description' + } + ]; + + beforeEach(() => { + // Создаем DOM-элемент для слота + mockSlot = document.createElement('div'); + document.body.appendChild(mockSlot); + + // Мок для корневой сущности + mockRootEntity = { + subEntities: mockEntities + } as unknown as MetaEntity; + + // Мок для метаданных + mockMetaData = { + getRootEntity: mock().mockReturnValue(mockRootEntity), + isEmpty: mock().mockReturnValue(false) + } as unknown as MetaData; + + // Мок для контекста данных + mockContext = { + getMetaData: mock().mockReturnValue(mockMetaData) + } as unknown as DataContext; + + // Мок для i18n + jest.spyOn(i18n, 'getText').mockImplementation((key: string) => { + if (key === 'RootViewTitle') return 'Entities'; + if (key === 'EntityMenuDesc') return 'Select an entity from the list below'; + if (key === 'ModelIsEmpty') return 'The model is empty'; + return key; + }); + + // Мок для функции setLocation + jest.spyOn(utils, 'setLocation').mockImplementation(() => {}); + }); + + afterEach(() => { + // Удаляем добавленные элементы + if (mockSlot.parentNode) { + mockSlot.parentNode.removeChild(mockSlot); + } + + // Сбрасываем моки + jest.restoreAllMocks(); + }); + + it('должен создаваться с правильными настройками по умолчанию', () => { + view = new RootDataView(mockSlot, mockContext, '/basePath'); + + // Проверяем, что контекст и метаданные установлены + expect((view as any).context).toBe(mockContext); + expect((view as any).metaData).toBe(mockMetaData); + + // Проверяем опции по умолчанию + const options = (view as any).options; + expect(options).toBeObject(); + expect(options.usePluralNames).toBe(true); + }); + + it('должен применять пользовательские настройки', () => { + view = new RootDataView(mockSlot, mockContext, '/basePath', { + usePluralNames: false + }); + + // Проверяем пользовательские опции + const options = (view as any).options; + expect(options).toBeObject(); + expect(options.usePluralNames).toBe(false); + }); + + it('должен рендерить заголовок', () => { + view = new RootDataView(mockSlot, mockContext, '/basePath'); + + // Проверяем, что заголовок содержит текст + const header = mockSlot.querySelector('h1'); + expect(header).toBeDefined(); + expect(header.textContent).toBe('Entities'); + }); + + it('должен рендерить список сущностей', () => { + view = new RootDataView(mockSlot, mockContext, '/basePath'); + + // Проверяем, что есть описание меню + const menuDescription = mockSlot.querySelector('.ed-menu-description'); + expect(menuDescription).toBeDefined(); + expect(menuDescription.textContent).toBe('Select an entity from the list below'); + + // Проверяем, что есть список сущностей + const entityMenu = mockSlot.querySelector('.ed-entity-menu'); + expect(entityMenu).toBeDefined(); + + // Проверяем количество элементов в списке + const entityItems = entityMenu.querySelectorAll('.ed-entity-item'); + expect(entityItems.length).toBe(3); + }); + + it('должен использовать множественные имена сущностей, когда usePluralNames=true', () => { + view = new RootDataView(mockSlot, mockContext, '/basePath', { + usePluralNames: true + }); + + // Проверяем, что используются множественные имена + const entityItems = mockSlot.querySelectorAll('.ed-entity-item-caption'); + expect(entityItems[0].textContent).toBe('Customers'); + expect(entityItems[1].textContent).toBe('Products'); + expect(entityItems[2].textContent).toBe('Orders'); + }); + + it('должен использовать обычные имена сущностей, когда usePluralNames=false', () => { + view = new RootDataView(mockSlot, mockContext, '/basePath', { + usePluralNames: false + }); + + // Проверяем, что используются обычные имена + const entityItems = mockSlot.querySelectorAll('.ed-entity-item-caption'); + expect(entityItems[0].textContent).toBe('Customer'); + expect(entityItems[1].textContent).toBe('Product'); + expect(entityItems[2].textContent).toBe('Order'); + }); + + it('должен отображать описания сущностей', () => { + view = new RootDataView(mockSlot, mockContext, '/basePath'); + + // Проверяем, что описания отображаются + const descriptionItems = mockSlot.querySelectorAll('.ed-entity-item-descr'); + + // Должно быть 2 описания (у entity2 нет описания) + expect(descriptionItems.length).toBe(2); + expect(descriptionItems[0].textContent).toBe('Customer entity description'); + expect(descriptionItems[1].textContent).toBe('Order entity description'); + }); + + it('должен обрабатывать клик по сущности', () => { + view = new RootDataView(mockSlot, mockContext, '/basePath'); + + // Получаем первый элемент списка + const firstEntityItem = mockSlot.querySelector('.ed-entity-item'); + + // Эмулируем клик + firstEntityItem.click(); + + // Проверяем, что setLocation был вызван с правильными параметрами + expect(utils.setLocation).toHaveBeenCalledWith('/basePath/entity1'); + }); + + it('должен отображать сообщение, если модель пуста', () => { + // Меняем мок, чтобы метаданные были пустыми + (mockMetaData.isEmpty as jest.Mock).mockReturnValue(true); + (mockRootEntity.subEntities as any) = []; + + view = new RootDataView(mockSlot, mockContext, '/basePath'); + + // Проверяем, что отображается сообщение о пустой модели + const menuDescription = mockSlot.querySelector('.ed-menu-description'); + expect(menuDescription).toBeDefined(); + expect(menuDescription.textContent).toBe('The model is empty'); + + // Проверяем, что список сущностей пуст + const entityItems = mockSlot.querySelectorAll('.ed-entity-item'); + expect(entityItems.length).toBe(0); + }); + + it('должен корректно декодировать ID сущностей при навигации', () => { + // Создаем сущность с ID, требующим кодирования + const encodedEntity = { + id: 'entity%20with%20space', + name: 'EncodedEntity', + caption: 'Encoded Entity', + captionPlural: 'Encoded Entities', + description: null + }; + + // Добавляем эту сущность в список + (mockRootEntity.subEntities as any) = [encodedEntity]; + + view = new RootDataView(mockSlot, mockContext, '/basePath'); + + // Получаем элемент списка + const entityItem = mockSlot.querySelector('.ed-entity-item'); + + // Эмулируем клик + entityItem.click(); + + // Проверяем, что setLocation был вызван с декодированным ID + expect(utils.setLocation).toHaveBeenCalledWith('/basePath/entity with space'); + }); +}); diff --git a/easydata.js/packs/crud/tests/text_data_filter.test.ts b/easydata.js/packs/crud/tests/text_data_filter.test.ts new file mode 100644 index 00000000..ca151fca --- /dev/null +++ b/easydata.js/packs/crud/tests/text_data_filter.test.ts @@ -0,0 +1,225 @@ +import { + EasyDataTable, + DataLoader, + ChunkInfo, + DataColumnDescriptor, + DataType +} from '@easydata/core'; + +import { TextDataFilter } from '../src/filter/text_data_filter'; +import { DataFilter } from '../src/filter/data_filter'; + +describe('TextDataFilter', () => { + // Мок для DataLoader + let mockLoader: DataLoader; + // Исходная таблица данных + let sourceTable: EasyDataTable; + // Тестируемый фильтр + let filter: TextDataFilter; + // Колонки для тестовой таблицы + let columns: DataColumnDescriptor[]; + // Данные для тестовой таблицы + let tableData: any[][]; + + // Настройка тестового окружения перед каждым тестом + beforeEach(() => { + // Определяем колонки для тестовой таблицы + columns = [ + { id: 'id', label: 'ID', type: DataType.Int32 }, + { id: 'name', label: 'Name', type: DataType.String }, + { id: 'description', label: 'Description', type: DataType.String }, + { id: 'price', label: 'Price', type: DataType.Currency } + ]; + + // Определяем данные для тестовой таблицы + tableData = [ + [1, 'Apple', 'Fresh red apple', 1.99], + [2, 'Banana', 'Yellow fruit', 0.99], + [3, 'Orange', 'Juicy citrus', 1.49], + [4, 'Pineapple', 'Tropical fruit', 3.99], + [5, 'Watermelon', 'Summer favorite', 5.99] + ]; + + // Создаем исходную таблицу данных + sourceTable = new EasyDataTable({ + columns: columns, + rows: tableData, + inMemory: true + }); + + // Создаем мок для DataLoader + mockLoader = { + loadChunk: (chunkInfo: ChunkInfo): Promise<{ table: EasyDataTable, total: number }> => { + // Эмулируем фильтрацию на сервере + const filterValue = chunkInfo['filters']?.[0]?.value?.toLowerCase(); + const filteredData = filterValue + ? tableData.filter(row => { + return row.some(cell => + cell && cell.toString().toLowerCase().includes(filterValue) + ); + }) + : tableData; + + const resultTable = new EasyDataTable({ + columns: columns, + rows: filteredData, + inMemory: true + }); + + return Promise.resolve({ + table: resultTable, + total: filteredData.length + }); + } + }; + + // Создаем тестируемый фильтр + filter = new TextDataFilter(mockLoader, sourceTable, 'products'); + }); + + it('должен быть экземпляром класса DataFilter', () => { + expect(filter).toBeInstanceOf(DataFilter); + expect(filter).toBeObject(); + }); + + it('должен возвращать пустую строку для getValue() после создания', () => { + const value = filter.getValue(); + expect(value).toBe(''); + }); + + it('должен корректно устанавливать и возвращать значение фильтра', () => { + return filter.apply('apple') + .then(() => { + const value = filter.getValue(); + expect(value).toBe('apple'); + }); + }); + + it('должен возвращать исходную таблицу при пустом значении фильтра', () => { + return filter.apply('') + .then(result => { + expect(result).toBe(sourceTable); + }); + }); + + it('должен возвращать исходную таблицу после очистки фильтра', () => { + return filter.apply('apple') + .then(() => filter.clear()) + .then(result => { + expect(result).toBe(sourceTable); + expect(filter.getValue()).toBe(''); + }); + }); + + it('должен фильтровать данные в памяти при полностью загруженной таблице', () => { + return filter.apply('apple') + .then(filteredTable => { + expect(filteredTable).not.toBe(sourceTable); + expect(filteredTable.getCachedCount()).toBe(2); // Apple и Pineapple + + const rows = filteredTable.getCachedRows(); + expect(rows).toBeArray(); + expect(rows.length).toBe(2); + + // Проверяем первую строку (Apple) + expect(rows[0].getValue('name')).toBe('Apple'); + + // Проверяем вторую строку (Pineapple) + expect(rows[1].getValue('name')).toBe('Pineapple'); + }); + }); + + it('должен использовать серверную фильтрацию когда таблица не полностью загружена', () => { + // Создаем частично загруженную таблицу и новый фильтр + const partialTable = new EasyDataTable({ + columns: columns, + loader: mockLoader + }); + + // Добавляем только часть данных, имитируя неполную загрузку + partialTable.addRow(tableData[0]); + partialTable.addRow(tableData[1]); + partialTable.setTotal(tableData.length); // Общее количество записей больше, чем закешировано + + const serverFilter = new TextDataFilter(mockLoader, partialTable, 'products'); + + // Шпионим за методом loadChunk у mockLoader + const loadChunkSpy = jest.spyOn(mockLoader, 'loadChunk'); + + return serverFilter.apply('orange') + .then(filteredTable => { + // Проверяем, что был вызван метод loadChunk + expect(loadChunkSpy).toHaveBeenCalled(); + + // Проверяем, что фильтр был передан в запрос + const callArgs = loadChunkSpy.mock.calls[0][0]; + expect(callArgs).toBeObject(); + expect(callArgs.filters).toBeArray(); + expect(callArgs.filters[0].value).toBe('orange'); + + // Проверяем результаты фильтрации + expect(filteredTable.getCachedCount()).toBe(1); + expect(filteredTable.getCachedRows()[0].getValue('name')).toBe('Orange'); + }); + }); + + it('должен поддерживать множественные поисковые слова через разделитель ||', () => { + return filter.apply('apple || melon') + .then(filteredTable => { + const rows = filteredTable.getCachedRows(); + expect(rows).toBeArray(); + expect(rows.length).toBe(3); // Apple, Pineapple, Watermelon + + const names = rows.map(row => row.getValue('name')); + expect(names).toContain('Apple'); + expect(names).toContain('Pineapple'); + expect(names).toContain('Watermelon'); + }); + }); + + it('должен фильтровать по нескольким колонкам', () => { + return filter.apply('fruit') + .then(filteredTable => { + const rows = filteredTable.getCachedRows(); + expect(rows).toBeArray(); + expect(rows.length).toBe(3); // Banana, Orange, Pineapple + + const names = rows.map(row => row.getValue('name')); + expect(names).toContain('Banana'); + expect(names).toContain('Orange'); + expect(names).toContain('Pineapple'); + }); + }); + + it('должен фильтровать без учета регистра', () => { + return filter.apply('APPLE') + .then(filteredTable => { + const rows = filteredTable.getCachedRows(); + expect(rows).toBeArray(); + expect(rows.length).toBe(2); // Apple, Pineapple + + const names = rows.map(row => row.getValue('name')); + expect(names).toContain('Apple'); + expect(names).toContain('Pineapple'); + }); + }); + + it('должен возвращать пустую таблицу если нет соответствий', () => { + return filter.apply('nonexistent') + .then(filteredTable => { + expect(filteredTable.getCachedCount()).toBe(0); + expect(filteredTable.getCachedRows()).toBeEmpty(); + }); + }); + + it('должен фильтровать по числовым значениям', () => { + return filter.apply('1.99') + .then(filteredTable => { + const rows = filteredTable.getCachedRows(); + expect(rows).toBeArray(); + expect(rows.length).toBe(1); // Apple с ценой 1.99 + expect(rows[0].getValue('name')).toBe('Apple'); + expect(rows[0].getValue('price')).toBe(1.99); + }); + }); +}); diff --git a/easydata.js/packs/crud/tests/text_filter_widget.test.ts b/easydata.js/packs/crud/tests/text_filter_widget.test.ts new file mode 100644 index 00000000..01134154 --- /dev/null +++ b/easydata.js/packs/crud/tests/text_filter_widget.test.ts @@ -0,0 +1,491 @@ +import { i18n, utils as dataUtils, EasyDataTable } from '@easydata/core'; +import { + browserUtils, CellRendererType, + EasyGrid, GridCellRenderer, + GridColumn, domel +} from '@easydata/ui'; + +import { TextFilterWidget } from '../src/widgets/text_filter_widget'; +import { DataFilter } from '../src/filter/data_filter'; + +describe('TextFilterWidget', () => { + // Переменные для тестов + let mockSlot: HTMLElement; + let mockGrid: EasyGrid; + let mockFilter: DataFilter; + let filterWidget: TextFilterWidget; + let mockCellRendererStore: any; + let defaultStringRenderer: GridCellRenderer; + let defaultNumberRenderer: GridCellRenderer; + + // Инициализация перед каждым тестом + beforeEach(() => { + // Создаем DOM для монтирования виджета + mockSlot = document.createElement('div'); + document.body.appendChild(mockSlot); + + // Создаем моки для рендереров ячеек + defaultStringRenderer = mock(); + defaultNumberRenderer = mock(); + + // Мок для хранилища рендереров + mockCellRendererStore = { + getDefaultRendererByType: mock((type: CellRendererType) => { + if (type === CellRendererType.STRING) return defaultStringRenderer; + if (type === CellRendererType.NUMBER) return defaultNumberRenderer; + return null; + }), + setDefaultRenderer: mock() + }; + + // Мок для EasyGrid + mockGrid = { + cellRendererStore: mockCellRendererStore, + setData: mock(), + } as unknown as EasyGrid; + + // Мок для DataFilter + mockFilter = { + getValue: mock().mockReturnValue(''), + apply: mock().mockResolvedValue(new EasyDataTable()) + } as unknown as DataFilter; + + // Мок для i18n.getText + jest.spyOn(i18n, 'getText').mockImplementation((key: string) => { + if (key === 'SearchInputPlaceholder') return 'Search...'; + if (key === 'SearchBtn') return 'Search'; + return key; + }); + + // Мок для browserUtils.isIE и isEdge + jest.spyOn(browserUtils, 'isIE').mockReturnValue(false); + jest.spyOn(browserUtils, 'isEdge').mockReturnValue(false); + + // Мок для dataUtils.isNumericType и getStringDataTypes + jest.spyOn(dataUtils, 'isNumericType').mockImplementation((type) => { + return type === 'number'; + }); + jest.spyOn(dataUtils, 'getStringDataTypes').mockReturnValue(['string']); + + // Создание экземпляра виджета для тестов + filterWidget = new TextFilterWidget(mockSlot, mockGrid, mockFilter); + }); + + // Очистка после каждого теста + afterEach(() => { + if (mockSlot.parentNode) { + mockSlot.parentNode.removeChild(mockSlot); + } + + // Сброс всех моков + jest.restoreAllMocks(); + }); + + it('должен создаваться с настройками по умолчанию', () => { + expect(filterWidget).toBeDefined(); + + // Проверка установки рендереров + expect(mockCellRendererStore.getDefaultRendererByType).toHaveBeenCalledWith(CellRendererType.STRING); + expect(mockCellRendererStore.getDefaultRendererByType).toHaveBeenCalledWith(CellRendererType.NUMBER); + expect(mockCellRendererStore.setDefaultRenderer).toHaveBeenCalledTimes(2); + }); + + it('должен правильно рендерить HTML структуру', () => { + // Проверка наличия поля ввода + const input = mockSlot.querySelector('input'); + expect(input).toBeDefined(); + expect(input.getAttribute('placeholder')).toBe('Search...'); + + // Проверка наличия кнопки поиска (вне режима instantMode) + const button = mockSlot.querySelector('button'); + expect(button).toBeDefined(); + expect(button.textContent).toBe('Search'); + + // Проверка наличия иконки очистки + const clearIcon = mockSlot.querySelector('span.icon'); + expect(clearIcon).toBeDefined(); + }); + + it('должен рендерить HTML без кнопки поиска в instantMode', () => { + // Удаляем предыдущий виджет + mockSlot.innerHTML = ''; + + // Создаем новый виджет с instantMode + filterWidget = new TextFilterWidget(mockSlot, mockGrid, mockFilter, { + instantMode: true + }); + + // Проверка наличия поля ввода + const input = mockSlot.querySelector('input'); + expect(input).toBeDefined(); + + // Проверка отсутствия кнопки поиска + const button = mockSlot.querySelector('button'); + expect(button).toBeNull(); + }); + + it('должен использовать другие классы для IE', () => { + // Удаляем предыдущий виджет + mockSlot.innerHTML = ''; + + // Меняем мок для IE + (browserUtils.isIE as jest.Mock).mockReturnValue(true); + + // Создаем новый виджет + filterWidget = new TextFilterWidget(mockSlot, mockGrid, mockFilter); + + // Проверка наличия класса для IE + expect(mockSlot.classList.contains('kfrm-fields-ie')).toBe(true); + }); + + it('должен устанавливать фокус на поле ввода, если опция focus=true', () => { + // Удаляем предыдущий виджет + mockSlot.innerHTML = ''; + + // Мок для focus + const focusMock = mock(); + HTMLInputElement.prototype.focus = focusMock; + + // Создаем новый виджет с опцией focus=true + filterWidget = new TextFilterWidget(mockSlot, mockGrid, mockFilter, { + focus: true + }); + + // Проверка вызова focus + expect(focusMock).toHaveBeenCalled(); + }); + + it('должен применять фильтр при нажатии Enter', () => { + // Получаем поле ввода + const input = mockSlot.querySelector('input') as HTMLInputElement; + + // Устанавливаем значение + input.value = 'test'; + + // Эмулируем нажатие Enter + const enterEvent = new KeyboardEvent('keydown', { keyCode: 13 }); + input.dispatchEvent(enterEvent); + + // Проверка вызова apply с правильным значением + expect(mockFilter.apply).toHaveBeenCalledWith('test'); + }); + + it('должен применять фильтр при нажатии кнопки Search', () => { + // Получаем поле ввода и кнопку + const input = mockSlot.querySelector('input') as HTMLInputElement; + const button = mockSlot.querySelector('button') as HTMLButtonElement; + + // Устанавливаем значение + input.value = 'test'; + + // Эмулируем клик по кнопке + button.click(); + + // Проверка вызова apply с правильным значением + expect(mockFilter.apply).toHaveBeenCalledWith('test'); + }); + + it('должен очищать поле ввода при нажатии на иконку очистки', () => { + // Получаем поле ввода и иконку очистки + const input = mockSlot.querySelector('input') as HTMLInputElement; + const clearIcon = mockSlot.querySelector('span.icon') as HTMLSpanElement; + + // Устанавливаем значение и фокус мок + input.value = 'test'; + const focusMock = mock(); + input.focus = focusMock; + + // Эмулируем клик по иконке очистки + clearIcon.click(); + + // Проверка очистки значения + expect(input.value).toBe(''); + + // Проверка установки фокуса + expect(focusMock).toHaveBeenCalled(); + + // Проверка вызова apply с пустым значением + expect(mockFilter.apply).toHaveBeenCalledWith(''); + }); + + it('должен применять фильтр с задержкой в instantMode при вводе', () => { + // Удаляем предыдущий виджет + mockSlot.innerHTML = ''; + + // Мок для setTimeout + jest.useFakeTimers(); + + // Создаем новый виджет с instantMode и маленьким таймаутом + filterWidget = new TextFilterWidget(mockSlot, mockGrid, mockFilter, { + instantMode: true, + instantTimeout: 500 + }); + + // Получаем поле ввода + const input = mockSlot.querySelector('input') as HTMLInputElement; + + // Устанавливаем значение и эмулируем keyup + input.value = 'test'; + input.dispatchEvent(new KeyboardEvent('keyup')); + + // Проверка, что apply не вызван сразу + expect(mockFilter.apply).not.toHaveBeenCalled(); + + // Проматываем таймеры + jest.advanceTimersByTime(500); + + // Проверка вызова apply с правильным значением + expect(mockFilter.apply).toHaveBeenCalledWith('test'); + + // Восстанавливаем таймеры + jest.useRealTimers(); + }); + + it('должен очищать предыдущий таймер при новом событии keyup', () => { + // Удаляем предыдущий виджет + mockSlot.innerHTML = ''; + + // Мок для setTimeout и clearTimeout + jest.useFakeTimers(); + const clearTimeoutSpy = jest.spyOn(window, 'clearTimeout'); + + // Создаем новый виджет с instantMode + filterWidget = new TextFilterWidget(mockSlot, mockGrid, mockFilter, { + instantMode: true, + instantTimeout: 500 + }); + + // Получаем поле ввода + const input = mockSlot.querySelector('input') as HTMLInputElement; + + // Первый ввод + input.value = 'test'; + input.dispatchEvent(new KeyboardEvent('keyup')); + + // Второй ввод до истечения таймаута + input.value = 'test2'; + input.dispatchEvent(new KeyboardEvent('keyup')); + + // Проверка вызова clearTimeout + expect(clearTimeoutSpy).toHaveBeenCalled(); + + // Проматываем таймеры + jest.advanceTimersByTime(500); + + // Проверка вызова apply с последним значением + expect(mockFilter.apply).toHaveBeenCalledWith('test2'); + + // Восстанавливаем таймеры + jest.useRealTimers(); + }); + + it('метод applyFilter должен вернуть true если значение фильтра изменилось', () => { + // Получаем поле ввода + const input = mockSlot.querySelector('input') as HTMLInputElement; + + // Устанавливаем значение + input.value = 'test'; + + // Мок для getValue, возвращающий другое значение + (mockFilter.getValue as jest.Mock).mockReturnValue('old'); + + // Вызываем метод и проверяем результат + const result = filterWidget.applyFilter(true); + expect(result).toBe(true); + + // Проверка вызова apply + expect(mockFilter.apply).toHaveBeenCalledWith('test'); + }); + + it('метод applyFilter должен вернуть false если значение фильтра не изменилось', () => { + // Получаем поле ввода + const input = mockSlot.querySelector('input') as HTMLInputElement; + + // Устанавливаем значение + input.value = 'test'; + + // Мок для getValue, возвращающий то же самое значение + (mockFilter.getValue as jest.Mock).mockReturnValue('test'); + + // Вызываем метод и проверяем результат + const result = filterWidget.applyFilter(true); + expect(result).toBe(false); + + // Проверка, что apply не вызван + expect(mockFilter.apply).not.toHaveBeenCalled(); + }); + + it('должен правильно выделять текст, совпадающий с фильтром', () => { + // Мок для getValue, возвращающий искомое слово + (mockFilter.getValue as jest.Mock).mockReturnValue('test'); + + // Вызываем highlightText напрямую через приватный метод + const result = (filterWidget as any).highlightText('This is a test string'); + + // Проверка типа результата + expect(result instanceof HTMLElement).toBe(true); + + // Проверка содержимого + const div = result as HTMLElement; + + // Должно быть 3 дочерних элемента: текст, span с выделением, текст + expect(div.childNodes.length).toBe(3); + + // Проверка правильного выделения + expect(div.childNodes[0].textContent).toBe('This is a '); + + const highlightSpan = div.childNodes[1] as HTMLSpanElement; + expect(highlightSpan.tagName).toBe('SPAN'); + expect(highlightSpan.style.backgroundColor).toBe('yellow'); + expect(highlightSpan.textContent).toBe('test'); + + expect(div.childNodes[2].textContent).toBe(' string'); + }); + + it('должен правильно выделять несколько совпадений', () => { + // Мок для getValue, возвращающий искомое слово + (mockFilter.getValue as jest.Mock).mockReturnValue('test'); + + // Вызываем highlightText напрямую через приватный метод + const result = (filterWidget as any).highlightText('test another test'); + + // Проверка типа результата + expect(result instanceof HTMLElement).toBe(true); + + // Проверка содержимого + const div = result as HTMLElement; + + // Должно быть 5 дочерних элементов: span, текст, span, текст + expect(div.childNodes.length).toBe(3); + + // Проверка первого выделения + const firstSpan = div.childNodes[0] as HTMLSpanElement; + expect(firstSpan.tagName).toBe('SPAN'); + expect(firstSpan.textContent).toBe('test'); + + // Проверка текста между выделениями + expect(div.childNodes[1].textContent).toBe(' another '); + + // Проверка второго выделения + const secondSpan = div.childNodes[2] as HTMLSpanElement; + expect(secondSpan.tagName).toBe('SPAN'); + expect(secondSpan.textContent).toBe('test'); + }); + + it('должен правильно обрабатывать несколько искомых слов через разделитель ||', () => { + // Мок для getValue, возвращающий несколько слов через разделитель + (mockFilter.getValue as jest.Mock).mockReturnValue('apple || banana'); + + // Вызываем highlightText напрямую через приватный метод + const result = (filterWidget as any).highlightText('I have an apple and a banana'); + + // Проверка типа результата + expect(result instanceof HTMLElement).toBe(true); + + // Проверка содержимого + const div = result as HTMLElement; + + // Должен содержать выделения для обоих слов + let appleFound = false; + let bananaFound = false; + + for (let i = 0; i < div.childNodes.length; i++) { + const node = div.childNodes[i]; + if (node instanceof HTMLSpanElement && node.textContent === 'apple') { + appleFound = true; + } + if (node instanceof HTMLSpanElement && node.textContent === 'banana') { + bananaFound = true; + } + } + + expect(appleFound).toBe(true); + expect(bananaFound).toBe(true); + }); + + it('должен выделять всю ячейку, если содержимое полностью совпадает с фильтром', () => { + // Мок для getValue, возвращающий полное значение ячейки + (mockFilter.getValue as jest.Mock).mockReturnValue('exact match'); + + // Вызываем highlightText напрямую через приватный метод + const result = (filterWidget as any).highlightText('exact match'); + + // Должен вернуть один span, выделяющий всё содержимое + expect(result instanceof HTMLSpanElement).toBe(true); + expect((result as HTMLSpanElement).style.backgroundColor).toBe('yellow'); + expect((result as HTMLSpanElement).textContent).toBe('exact match'); + }); + + it('должен возвращать исходный текст, если нет совпадений', () => { + // Мок для getValue, возвращающий слово, которого нет в тексте + (mockFilter.getValue as jest.Mock).mockReturnValue('missing'); + + // Вызываем highlightText напрямую через приватный метод + const result = (filterWidget as any).highlightText('This is a test string'); + + // Должен вернуть оригинальную строку + expect(result).toBe('This is a test string'); + }); + + it('должен возвращать исходный текст, если фильтр пустой', () => { + // Мок для getValue, возвращающий пустую строку + (mockFilter.getValue as jest.Mock).mockReturnValue(''); + + // Вызываем highlightText напрямую через приватный метод + const result = (filterWidget as any).highlightText('This is a test string'); + + // Должен вернуть оригинальную строку + expect(result).toBe('This is a test string'); + }); + + it('должен использовать пользовательский рендерер для строковых ячеек', () => { + // Создаем моки для параметров рендерера + const column: GridColumn = { + dataColumn: { id: 'name' }, + type: 'string' + } as GridColumn; + const cellElement = document.createElement('div'); + const rowElement = document.createElement('tr'); + + // Получаем установленный рендерер для строк + const stringRendererCall = (mockCellRendererStore.setDefaultRenderer as jest.Mock).mock.calls[0]; + const customStringRenderer = stringRendererCall[1]; + + // Мок для getValue, чтобы строки выделялись + (mockFilter.getValue as jest.Mock).mockReturnValue('test'); + + // Вызываем пользовательский рендерер + customStringRenderer('This is a test', column, cellElement, rowElement); + + // Проверяем, что ячейка содержит выделенный текст + expect(cellElement.innerHTML).not.toBe(''); + expect(cellElement.querySelector('span[style*="yellow"]')).toBeDefined(); + }); + + it('должен использовать пользовательский рендерер для числовых ячеек', () => { + // Создаем моки для параметров рендерера + const column: GridColumn = { + dataColumn: { id: 'value' }, + type: 'number' + } as GridColumn; + const cellElement = document.createElement('div'); + const rowElement = document.createElement('tr'); + + // Получаем установленный рендерер для чисел + const numberRendererCall = (mockCellRendererStore.setDefaultRenderer as jest.Mock).mock.calls[1]; + const customNumberRenderer = numberRendererCall[1]; + + // Мок для getValue, чтобы числа выделялись + (mockFilter.getValue as jest.Mock).mockReturnValue('42'); + + // Мок для toLocaleString + Number.prototype.toLocaleString = function() { return this.toString(); }; + + // Вызываем пользовательский рендерер + customNumberRenderer(42, column, cellElement, rowElement); + + // Проверяем, что ячейка содержит выделенный текст + expect(cellElement.innerHTML).not.toBe(''); + expect(cellElement.querySelector('span[style*="yellow"]')).toBeDefined(); + }); +}); diff --git a/easydata.js/packs/crud/tests/utils.test.ts b/easydata.js/packs/crud/tests/utils.test.ts new file mode 100644 index 00000000..cbec0553 --- /dev/null +++ b/easydata.js/packs/crud/tests/utils.test.ts @@ -0,0 +1,127 @@ +import { DataType, i18n } from '@easydata/core'; +import { getInternalDateTimeFormat, getEditDateTimeFormat, setLocation } from '../src/utils/utils'; + +describe('Utils', () => { + // Сохраняем оригинальные объекты перед тестами + const originalWindow = { ...window }; + const originalHistory = { ...window.history }; + const originalDispatchEvent = window.dispatchEvent; + + // Оригинальные настройки i18n + let originalSettings: any; + + beforeEach(() => { + // Сохраняем оригинальные настройки i18n + originalSettings = i18n.getLocaleSettings(); + + // Создаем мок для i18n.getLocaleSettings + jest.spyOn(i18n, 'getLocaleSettings').mockReturnValue({ + ...originalSettings, + editDateFormat: 'dd.MM.yyyy', + editTimeFormat: 'HH:mm', + }); + + // Создаем моки для window.history + window.history.pushState = mock(); + window.dispatchEvent = mock(); + }); + + afterEach(() => { + // Восстанавливаем оригинальные объекты и функции + jest.restoreAllMocks(); + window.history = originalHistory; + window.dispatchEvent = originalDispatchEvent; + }); + + it('должен возвращать правильный внутренний формат для Date', () => { + const format = getInternalDateTimeFormat(DataType.Date); + expect(format).toBe('yyyy-MM-dd'); + }); + + it('должен возвращать правильный внутренний формат для Time', () => { + const format = getInternalDateTimeFormat(DataType.Time); + expect(format).toBe('HH:mm'); + }); + + it('должен возвращать правильный внутренний формат для DateTime', () => { + const format = getInternalDateTimeFormat(DataType.DateTime); + expect(format).toBe('yyyy-MM-ddTHH:mm'); + }); + + it('должен возвращать формат редактирования из настроек локали для Date', () => { + const format = getEditDateTimeFormat(DataType.Date); + expect(format).toBe('dd.MM.yyyy'); + }); + + it('должен возвращать формат редактирования из настроек локали для Time', () => { + const format = getEditDateTimeFormat(DataType.Time); + expect(format).toBe('HH:mm'); + }); + + it('должен возвращать формат редактирования из настроек локали для DateTime', () => { + const format = getEditDateTimeFormat(DataType.DateTime); + expect(format).toBe('dd.MM.yyyy HH:mm'); + }); + + it('должен изменять локацию через pushState и генерировать событие', () => { + // Устанавливаем исходное значение state + const mockState = { test: 'state' }; + window.history.state = mockState; + document.title = 'Test Title'; + + // Вызываем функцию + setLocation('/new-path'); + + // Проверяем, что pushState был вызван с правильными аргументами + expect(window.history.pushState).toHaveBeenCalledWith( + mockState, + 'Test Title', + '/new-path' + ); + + // Проверяем, что было сгенерировано правильное событие + expect(window.dispatchEvent).toHaveBeenCalled(); + const eventArg = (window.dispatchEvent as jest.Mock).mock.calls[0][0]; + expect(eventArg).toBeInstanceOf(Event); + expect(eventArg.type).toBe('ed_set_location'); + }); + + it('должен работать с различными форматами путей', () => { + // Относительный путь + setLocation('relative/path'); + expect(window.history.pushState).toHaveBeenCalledWith( + expect.anything(), + expect.anything(), + 'relative/path' + ); + + // Путь с параметрами + setLocation('/path?param=value'); + expect(window.history.pushState).toHaveBeenCalledWith( + expect.anything(), + expect.anything(), + '/path?param=value' + ); + + // Путь с хэшем + setLocation('/path#section'); + expect(window.history.pushState).toHaveBeenCalledWith( + expect.anything(), + expect.anything(), + '/path#section' + ); + }); + + it('должен использовать форматы из i18n для редактирования', () => { + // Перенастраиваем мок для i18n.getLocaleSettings с другими форматами + (i18n.getLocaleSettings as jest.Mock).mockReturnValue({ + editDateFormat: 'MM/dd/yyyy', + editTimeFormat: 'hh:mm a', + }); + + // Проверяем обновленные форматы + expect(getEditDateTimeFormat(DataType.Date)).toBe('MM/dd/yyyy'); + expect(getEditDateTimeFormat(DataType.Time)).toBe('hh:mm a'); + expect(getEditDateTimeFormat(DataType.DateTime)).toBe('MM/dd/yyyy hh:mm a'); + }); +}); diff --git a/easydata.js/packs/crud/tests/validators.test.ts b/easydata.js/packs/crud/tests/validators.test.ts new file mode 100644 index 00000000..60551c94 --- /dev/null +++ b/easydata.js/packs/crud/tests/validators.test.ts @@ -0,0 +1,281 @@ +import { DataType, i18n, MetaEntityAttr } from '@easydata/core'; +import { Validator, ValidationResult } from '../src/validators/validator'; +import { TypeValidator } from '../src/validators/type_validator'; +import { RequiredValidator } from '../src/validators/required_validator'; +import { DateTimeValidator } from '../src/validators/datetime_validator'; +import * as utils from '../src/utils/utils'; + +describe('TypeValidator', () => { + let validator: TypeValidator; + let mockAttr: MetaEntityAttr; + + beforeEach(() => { + validator = new TypeValidator(); + + // Мок текстов для i18n + jest.spyOn(i18n, 'getText').mockImplementation((key: string) => { + if (key === 'NumberError') return 'Value must be a number'; + if (key === 'IntNumberError') return 'Value must be an integer number'; + return key; + }); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('должен иметь имя "Type"', () => { + expect(validator.name).toBe('Type'); + expect(validator).toBeInstanceOf(Validator); + }); + + it('должен успешно проходить валидацию для null или пустой строки', () => { + mockAttr = { dataType: DataType.Int32 } as MetaEntityAttr; + + const result1 = validator.validate(mockAttr, null); + expect(result1.successed).toBe(true); + + const result2 = validator.validate(mockAttr, undefined); + expect(result2.successed).toBe(true); + + const result3 = validator.validate(mockAttr, ''); + expect(result3.successed).toBe(true); + }); + + it('должен проверять числовые значения для числового типа', () => { + mockAttr = { dataType: DataType.Int32 } as MetaEntityAttr; + + // Валидные значения + const result1 = validator.validate(mockAttr, '123'); + expect(result1.successed).toBe(true); + + const result2 = validator.validate(mockAttr, 123); + expect(result2.successed).toBe(true); + + // Невалидные значения + const result3 = validator.validate(mockAttr, 'abc'); + expect(result3.successed).toBe(false); + expect(result3.messages).toBeArray(); + expect(result3.messages[0]).toBe('Value must be a number'); + }); + + it('должен проверять дробные значения для целочисленного типа', () => { + mockAttr = { dataType: DataType.Int32 } as MetaEntityAttr; + + // Целочисленное значение + const result1 = validator.validate(mockAttr, '123'); + expect(result1.successed).toBe(true); + + // Дробное значение не должно быть валидным для Int32 + const result2 = validator.validate(mockAttr, '123.45'); + expect(result2.successed).toBe(false); + expect(result2.messages).toBeArray(); + expect(result2.messages[0]).toBe('Value must be an integer number'); + }); + + it('должен принимать дробные значения для типа Float', () => { + mockAttr = { dataType: DataType.Float } as MetaEntityAttr; + + // Целочисленное значение + const result1 = validator.validate(mockAttr, '123'); + expect(result1.successed).toBe(true); + + // Дробное значение должно быть валидным для Float + const result2 = validator.validate(mockAttr, '123.45'); + expect(result2.successed).toBe(true); + + // Строка не должна быть валидной + const result3 = validator.validate(mockAttr, 'abc'); + expect(result3.successed).toBe(false); + expect(result3.messages).toBeArray(); + expect(result3.messages[0]).toBe('Value must be a number'); + }); + + it('должен успешно проходить валидацию для нечисловых типов', () => { + mockAttr = { dataType: DataType.String } as MetaEntityAttr; + + // Любое значение должно быть валидным для строки + const result1 = validator.validate(mockAttr, '123'); + expect(result1.successed).toBe(true); + + const result2 = validator.validate(mockAttr, 'abc'); + expect(result2.successed).toBe(true); + }); +}); + +describe('RequiredValidator', () => { + let validator: RequiredValidator; + let mockAttr: MetaEntityAttr; + + beforeEach(() => { + validator = new RequiredValidator(); + + // Мок текстов для i18n + jest.spyOn(i18n, 'getText').mockImplementation((key: string) => { + if (key === 'RequiredError') return 'This field is required'; + return key; + }); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('должен иметь имя "Required"', () => { + expect(validator.name).toBe('Required'); + expect(validator).toBeInstanceOf(Validator); + }); + + it('должен проходить валидацию для nullable атрибутов', () => { + mockAttr = { isNullable: true } as MetaEntityAttr; + + const result1 = validator.validate(mockAttr, null); + expect(result1.successed).toBe(true); + + const result2 = validator.validate(mockAttr, undefined); + expect(result2.successed).toBe(true); + + const result3 = validator.validate(mockAttr, ''); + expect(result3.successed).toBe(true); + }); + + it('должен проверять обязательные поля', () => { + mockAttr = { isNullable: false } as MetaEntityAttr; + + // Не должно быть валидным для null, undefined или пустой строки + const result1 = validator.validate(mockAttr, null); + expect(result1.successed).toBe(false); + expect(result1.messages).toBeArray(); + expect(result1.messages[0]).toBe('This field is required'); + + const result2 = validator.validate(mockAttr, undefined); + expect(result2.successed).toBe(false); + expect(result2.messages).toBeArray(); + expect(result2.messages[0]).toBe('This field is required'); + + const result3 = validator.validate(mockAttr, ''); + expect(result3.successed).toBe(false); + expect(result3.messages).toBeArray(); + expect(result3.messages[0]).toBe('This field is required'); + + // Должно быть валидным для непустых значений + const result4 = validator.validate(mockAttr, 'some value'); + expect(result4.successed).toBe(true); + + const result5 = validator.validate(mockAttr, 0); + expect(result5.successed).toBe(true); + + const result6 = validator.validate(mockAttr, false); + expect(result6.successed).toBe(true); + }); +}); + +describe('DateTimeValidator', () => { + let validator: DateTimeValidator; + let mockAttr: MetaEntityAttr; + + beforeEach(() => { + validator = new DateTimeValidator(); + + // Мок текстов для i18n + jest.spyOn(i18n, 'getText').mockImplementation((key: string) => { + if (key === 'DateTimeError') return 'Invalid date format'; + return key; + }); + + // Мок для getEditDateTimeFormat + jest.spyOn(utils, 'getEditDateTimeFormat').mockImplementation((dataType: DataType) => { + if (dataType === DataType.Date) return 'dd.MM.yyyy'; + if (dataType === DataType.Time) return 'HH:mm'; + if (dataType === DataType.DateTime) return 'dd.MM.yyyy HH:mm'; + return ''; + }); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('должен иметь имя "DateTime"', () => { + expect(validator.name).toBe('DateTime'); + expect(validator).toBeInstanceOf(Validator); + }); + + it('должен успешно проходить валидацию для null или пустой строки', () => { + mockAttr = { dataType: DataType.Date } as MetaEntityAttr; + + const result1 = validator.validate(mockAttr, null); + expect(result1.successed).toBe(true); + + const result2 = validator.validate(mockAttr, undefined); + expect(result2.successed).toBe(true); + + const result3 = validator.validate(mockAttr, ''); + expect(result3.successed).toBe(true); + }); + + it('должен проверять формат даты', () => { + mockAttr = { dataType: DataType.Date } as MetaEntityAttr; + + // Корректный формат + const result1 = validator.validate(mockAttr, '15.03.2023'); + expect(result1.successed).toBe(true); + + // Некорректный формат + const result2 = validator.validate(mockAttr, '2023/03/15'); + expect(result2.successed).toBe(false); + expect(result2.messages).toBeArray(); + expect(result2.messages[0]).toBe('Invalid date format'); + + // Некорректное значение + const result3 = validator.validate(mockAttr, '32.13.2023'); + expect(result3.successed).toBe(false); + expect(result3.messages).toBeArray(); + expect(result3.messages[0]).toBe('Invalid date format'); + }); + + it('должен проверять формат времени', () => { + mockAttr = { dataType: DataType.Time } as MetaEntityAttr; + + // Корректный формат + const result1 = validator.validate(mockAttr, '14:30'); + expect(result1.successed).toBe(true); + + // Некорректный формат + const result2 = validator.validate(mockAttr, '14.30'); + expect(result2.successed).toBe(false); + expect(result2.messages).toBeArray(); + expect(result2.messages[0]).toBe('Invalid date format'); + + // Некорректное значение + const result3 = validator.validate(mockAttr, '25:70'); + expect(result3.successed).toBe(false); + expect(result3.messages).toBeArray(); + expect(result3.messages[0]).toBe('Invalid date format'); + }); + + it('должен проверять формат даты и времени', () => { + mockAttr = { dataType: DataType.DateTime } as MetaEntityAttr; + + // Корректный формат + const result1 = validator.validate(mockAttr, '15.03.2023 14:30'); + expect(result1.successed).toBe(true); + + // Некорректный формат + const result2 = validator.validate(mockAttr, '2023-03-15 14:30'); + expect(result2.successed).toBe(false); + expect(result2.messages).toBeArray(); + expect(result2.messages[0]).toBe('Invalid date format'); + }); + + it('должен успешно проходить валидацию для нетиповых дат', () => { + mockAttr = { dataType: DataType.String } as MetaEntityAttr; + + // Любое значение должно быть валидным для строки + const result1 = validator.validate(mockAttr, '15.03.2023'); + expect(result1.successed).toBe(true); + + const result2 = validator.validate(mockAttr, 'неверная дата'); + expect(result2.successed).toBe(true); + }); +}); diff --git a/easydata.js/packs/ui/package.json b/easydata.js/packs/ui/package.json index eb07bfa1..bcb0fd5d 100644 --- a/easydata.js/packs/ui/package.json +++ b/easydata.js/packs/ui/package.json @@ -6,11 +6,11 @@ "main": "./dist/easydata.ui.cjs.js", "module": "./dist/easydata.ui.esm.js", "scripts": { - "test": "vitest run", "clear": "shx rm -rf dist/* docs/*", "build": "npm run clear && rollup -c", "watch": "rollup -c -w", - "docs": "typedoc src/public_api.ts --out ./docs" + "docs": "typedoc src/public_api.ts --out ./docs", + "test": "cross-env NODE_OPTIONS=\"--import tsx\" latte -d -v -t --include=./**/*.test.ts" }, "author": "Korzh.com", "homepage": "https://github.com/KorzhCom/EasyData", diff --git a/easydata.js/packs/ui/rollup.config.mjs b/easydata.js/packs/ui/rollup.config.mjs index 91bb14e9..d0fd67bc 100644 --- a/easydata.js/packs/ui/rollup.config.mjs +++ b/easydata.js/packs/ui/rollup.config.mjs @@ -5,7 +5,7 @@ import postcss from 'rollup-plugin-postcss' import autoprefixer from "autoprefixer" import progress from 'rollup-plugin-progress' import typescript from '@rollup/plugin-typescript' -import typedoc from '@olton/rollup-plugin-typedoc' +// import typedoc from '@olton/rollup-plugin-typedoc' import noEmit from 'rollup-plugin-no-emit' import * as path from "path"; import { fileURLToPath } from 'url'; @@ -49,12 +49,12 @@ export default [ typescript({ sourceMap: sourcemap, }), nodeResolve({ browser: true, }), commonjs(), - typedoc({ - json: '../../docs/easydata-ui.json', - out: './docs', - entryPoints: ['./src/**/*.ts'], - tsconfig: './tsconfig.json' - }), + // typedoc({ + // json: '../../docs/easydata-ui.json', + // out: './docs', + // entryPoints: ['./src/**/*.ts'], + // tsconfig: './tsconfig.json' + // }), ], external: ["@easydata/core"], output: [ diff --git a/package.json b/package.json index 2eb8f975..acce43d9 100644 --- a/package.json +++ b/package.json @@ -28,31 +28,29 @@ "devDependencies": { "@olton/rollup-plugin-typedoc": "^0.2.0", "@rollup/plugin-buble": "^1.0.3", - "@rollup/plugin-commonjs": "^28.0.3", + "@rollup/plugin-commonjs": "^28.0.6", "@rollup/plugin-multi-entry": "^6.0.1", "@rollup/plugin-node-resolve": "^16.0.1", "@rollup/plugin-terser": "^0.4.4", - "@rollup/plugin-typescript": "^12.1.2", + "@rollup/plugin-typescript": "^12.1.4", "@testing-library/jest-dom": "^6.6.3", "@types/jsdom": "^21.1.7", - "@vitest/ui": "^3.1.3", + "@vitest/ui": "^3.2.4", "autoprefixer": "^10.4.21", "copyfiles": "^2.4.1", "jsdom": "^26.1.0", - "lerna": "^8.2.2", - "rollup": "^4.40.2", + "lerna": "^8.2.3", + "rollup": "^4.45.1", "rollup-plugin-cleanup": "^3.2.1", "rollup-plugin-no-emit": "^1.3.0", "rollup-plugin-postcss": "^4.0.2", "rollup-plugin-progress": "^1.1.2", "shx": "^0.4.0", "tslib": "^2.8.1", - "typedoc": "^0.28.4", - "typescript": "^5.8.3" - }, - "dependencies": { - "@olton/latte": "^0.14.1", + "typedoc": "^0.28.7", + "typescript": "^5.8.3", + "@olton/latte": "^0.17.3", "cross-env": "^7.0.3", - "tsx": "^4.19.4" + "tsx": "^4.20.3" } } diff --git a/playground/EasyDataAspNetCoreTest01/rollup.config.mjs b/playground/EasyDataAspNetCoreTest01/rollup.config.mjs index c678884d..f30c4bd5 100644 --- a/playground/EasyDataAspNetCoreTest01/rollup.config.mjs +++ b/playground/EasyDataAspNetCoreTest01/rollup.config.mjs @@ -5,7 +5,7 @@ import postcss from 'rollup-plugin-postcss' import autoprefixer from "autoprefixer" import progress from 'rollup-plugin-progress' import typescript from '@rollup/plugin-typescript' -import typedoc from '@olton/rollup-plugin-typedoc' +// import typedoc from '@olton/rollup-plugin-typedoc' import noEmit from 'rollup-plugin-no-emit' import multi from '@rollup/plugin-multi-entry' import * as path from "path"; diff --git a/playground/EasyDataAspNetCoreTest02/rollup.config.mjs b/playground/EasyDataAspNetCoreTest02/rollup.config.mjs index a5f93fd4..928d73af 100644 --- a/playground/EasyDataAspNetCoreTest02/rollup.config.mjs +++ b/playground/EasyDataAspNetCoreTest02/rollup.config.mjs @@ -5,7 +5,7 @@ import postcss from 'rollup-plugin-postcss' import autoprefixer from "autoprefixer" import progress from 'rollup-plugin-progress' import typescript from '@rollup/plugin-typescript' -import typedoc from '@olton/rollup-plugin-typedoc' +// import typedoc from '@olton/rollup-plugin-typedoc' import noEmit from 'rollup-plugin-no-emit' import * as path from "path"; import { fileURLToPath } from 'url'; From 5710b27f0e4a405f713cd389c761b4ae8e2af127 Mon Sep 17 00:00:00 2001 From: Oleksandr Melnychenko Date: Fri, 18 Jul 2025 21:25:02 +0300 Subject: [PATCH 02/11] Update Directiry.Build.props --- easydata.net/src/Directory.Build.props | 2 ++ 1 file changed, 2 insertions(+) diff --git a/easydata.net/src/Directory.Build.props b/easydata.net/src/Directory.Build.props index e15543ee..23e1f934 100644 --- a/easydata.net/src/Directory.Build.props +++ b/easydata.net/src/Directory.Build.props @@ -1,6 +1,8 @@ + 1.5.9 + 1.5.9.1 Debug;Release Korzh.com From a2dd5049e96fdf72d1a7e3c8bc17ca0a50983fb6 Mon Sep 17 00:00:00 2001 From: korzh Date: Sat, 19 Jul 2025 13:41:08 +0300 Subject: [PATCH 03/11] Update test comments part 1 --- .../packs/core/tests/aggr_settings.test.ts | 14 +- .../packs/core/tests/aggr_structures.test.ts | 58 ++--- .../packs/core/tests/data_column.test.ts | 42 +-- easydata.js/packs/core/tests/data_row.test.ts | 26 +- .../packs/core/tests/easy_data_table.test.ts | 50 ++-- .../packs/core/tests/easy_guid.test.ts | 24 +- .../packs/core/tests/event_emitter.test.ts | 38 +-- .../core/tests/http_action_result.test.ts | 26 +- .../packs/core/tests/http_client.test.ts | 60 ++--- .../packs/core/tests/http_request.test.ts | 30 +-- easydata.js/packs/core/tests/i18n.test.ts | 64 ++--- easydata.js/packs/core/tests/liquid.test.ts | 26 +- .../packs/core/tests/meta_data.test.ts | 84 +++--- .../packs/core/tests/meta_entity.test.ts | 36 +-- .../packs/core/tests/string_utils.test.ts | 12 +- .../packs/core/tests/time_utils.test.ts | 54 ++-- easydata.js/packs/core/tests/utils.test.ts | 86 +++---- .../packs/core/tests/value_editor.test.ts | 24 +- .../packs/crud/tests/data_context.test.ts | 120 ++++----- .../tests/easy_data_view_dispatcher.test.ts | 90 +++---- .../packs/crud/tests/entity_data_view.test.ts | 148 +++++------ .../packs/crud/tests/entity_edit_form.test.ts | 99 +++---- .../crud/tests/entity_form_builder.test.ts | 84 +++--- .../packs/crud/tests/root_data_view.test.ts | 78 +++--- .../packs/crud/tests/text_data_filter.test.ts | 56 ++-- .../crud/tests/text_filter_widget.test.ts | 242 +++++++++--------- easydata.js/packs/crud/tests/utils.test.ts | 42 +-- .../packs/crud/tests/validators.test.ts | 58 ++--- 28 files changed, 888 insertions(+), 883 deletions(-) diff --git a/easydata.js/packs/core/tests/aggr_settings.test.ts b/easydata.js/packs/core/tests/aggr_settings.test.ts index e5d5e63c..f53095f6 100644 --- a/easydata.js/packs/core/tests/aggr_settings.test.ts +++ b/easydata.js/packs/core/tests/aggr_settings.test.ts @@ -10,7 +10,7 @@ describe('AggregationSettings', () => { let settings: AggregationSettings; beforeEach(() => { - // Создаем мок для AggregationColumnStore + // Create mock for AggregationColumnStore columnStore = mock({ getColumnIds: (from, to) => { const result = []; @@ -20,11 +20,11 @@ describe('AggregationSettings', () => { return result; }, validateColumns: (columns) => { - // Простая проверка - все колонки должны начинаться с 'col' + // Simple check - all columns should start with 'col' return columns.every(col => typeof col === 'string' && col.startsWith('col')); }, validateAggregate: (colId, funcId) => { - // Проверка - валидные функции: 'sum', 'avg', 'min', 'max', 'count' + // Check - valid functions: 'sum', 'avg', 'min', 'max', 'count' const validFuncs = ['sum', 'avg', 'min', 'max', 'count']; return typeof colId === 'string' && colId.startsWith('col') && @@ -126,18 +126,18 @@ describe('AggregationSettings', () => { }); it('should check if settings are valid', () => { - // Несуществующие группы и агрегаты -> невалидно + // No groups and aggregates -> invalid expect(settings.isValid()).toBe(false); - // Только группы -> невалидно + // Only groups -> invalid settings.addGroup({ columns: ['col1'] }); expect(settings.isValid()).toBe(false); - // Группы и агрегаты -> валидно + // Groups and aggregates -> valid settings.addAggregateColumn('col2', 'sum'); expect(settings.isValid()).toBe(true); - // Только агрегаты и тотал -> валидно + // Only aggregates and total -> valid settings.clear(); settings.addAggregateColumn('col1', 'sum'); settings.addGrandTotals(); diff --git a/easydata.js/packs/core/tests/aggr_structures.test.ts b/easydata.js/packs/core/tests/aggr_structures.test.ts index e489a0b3..77f2d166 100644 --- a/easydata.js/packs/core/tests/aggr_structures.test.ts +++ b/easydata.js/packs/core/tests/aggr_structures.test.ts @@ -11,7 +11,7 @@ import { DataGroup } from '../src/data/aggr_structures'; -// Тесты для AggregationColumnStore +// Tests for AggregationColumnStore describe('AggregationColumnStore Interface', () => { let columnStore: AggregationColumnStore; @@ -35,22 +35,22 @@ describe('AggregationColumnStore Interface', () => { }; }); - it('должен возвращать массив идентификаторов колонок в указанном диапазоне', () => { + it('should return an array of column identifiers in specified range', () => { const columns = columnStore.getColumnIds(1, 3); expect(columns).toBeArrayEqual(['col1', 'col2', 'col3']); }); - it('должен возвращать один идентификатор колонки, если указан только начальный индекс', () => { + it('should return a single column identifier if only starting index is specified', () => { const columns = columnStore.getColumnIds(5); expect(columns).toBeArrayEqual(['col5']); }); - it('должен проверять валидность колонок', () => { + it('should check the column validity', () => { expect(columnStore.validateColumns(['col1', 'col2'])).toBe(true); expect(columnStore.validateColumns(['col1', 'invalid'])).toBe(false); }); - it('должен проверять валидность агрегатной функции для колонки', () => { + it('should check the aggregate function validity for column', () => { expect(columnStore.validateAggregate('col1', 'sum')).toBe(true); expect(columnStore.validateAggregate('col2', 'avg')).toBe(true); expect(columnStore.validateAggregate('col3', 'invalid')).toBe(false); @@ -58,7 +58,7 @@ describe('AggregationColumnStore Interface', () => { }); }); -// Тесты для AggregatesContainer +// Tests for AggregatesContainer describe('AggregatesContainer Interface', () => { let container: AggregatesContainer; @@ -89,27 +89,27 @@ describe('AggregatesContainer Interface', () => { }; }); - it('должен сохранять данные агрегации для определенного уровня', () => { + it('should save aggregate data for a specific level', () => { const data = new Map(); data.set(JSON.stringify({col1: 'value1'}), {sum: 100, count: 10}); container.setAggregateData(1, data); - - // Проверим через getAggregateData + + // Check this through getAggregateData return container.getAggregateData(1, {col1: 'value1'}) .then(totals => { expect(totals).toBeObject({sum: 100, count: 10}); }); }); - it('должен возвращать пустой объект для несуществующего уровня', () => { + it('should return an empty object for a non-existent level', () => { return container.getAggregateData(999, {col1: 'value1'}) .then(totals => { expect(totals).toBeObject({}); }); }); - it('должен обновлять данные агрегации', () => { + it('should update aggregate data', () => { container.updateAggregateData(1, {col1: 'value1'}, {sum: 100}); container.updateAggregateData(1, {col1: 'value1'}, {sum: 200}); @@ -119,7 +119,7 @@ describe('AggregatesContainer Interface', () => { }); }); - it('должен создавать новый уровень при обновлении данных несуществующего уровня', () => { + it('should create a new level when updating data for a non-existent level', () => { container.updateAggregateData(2, {col2: 'value2'}, {avg: 50}); return container.getAggregateData(2, {col2: 'value2'}) @@ -129,14 +129,14 @@ describe('AggregatesContainer Interface', () => { }); }); -// Тесты для AggregatesCalculator +// Tests for AggregatesCalculator describe('AggregatesCalculator Interface', () => { let container: AggregatesContainer; let calculator: AggregatesCalculator; let needRecalculationValue: boolean; beforeEach(() => { - // Создаем мок для AggregatesContainer + // Create mock for AggregatesContainer container = { setAggregateData: mock(), getAggregateData: mock().mockImplementation(async () => ({})), @@ -145,7 +145,7 @@ describe('AggregatesCalculator Interface', () => { needRecalculationValue = true; - // Создаем мок для AggregatesCalculator + // Create a mock for AggregatesCalculator calculator = { getAggrContainer: () => container, @@ -160,7 +160,7 @@ describe('AggregatesCalculator Interface', () => { throw error; } - // Симулируем вычисление и получение результата + // Simulate calculation and obtaining result const result = { sum: 100, count: 10 }; if (options?.resultObtained) { options.resultObtained(result, 1); @@ -177,12 +177,12 @@ describe('AggregatesCalculator Interface', () => { }; }); - it('должен возвращать контейнер агрегатов', () => { + it('should return an aggregate container', () => { const result = calculator.getAggrContainer(); expect(result).toBe(container); }); - it('должен выполнять вычисление с вызовом callback-функций', async () => { + it('should perform a calculation with callback functions', async () => { const resultCallback = mock(); const errorCallback = mock(); @@ -196,7 +196,7 @@ describe('AggregatesCalculator Interface', () => { expect(calculator.needRecalculation()).toBe(false); }); - it('должен вызывать errorOccurred при ошибке вычисления', async () => { + it('should call the errorOccurred on calculation error', async () => { const resultCallback = mock(); const errorCallback = mock(); @@ -207,7 +207,7 @@ describe('AggregatesCalculator Interface', () => { errorOccurred: errorCallback }); } catch (error) { - // Ожидаем, что ошибка будет перехвачена + // Expect the error to be caught } expect(resultCallback).not.toHaveBeenCalled(); @@ -217,20 +217,20 @@ describe('AggregatesCalculator Interface', () => { expect(error.level).toBe(0); }); - it('должен возвращать needRecalculation=true после reset', () => { + it('should return needRecalculation=true after reset', () => { calculator.reset(); expect(calculator.needRecalculation()).toBe(true); }); - it('должен возвращать needRecalculation=false после успешного вычисления', async () => { + it('should return needRecalculation=false after successful calculation', async () => { await calculator.calculate(); expect(calculator.needRecalculation()).toBe(false); }); }); -// Тесты для структур данных +// Tests for data structures describe('Data Structures', () => { - it('должен правильно определять DataGroup', () => { + it('should correctly define a DataGroup', () => { const group: DataGroup = { name: 'TestGroup', columns: ['col1', 'col2'] @@ -240,7 +240,7 @@ describe('Data Structures', () => { expect(group.columns).toBeArrayEqual(['col1', 'col2']); }); - it('должен работать с GroupKey как с объектом', () => { + it('should work with GroupKey as an object', () => { const key: GroupKey = { col1: 'value1', col2: 100 @@ -249,8 +249,8 @@ describe('Data Structures', () => { expect(key.col1).toBe('value1'); expect(key.col2).toBe(100); }); - - it('должен работать с GroupTotals как с объектом', () => { + + it('should work with GroupTotals as an object', () => { const totals: GroupTotals = { sum: 500, avg: 100, @@ -262,7 +262,7 @@ describe('Data Structures', () => { expect(totals.count).toBe(5); }); - it('должен создавать AggrCalculationError с уровнем', () => { + it('should create an AggrCalculationError with a level', () => { const error = new Error('Test error') as AggrCalculationError; error.level = 2; @@ -270,7 +270,7 @@ describe('Data Structures', () => { expect(error.level).toBe(2); }); - it('должен создавать AggrCalculationOptions с опциями', () => { + it('should create an AggrCalculationOptions with options', () => { const options: AggrCalculationOptions = { maxLevel: 3, resultObtained: mock(), diff --git a/easydata.js/packs/core/tests/data_column.test.ts b/easydata.js/packs/core/tests/data_column.test.ts index 68d0126b..9ff3a713 100644 --- a/easydata.js/packs/core/tests/data_column.test.ts +++ b/easydata.js/packs/core/tests/data_column.test.ts @@ -4,7 +4,7 @@ import { DataColumn, DataColumnList, DataColumnDescriptor, ColumnAlignment, Data import { DataType } from '../src/types/data_type'; describe('ColumnAlignment enum', () => { - it('должен содержать правильные значения', () => { + it('should contain correct values', () => { expect(ColumnAlignment.None).toBe(0); expect(ColumnAlignment.Left).toBe(1); expect(ColumnAlignment.Center).toBe(2); @@ -13,7 +13,7 @@ describe('ColumnAlignment enum', () => { }); describe('DataColumn', () => { - it('должен создаваться с минимальными параметрами', () => { + it('should create a DataColumn based on minimal set of parameters', () => { const desc: DataColumnDescriptor = { id: 'test-id', label: 'Test Label' @@ -30,7 +30,7 @@ describe('DataColumn', () => { expect(column.calculatedWidth).toBe(0); }); - it('должен создаваться со всеми параметрами', () => { + it('should create a DataColumn with all parameters', () => { const style: DataColumnStyle = { alignment: ColumnAlignment.Right }; @@ -60,28 +60,28 @@ describe('DataColumn', () => { expect(column.description).toBe('Test description'); }); - it('должен выбрасывать ошибку при создании без опций', () => { + it('should throw an error when created without options', () => { expect(() => { - // @ts-ignore - Намеренно передаем неверные параметры для теста + // @ts-ignore - Intentionally passing invalid parameters for testing new DataColumn(null); }).toThrow("Options are required"); }); - it('должен выбрасывать ошибку при создании без id', () => { + it('should throw an error when created without id', () => { expect(() => { const desc: DataColumnDescriptor = { - // @ts-ignore - Намеренно пропускаем id для теста + // @ts-ignore - Intentionally omitting id for testing label: 'Test Label' }; new DataColumn(desc); }).toThrow("Field Id is required"); }); - it('должен выбрасывать ошибку при создании без метки', () => { + it('should throw an error when created without label', () => { expect(() => { const desc: DataColumnDescriptor = { id: 'test-id', - // @ts-ignore - Намеренно пропускаем label для теста + // @ts-ignore - Intentionally omitting label for testing }; new DataColumn(desc); }).toThrow("Label is required"); @@ -95,7 +95,7 @@ describe('DataColumnList', () => { columnList = new DataColumnList(); }); - it('должен быть пустым после создания', () => { + it('should be empty after creation', () => { expect(columnList.count).toBe(0); expect(columnList.getItems()).toBeArray(); expect(columnList.getItems()).toBeEmpty(); @@ -103,7 +103,7 @@ describe('DataColumnList', () => { expect(columnList.getDateColumnIndexes()).toBeEmpty(); }); - it('должен добавлять столбцы и возвращать индекс', () => { + it('should add columns and return the index', () => { const desc: DataColumnDescriptor = { id: 'column1', label: 'Column 1' @@ -116,7 +116,7 @@ describe('DataColumnList', () => { expect(columnList.get(0).id).toBe('column1'); }); - it('должен добавлять экземпляры DataColumn', () => { + it('should add instances of DataColumn', () => { const column = new DataColumn({ id: 'column1', label: 'Column 1' @@ -129,7 +129,7 @@ describe('DataColumnList', () => { expect(columnList.get(0)).toBe(column); }); - it('должен отслеживать индексы столбцов с датами', () => { + it('should track date column indexes', () => { columnList.add({ id: 'string-column', label: 'String Column', @@ -166,7 +166,7 @@ describe('DataColumnList', () => { expect(columnList.count).toBe(5); }); - it('должен получать столбец по индексу', () => { + it('should get a column by index', () => { columnList.add({ id: 'column1', label: 'Column 1' @@ -183,7 +183,7 @@ describe('DataColumnList', () => { expect(column.label).toBe('Column 2'); }); - it('должен возвращать null при получении несуществующего индекса', () => { + it('should return null when getting a non-existent index', () => { columnList.add({ id: 'column1', label: 'Column 1' @@ -196,7 +196,7 @@ describe('DataColumnList', () => { expect(columnOutOfRange).toBeNull(); }); - it('должен получать индекс столбца по ID', () => { + it('should return the index of a column by ID', () => { columnList.add({ id: 'column1', label: 'Column 1' @@ -212,7 +212,7 @@ describe('DataColumnList', () => { expect(columnList.getIndex('non-existent')).toBeUndefined(); }); - it('должен заменять столбец по индексу', () => { + it('should replace a column by index', () => { columnList.add({ id: 'column1', label: 'Column 1' @@ -229,7 +229,7 @@ describe('DataColumnList', () => { expect(columnList.get(0)).toBe(newColumn); }); - it('должен перемещать столбец на новую позицию', () => { + it('should move a column to a new position', () => { const col1 = new DataColumn({ id: 'column1', label: 'Column 1' @@ -256,7 +256,7 @@ describe('DataColumnList', () => { expect(columnList.get(2)).toBe(col1); }); - it('должен удалять столбец по индексу', () => { + it('should remove a column by index', () => { columnList.add({ id: 'column1', label: 'Column 1' @@ -279,7 +279,7 @@ describe('DataColumnList', () => { expect(columnList.get(1).id).toBe('column3'); }); - it('должен удалять столбцы с датами из индексов', () => { + it('should remove date columns from indexes', () => { columnList.add({ id: 'string-column', label: 'String Column', @@ -305,7 +305,7 @@ describe('DataColumnList', () => { expect(columnList.getDateColumnIndexes()).toBeArrayEqual([]); }); - it('должен очищать список столбцов', () => { + it('should clear the column list', () => { columnList.add({ id: 'column1', label: 'Column 1' diff --git a/easydata.js/packs/core/tests/data_row.test.ts b/easydata.js/packs/core/tests/data_row.test.ts index 5438520d..65b2b3eb 100644 --- a/easydata.js/packs/core/tests/data_row.test.ts +++ b/easydata.js/packs/core/tests/data_row.test.ts @@ -14,7 +14,7 @@ describe('DataRow', () => { columnList.add({ id: 'active', label: 'Active' }); }); - it('должен создаваться с колонками и значениями', () => { + it('should be created with columns and values', () => { const values = [1, 'John', 30, true]; const row = new DataRow(columnList, values); @@ -22,24 +22,24 @@ describe('DataRow', () => { expect(row.size()).toBe(4); }); - it('должен возвращать размер строки', () => { + it('should return row size', () => { const values = [1, 'John', 30, true]; const row = new DataRow(columnList, values); expect(row.size()).toBe(4); }); - it('должен возвращать копию массива значений', () => { + it('should return copy of values array', () => { const values = [1, 'John', 30, true]; const row = new DataRow(columnList, values); const result = row.toArray(); expect(result).toBeEqual(values); - expect(result).not.toBe(values); // Должен быть новый массив, а не ссылка на тот же самый + expect(result).not.toBe(values); // Should be new array, not reference to the same one }); - it('должен получать значение по индексу', () => { + it('should get value by index', () => { const values = [1, 'John', 30, true]; const row = new DataRow(columnList, values); @@ -49,7 +49,7 @@ describe('DataRow', () => { expect(row.getValue(3)).toBe(true); }); - it('должен получать значение по ID колонки', () => { + it('should get value by column ID', () => { const values = [1, 'John', 30, true]; const row = new DataRow(columnList, values); @@ -59,7 +59,7 @@ describe('DataRow', () => { expect(row.getValue('active')).toBe(true); }); - it('должен устанавливать значение по индексу', () => { + it('should set value by index', () => { const values = [1, 'John', 30, true]; const row = new DataRow(columnList, values); @@ -74,7 +74,7 @@ describe('DataRow', () => { expect(row.getValue(3)).toBe(false); }); - it('должен устанавливать значение по ID колонки', () => { + it('should set value by column ID', () => { const values = [1, 'John', 30, true]; const row = new DataRow(columnList, values); @@ -89,7 +89,7 @@ describe('DataRow', () => { expect(row.getValue('active')).toBe(false); }); - it('должен выбрасывать ошибку при получении значения по несуществующему ID колонки', () => { + it('should throw error when getting value by non-existent column ID', () => { const values = [1, 'John', 30, true]; const row = new DataRow(columnList, values); @@ -102,7 +102,7 @@ describe('DataRow', () => { }).toThrow("No column with id 'nonExistentId'"); }); - it('должен выбрасывать ошибку при получении значения по индексу вне диапазона', () => { + it('should throw error when getting value by index out of range', () => { const values = [1, 'John', 30, true]; const row = new DataRow(columnList, values); @@ -115,7 +115,7 @@ describe('DataRow', () => { }).toThrow("Out of range: 4"); }); - it('должен выбрасывать ошибку при установке значения по несуществующему ID колонки', () => { + it('should throw error when setting value by non-existent column ID', () => { const values = [1, 'John', 30, true]; const row = new DataRow(columnList, values); @@ -128,7 +128,7 @@ describe('DataRow', () => { }).toThrow("No column with id 'nonExistentId'"); }); - it('должен выбрасывать ошибку при установке значения по индексу вне диапазона', () => { + it('should throw error when setting value by index out of range', () => { const values = [1, 'John', 30, true]; const row = new DataRow(columnList, values); @@ -141,7 +141,7 @@ describe('DataRow', () => { }).toThrow("Out of range: 4"); }); - it('должен работать с пустым массивом значений', () => { + it('should work with empty values array', () => { const emptyValues: any[] = []; const row = new DataRow(columnList, emptyValues); diff --git a/easydata.js/packs/core/tests/easy_data_table.test.ts b/easydata.js/packs/core/tests/easy_data_table.test.ts index be909b58..3e618111 100644 --- a/easydata.js/packs/core/tests/easy_data_table.test.ts +++ b/easydata.js/packs/core/tests/easy_data_table.test.ts @@ -5,7 +5,7 @@ import { DataRow } from '../src/data/data_row'; import { DataType } from '../src/types/data_type'; describe('EasyDataTable', () => { - // Мок для DataLoader + // Mock for DataLoader class MockDataLoader implements DataLoader { private data: any[] = []; private totalCount: number; @@ -33,14 +33,14 @@ describe('EasyDataTable', () => { } } - // Базовые настройки колонок для тестов + // Basic column settings for tests const defaultColumns: DataColumnDescriptor[] = [ { id: 'id', label: 'ID', type: DataType.Int32 }, { id: 'name', label: 'Name', type: DataType.String }, { id: 'birthday', label: 'Birthday', type: DataType.Date } ]; - // Тестовые данные для строк + // Test data for rows const testData = [ { id: 1, name: 'John', birthday: '2000-01-01' }, { id: 2, name: 'Jane', birthday: '1995-05-15' }, @@ -48,7 +48,7 @@ describe('EasyDataTable', () => { { id: 4, name: 'Alice', birthday: '1990-07-20' } ]; - it('должен создаваться с настройками по умолчанию', () => { + it('should be created with default settings', () => { const table = new EasyDataTable(); expect(table).toBeDefined(); @@ -59,7 +59,7 @@ describe('EasyDataTable', () => { expect(table.getTotal()).toBe(0); }); - it('должен создаваться с пользовательскими настройками', () => { + it('should be created with custom settings', () => { const options: EasyDataTableOptions = { chunkSize: 500, elasticChunks: true, @@ -74,7 +74,7 @@ describe('EasyDataTable', () => { expect(table.columns.count).toBe(3); }); - it('должен добавлять колонки при инициализации', () => { + it('should add columns during initialization', () => { const table = new EasyDataTable({ columns: defaultColumns }); @@ -85,7 +85,7 @@ describe('EasyDataTable', () => { expect(table.columns.get(2).id).toBe('birthday'); }); - it('должен создавать строки данных из массива значений', () => { + it('should create data rows from array values', () => { const table = new EasyDataTable({ columns: defaultColumns }); @@ -98,7 +98,7 @@ describe('EasyDataTable', () => { expect(row.getValue('birthday') instanceof Date).toBe(true); }); - it('должен создавать строки из объектов данных', () => { + it('should create rows from data objects', () => { const table = new EasyDataTable({ columns: defaultColumns, rows: testData @@ -114,7 +114,7 @@ describe('EasyDataTable', () => { }); }); - it('должен корректно преобразовывать даты при создании строк', () => { + it('should correctly convert dates when creating rows', () => { const table = new EasyDataTable({ columns: [ { id: 'date', label: 'Date', type: DataType.Date }, @@ -138,7 +138,7 @@ describe('EasyDataTable', () => { expect(row.getValue('date').getDate()).toBe(15); }); - it('должен получать строки по смещению и лимиту', () => { + it('should get rows by offset and limit', () => { const table = new EasyDataTable({ columns: defaultColumns, rows: testData @@ -151,7 +151,7 @@ describe('EasyDataTable', () => { }); }); - it('должен получать строки по странице и размеру страницы', () => { + it('should get rows by page and page size', () => { const table = new EasyDataTable({ columns: defaultColumns, rows: testData @@ -164,7 +164,7 @@ describe('EasyDataTable', () => { }); }); - it('должен возвращать пустой массив строк при запросе за пределами данных', () => { + it('should return empty array of rows when requesting beyond data', () => { const table = new EasyDataTable({ columns: defaultColumns, rows: testData @@ -176,7 +176,7 @@ describe('EasyDataTable', () => { }); }); - it('должен получать одиночную строку по индексу', () => { + it('should get single row by index', () => { const table = new EasyDataTable({ columns: defaultColumns, rows: testData @@ -189,7 +189,7 @@ describe('EasyDataTable', () => { }); }); - it('должен возвращать null при запросе несуществующей строки', () => { + it('should return null при запросе несуществующей строки', () => { const table = new EasyDataTable({ columns: defaultColumns, rows: testData @@ -200,7 +200,7 @@ describe('EasyDataTable', () => { }); }); - it('должен загружать данные из DataLoader', () => { + it('should load data from DataLoader', () => { const loader = new MockDataLoader(testData, 10); const table = new EasyDataTable({ columns: defaultColumns, @@ -210,9 +210,9 @@ describe('EasyDataTable', () => { return table.getRows({ offset: 0, limit: 2 }).then(rows => { expect(rows.length).toBe(2); expect(table.getTotal()).toBe(10); - expect(table.getCachedCount()).toBe(4); // весь тестовый набор в кеше + expect(table.getCachedCount()).toBe(4); // entire test dataset is cached - // Проверка кеширования + // Check кеширования return table.getRows({ offset: 2, limit: 2 }).then(moreRows => { expect(moreRows.length).toBe(2); expect(table.getCachedCount()).toBe(4); @@ -220,7 +220,7 @@ describe('EasyDataTable', () => { }); }); - it('должен работать с эластичными чанками', () => { + it('should work with elastic chunks', () => { const loader = new MockDataLoader(testData); const table = new EasyDataTable({ chunkSize: 10, @@ -229,12 +229,12 @@ describe('EasyDataTable', () => { }); return table.getRows({ offset: 0, limit: 2 }).then(() => { - expect(table.getTotal()).toBe(4); // Всего 4 записи из тестовых данных + expect(table.getTotal()).toBe(4); // Total 4 records from test data expect(table.totalIsKnown()).toBe(true); }); }); - it('должен обновлять chunkSize и очищать кеш', () => { + it('should update chunkSize and clear cache', () => { const table = new EasyDataTable({ chunkSize: 100, rows: testData @@ -249,7 +249,7 @@ describe('EasyDataTable', () => { expect(table.getCachedCount()).toBe(0); // Кеш очищен }); - it('должен выполнять колбэк onUpdate при обновлении данных', () => { + it('should execute onUpdate callback on data update', () => { let updateCalled = false; let lastTable: EasyDataTable | undefined; @@ -269,7 +269,7 @@ describe('EasyDataTable', () => { expect(lastTable).toBe(table); }); - it('должен очищать все данные при вызове clear', () => { + it('should clear all data on clear call', () => { const table = new EasyDataTable({ columns: defaultColumns, rows: testData @@ -285,20 +285,20 @@ describe('EasyDataTable', () => { expect(table.getTotal()).toBe(0); }); - it('должен выбрасывать ошибку при запросе данных без loader', async () => { + it('should throw error при запросе данных без loader', async () => { const table = new EasyDataTable(); // Нет данных в кеше и нет loader try { await table.getRows({ offset: 0, limit: 10 }); - fail('Должна быть выброшена ошибка'); + fail('Error should have been thrown'); } catch (error) { expect(error).toContain('Loader is not defined'); } }); - it('должен устанавливать общее количество строк вручную', () => { + it('should set total row count manually', () => { const table = new EasyDataTable(); table.setTotal(100); diff --git a/easydata.js/packs/core/tests/easy_guid.test.ts b/easydata.js/packs/core/tests/easy_guid.test.ts index 2f9eba47..68d18aa5 100644 --- a/easydata.js/packs/core/tests/easy_guid.test.ts +++ b/easydata.js/packs/core/tests/easy_guid.test.ts @@ -1,48 +1,48 @@ import { EasyGuid } from '../src/utils/easy_guid'; describe('EasyGuid', () => { - it('должен генерировать строку в формате GUID', () => { + it('should генерировать строку в формате GUID', () => { const guid = EasyGuid.newGuid(); - // Проверяем, что возвращаемое значение - строка + // Check, что возвращаемое значение - строка expect(typeof guid).toBe('string'); - // Проверяем длину GUID (должна быть 36 символов включая дефисы) + // Check длину GUID (должна быть 36 символов включая дефисы) expect(guid.length).toBe(36); - // Проверяем формат GUID с помощью регулярного выражения + // Check формат GUID с помощью регулярного выражения // Формат: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx, где y - это 8, 9, a или b const guidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; expect(guidRegex.test(guid)).toBe(true); - // Проверяем, что 4-й символ в третьей группе - это 4 (версия GUID) + // Check, что 4-й символ в третьей группе - это 4 (версия GUID) expect(guid.charAt(14)).toBe('4'); }); - it('должен генерировать уникальные GUID при каждом вызове', () => { - const guidCount = 1000; // Проверка на большом количестве GUIDов + it('should генерировать уникальные GUID при каждом вызове', () => { + const guidCount = 1000; // Check на большом количестве GUIDов const guidSet = new Set(); for (let i = 0; i < guidCount; i++) { guidSet.add(EasyGuid.newGuid()); } - // Если все GUIDы уникальны, размер Set должен равняться guidCount + // Если все GUIDы уникальны, размер Set should равняться guidCount expect(guidSet.size).toBe(guidCount); }); - it('должен генерировать GUIDы с правильными позициями дефисов', () => { + it('should генерировать GUIDы с правильными позициями дефисов', () => { const guid = EasyGuid.newGuid(); - // Проверяем позиции дефисов + // Check позиции дефисов expect(guid.charAt(8)).toBe('-'); expect(guid.charAt(13)).toBe('-'); expect(guid.charAt(18)).toBe('-'); expect(guid.charAt(23)).toBe('-'); }); - it('должен генерировать GUIDы с правильным значением в первом разряде третьей группы', () => { - // Согласно спецификации GUID первый разряд третьей группы должен быть 8, 9, A или B + it('should генерировать GUIDы с правильным значением в первом разряде третьей группы', () => { + // Согласно спецификации GUID первый разряд третьей группы should быть 8, 9, A или B const allowedChars = ['8', '9', 'a', 'b', 'A', 'B']; for (let i = 0; i < 100; i++) { diff --git a/easydata.js/packs/core/tests/event_emitter.test.ts b/easydata.js/packs/core/tests/event_emitter.test.ts index 9e89b0f9..18838177 100644 --- a/easydata.js/packs/core/tests/event_emitter.test.ts +++ b/easydata.js/packs/core/tests/event_emitter.test.ts @@ -9,7 +9,7 @@ describe('EventEmitter', () => { eventEmitter = new EventEmitter(source); }); - it('должен создаваться с указанным источником', () => { + it('should создаваться с указанным источником', () => { expect(eventEmitter).toBeDefined(); // Нам нужно вызвать событие, чтобы проверить источник @@ -21,7 +21,7 @@ describe('EventEmitter', () => { eventEmitter.fire('testEvent'); }); - it('должен возвращать ID подписки при подписке на событие', () => { + it('should return ID подписки при подписке на событие', () => { const callback = (event: EqEvent) => {}; const subscriptionId = eventEmitter.subscribe('testEvent', callback); @@ -31,7 +31,7 @@ describe('EventEmitter', () => { expect(subscriptionId.length).toBeGreaterThan(0); }); - it('должен вызывать колбэк при активации события', () => { + it('should вызывать колбэк при активации события', () => { let callbackCalled = false; const testData = { test: 'data' }; @@ -48,7 +48,7 @@ describe('EventEmitter', () => { expect(callbackCalled).toBe(true); }); - it('должен вызывать несколько колбэков для одного события', () => { + it('should вызывать несколько колбэков для одного события', () => { let callCount = 0; const callback1 = () => { callCount++; }; @@ -64,7 +64,7 @@ describe('EventEmitter', () => { expect(callCount).toBe(3); }); - it('должен вызывать только колбэки для указанного типа события', () => { + it('should вызывать только колбэки для указанного типа события', () => { let event1CallCount = 0; let event2CallCount = 0; @@ -77,7 +77,7 @@ describe('EventEmitter', () => { expect(event2CallCount).toBe(0); }); - it('не должен вызывать колбэк после отписки', () => { + it('не should вызывать колбэк после отписки', () => { let callbackCalled = false; const callback = () => { callbackCalled = true; }; @@ -89,21 +89,21 @@ describe('EventEmitter', () => { expect(callbackCalled).toBe(false); }); - it('должен правильно работать при отписке несуществующего ID', () => { + it('should правильно работать при отписке несуществующего ID', () => { // Не должно выбрасывать исключение expect(() => { eventEmitter.unsubscribe('testEvent', 'non-existent-id'); }).not.toThrow(); }); - it('должен правильно работать при попытке активации несуществующего события', () => { + it('should правильно работать при попытке активации несуществующего события', () => { // Не должно выбрасывать исключение expect(() => { eventEmitter.fire('non-existent-event'); }).not.toThrow(); }); - it('должен откладывать выполнение события с параметром postpone', (done) => { + it('should откладывать выполнение события с параметром postpone', (done) => { let callbackCalled = false; const callback = () => { @@ -115,11 +115,11 @@ describe('EventEmitter', () => { eventEmitter.fire('testEvent', null, 50); // отложить на 50 мс - // Сразу после вызова fire колбэк еще не должен быть вызван + // Сразу после вызова fire колбэк еще не should быть вызван expect(callbackCalled).toBe(false); }); - it('должен входить в тихий режим и выходить из него', () => { + it('should входить в тихий режим и выходить из него', () => { let callbackCalled = false; const callback = () => { callbackCalled = true; }; @@ -133,7 +133,7 @@ describe('EventEmitter', () => { eventEmitter.enterSilentMode(); expect(eventEmitter.isSilent()).toBe(true); - // В тихом режиме колбэк не должен вызываться + // В тихом режиме колбэк не should вызываться eventEmitter.fire('testEvent'); expect(callbackCalled).toBe(false); @@ -141,12 +141,12 @@ describe('EventEmitter', () => { eventEmitter.exitSilentMode(); expect(eventEmitter.isSilent()).toBe(false); - // После выхода из тихого режима колбэк должен вызываться + // После выхода из тихого режима колбэк should вызываться eventEmitter.fire('testEvent'); expect(callbackCalled).toBe(true); }); - it('должен поддерживать вложенный тихий режим', () => { + it('should поддерживать вложенный тихий режим', () => { let callbackCalled = false; const callback = () => { callbackCalled = true; }; @@ -162,7 +162,7 @@ describe('EventEmitter', () => { eventEmitter.exitSilentMode(); expect(eventEmitter.isSilent()).toBe(true); - // В тихом режиме колбэк не должен вызываться + // В тихом режиме колбэк не should вызываться eventEmitter.fire('testEvent'); expect(callbackCalled).toBe(false); @@ -170,12 +170,12 @@ describe('EventEmitter', () => { eventEmitter.exitSilentMode(); expect(eventEmitter.isSilent()).toBe(false); - // После полного выхода из тихого режима колбэк должен вызываться + // После полного выхода из тихого режима колбэк should вызываться eventEmitter.fire('testEvent'); expect(callbackCalled).toBe(true); }); - it('должен принудительно вызывать события в тихом режиме с параметром force', () => { + it('should принудительно вызывать события в тихом режиме с параметром force', () => { let callbackCalled = false; const callback = () => { callbackCalled = true; }; @@ -186,11 +186,11 @@ describe('EventEmitter', () => { eventEmitter.enterSilentMode(); expect(eventEmitter.isSilent()).toBe(true); - // Обычный вызов не должен срабатывать + // Обычный вызов не should срабатывать eventEmitter.fire('testEvent'); expect(callbackCalled).toBe(false); - // Принудительный вызов должен сработать даже в тихом режиме + // Принудительный вызов should сработать даже в тихом режиме eventEmitter.fire('testEvent', null, 0, true); expect(callbackCalled).toBe(true); }); diff --git a/easydata.js/packs/core/tests/http_action_result.test.ts b/easydata.js/packs/core/tests/http_action_result.test.ts index b0f3d491..5f2b1b79 100644 --- a/easydata.js/packs/core/tests/http_action_result.test.ts +++ b/easydata.js/packs/core/tests/http_action_result.test.ts @@ -7,7 +7,7 @@ describe('HttpActionResult', () => { let actionResult: HttpActionResult; beforeEach(() => { - // Создаем мок для HttpRequest + // Create mock for HttpRequest mockRequest = { url: 'https://test.com/api', method: 'GET' @@ -16,23 +16,23 @@ describe('HttpActionResult', () => { // Создаем Promise, который успешно завершается со значением 'success' mockPromise = Promise.resolve('success'); - // Создаем экземпляр HttpActionResult + // Create instance HttpActionResult actionResult = new HttpActionResult(mockRequest, mockPromise); }); - it('должен сохранять request и promise переданные в конструктор', () => { + it('should сохранять request и promise переданные в конструктор', () => { expect(actionResult.getRequest()).toBe(mockRequest); expect(actionResult.getPromise()).toBe(mockPromise); }); - it('должен возвращать исходный request через getRequest', () => { + it('should return исходный request через getRequest', () => { const request = actionResult.getRequest(); expect(request).toBe(mockRequest); expect(request.url).toBe('https://test.com/api'); expect(request.method).toBe('GET'); }); - it('должен возвращать исходный promise через getPromise', () => { + it('should return исходный promise через getPromise', () => { const promise = actionResult.getPromise(); expect(promise).toBe(mockPromise); @@ -41,7 +41,7 @@ describe('HttpActionResult', () => { }); }); - it('должен реализовать метод then и получить значение из promise', () => { + it('should реализовать метод then и получить значение из promise', () => { let resultValue = ''; return actionResult.then(value => { @@ -53,7 +53,7 @@ describe('HttpActionResult', () => { }); }); - it('должен правильно обрабатывать rejected promise в методе then', () => { + it('should правильно обрабатывать rejected promise в методе then', () => { // Создаем rejected promise const rejectedPromise = Promise.reject(new Error('error message')); const rejectedResult = new HttpActionResult(mockRequest, rejectedPromise); @@ -64,7 +64,7 @@ describe('HttpActionResult', () => { return rejectedResult .then( () => { - // Этот код не должен выполниться + // Этот код не should выполниться errorInThen = true; }, (error) => { @@ -81,7 +81,7 @@ describe('HttpActionResult', () => { }); }); - it('должен реализовать метод catch и обрабатывать ошибки', () => { + it('should реализовать метод catch и обрабатывать ошибки', () => { // Создаем rejected promise const rejectedPromise = Promise.reject(new Error('test error')); const rejectedResult = new HttpActionResult(mockRequest, rejectedPromise); @@ -100,7 +100,7 @@ describe('HttpActionResult', () => { }); }); - it('должен реализовать метод finally и вызвать его независимо от результата', () => { + it('should реализовать метод finally и вызвать его независимо от результата', () => { let finallyCalled = false; return actionResult @@ -112,7 +112,7 @@ describe('HttpActionResult', () => { }); }); - it('должен вызвать finally даже при ошибке', () => { + it('should вызвать finally даже при ошибке', () => { // Создаем rejected promise const rejectedPromise = Promise.reject(new Error('test error')); const rejectedResult = new HttpActionResult(mockRequest, rejectedPromise); @@ -128,7 +128,7 @@ describe('HttpActionResult', () => { }); }); - it('должен поддерживать цепочку методов then, catch и finally', () => { + it('should поддерживать цепочку методов then, catch и finally', () => { let thenCalled = false; let catchCalled = false; let finallyCalled = false; @@ -140,7 +140,7 @@ describe('HttpActionResult', () => { return value.toUpperCase(); }) .catch(() => { - // Не должен быть вызван + // Не should быть вызван catchCalled = true; return 'error'; }) diff --git a/easydata.js/packs/core/tests/http_client.test.ts b/easydata.js/packs/core/tests/http_client.test.ts index 5adb7478..d8611cb6 100644 --- a/easydata.js/packs/core/tests/http_client.test.ts +++ b/easydata.js/packs/core/tests/http_client.test.ts @@ -14,7 +14,7 @@ describe('HttpClient', () => { beforeEach(() => { requests = []; - // Мок для XMLHttpRequest + // Mock for XMLHttpRequest xhrMock = { open: mock(), send: mock(), @@ -27,7 +27,7 @@ describe('HttpClient', () => { onerror: null }; - // Сохраняем оригинальный XMLHttpRequest и заменяем его на мок + // Save оригинальный XMLHttpRequest и заменяем его на мок originalXMLHttpRequest = window.XMLHttpRequest; window.XMLHttpRequest = mock().mockImplementation(() => { const xhr = xhrMock; @@ -39,17 +39,17 @@ describe('HttpClient', () => { }); afterEach(() => { - // Восстанавливаем оригинальный XMLHttpRequest + // Restore оригинальный XMLHttpRequest window.XMLHttpRequest = originalXMLHttpRequest; }); - it('должен создаваться с дефолтными настройками', () => { + it('should создаваться с дефолтными настройками', () => { expect(httpClient.defaultHeaders).toBeObject({}); expect(httpClient.customPayload).toBeUndefined(); expect(httpClient.responseBody).toBeUndefined(); }); - it('должен выполнять GET запрос с правильными параметрами', () => { + it('should выполнять GET запрос с правильными параметрами', () => { const url = 'https://test.com/api/data'; const options = { headers: { 'X-Custom-Header': 'test' }, @@ -65,15 +65,15 @@ describe('HttpClient', () => { true ); - // Проверяем установку заголовков + // Check установку заголовков expect(xhrMock.setRequestHeader).toHaveBeenCalledWith('X-Custom-Header', 'test'); expect(xhrMock.setRequestHeader).toHaveBeenCalledWith('Content-Type', 'application/json'); - // Проверяем вызов send + // Check вызов send expect(xhrMock.send).toHaveBeenCalledWith(null); }); - it('должен выполнять POST запрос с данными', () => { + it('should выполнять POST запрос с данными', () => { const url = 'https://test.com/api/data'; const data = { name: 'John', age: 30 }; @@ -84,7 +84,7 @@ describe('HttpClient', () => { expect(xhrMock.send).toHaveBeenCalledWith(JSON.stringify(data)); }); - it('должен выполнять PUT запрос с данными', () => { + it('should выполнять PUT запрос с данными', () => { const url = 'https://test.com/api/data/1'; const data = { name: 'Updated Name', age: 31 }; @@ -95,7 +95,7 @@ describe('HttpClient', () => { expect(xhrMock.send).toHaveBeenCalledWith(JSON.stringify(data)); }); - it('должен выполнять DELETE запрос', () => { + it('should выполнять DELETE запрос', () => { const url = 'https://test.com/api/data/1'; httpClient.delete(url); @@ -105,7 +105,7 @@ describe('HttpClient', () => { expect(xhrMock.send).toHaveBeenCalledWith(null); }); - it('должен добавлять defaultHeaders к запросу', () => { + it('should добавлять defaultHeaders к запросу', () => { httpClient.defaultHeaders = { 'Authorization': 'Bearer test-token', 'X-Api-Key': 'api-key' @@ -117,7 +117,7 @@ describe('HttpClient', () => { expect(xhrMock.setRequestHeader).toHaveBeenCalledWith('X-Api-Key', 'api-key'); }); - it('должен добавлять customPayload к данным запроса', () => { + it('should добавлять customPayload к данным запроса', () => { const data = { name: 'John' }; httpClient.customPayload = ['custom-payload-value']; @@ -129,7 +129,7 @@ describe('HttpClient', () => { })); }); - it('должен вызывать onRequest после формирования запроса', () => { + it('should вызывать onRequest после формирования запроса', () => { const onRequestMock = mock((request: HttpRequest) => { // Изменяем запрос в обработчике request.url = request.url + '/modified'; @@ -142,14 +142,14 @@ describe('HttpClient', () => { expect(xhrMock.open).toHaveBeenCalledWith('GET', 'https://test.com/api/data/modified', true); }); - it('должен выбрасывать предупреждение при использовании устаревшего beforeEachRequest', () => { + it('should выбрасывать предупреждение при использовании устаревшего beforeEachRequest', () => { expect(1).toBe(1); // Замените на реальный тест, если нужно }); - it('должен обрабатывать успешный ответ с JSON данными', async () => { + it('should обрабатывать успешный ответ с JSON данными', async () => { const responsePromise = httpClient.get('https://test.com/api/data'); - // Эмулируем успешный ответ + // Emulate успешный ответ xhrMock.onreadystatechange(); const response = await responsePromise; @@ -157,22 +157,22 @@ describe('HttpClient', () => { expect(httpClient.responseBody).toBeObject({ success: true, data: 'test' }); }); - it('должен вызывать onResponse при успешном ответе', () => { + it('should вызывать onResponse при успешном ответе', () => { const onResponseMock = mock(); httpClient.onResponse = onResponseMock; const responsePromise = httpClient.get('https://test.com/api/data'); - // Эмулируем успешный ответ + // Emulate успешный ответ xhrMock.onreadystatechange(); expect(onResponseMock).toHaveBeenCalledWith(xhrMock); }); - it('должен обрабатывать ошибки сети', async () => { + it('should обрабатывать ошибки сети', async () => { const responsePromise = httpClient.get('https://test.com/api/data'); - // Эмулируем ошибку сети + // Emulate ошибку сети xhrMock.status = 0; xhrMock.onreadystatechange(); @@ -187,10 +187,10 @@ describe('HttpClient', () => { } }); - it('должен обрабатывать HTTP ошибки', async () => { + it('should обрабатывать HTTP ошибки', async () => { const responsePromise = httpClient.get('https://test.com/api/data'); - // Эмулируем HTTP ошибку + // Emulate HTTP ошибку xhrMock.status = 404; xhrMock.responseText = '{"message":"Resource not found"}'; xhrMock.onreadystatechange(); @@ -206,10 +206,10 @@ describe('HttpClient', () => { } }); - it('должен обрабатывать HTTP ошибку без сообщения', async () => { + it('should обрабатывать HTTP ошибку без сообщения', async () => { const responsePromise = httpClient.get('https://test.com/api/data'); - // Эмулируем HTTP ошибку без поля message в ответе + // Emulate HTTP ошибку без поля message в ответе xhrMock.status = 404; xhrMock.responseText = '{}'; xhrMock.onreadystatechange(); @@ -225,7 +225,7 @@ describe('HttpClient', () => { } }); - it('должен обрабатывать ответы с разными типами данных', async () => { + it('should обрабатывать ответы с разными типами данных', async () => { // Тестируем text ответ xhrMock.getResponseHeader.mockReturnValue('text/plain'); xhrMock.responseText = 'Plain text response'; @@ -238,7 +238,7 @@ describe('HttpClient', () => { expect(httpClient.responseBody).toBe('Plain text response'); }); - it('должен поддерживать параметры responseType', () => { + it('should поддерживать параметры responseType', () => { httpClient.get('https://test.com/api/binary', { responseType: 'arraybuffer' }); @@ -246,7 +246,7 @@ describe('HttpClient', () => { expect(xhrMock.responseType).toBe('arraybuffer'); }); - it('должен возвращать экземпляр HttpActionResult', () => { + it('should return экземпляр HttpActionResult', () => { const result = httpClient.get('https://test.com/api/data'); expect(result).toBeInstanceOf(HttpActionResult); @@ -254,7 +254,7 @@ describe('HttpClient', () => { expect(result.getPromise()).toBeDefined(); }); - it('должен корректно обрабатывать форм-данные', () => { + it('should correctly handle форм-данные', () => { const formData = new FormData(); formData.append('file', new Blob(['test content'], { type: 'text/plain' })); @@ -267,11 +267,11 @@ describe('HttpClient', () => { .find(call => call[0] === 'Content-Type'); expect(contentTypeHeader).toBeUndefined(); - // Проверяем что данные отправляются как есть + // Check что данные отправляются как есть expect(xhrMock.send).toHaveBeenCalledWith(formData); }); - it('должен корректно формировать URL с query параметрами', () => { + it('should correctly формировать URL с query параметрами', () => { httpClient.get('https://test.com/api/data', { queryParams: { id: 123, diff --git a/easydata.js/packs/core/tests/http_request.test.ts b/easydata.js/packs/core/tests/http_request.test.ts index 625311d4..38d4be45 100644 --- a/easydata.js/packs/core/tests/http_request.test.ts +++ b/easydata.js/packs/core/tests/http_request.test.ts @@ -8,7 +8,7 @@ describe('HttpRequest', () => { let requestDescriptor: HttpRequestDescriptor; beforeEach(() => { - // Мок для XMLHttpRequest + // Mock for XMLHttpRequest xhrMock = { open: mock(), abort: mock(), @@ -30,7 +30,7 @@ describe('HttpRequest', () => { }; }); - it('должен инициализировать запрос с переданными параметрами', () => { + it('should инициализировать запрос с переданными параметрами', () => { const request = new HttpRequest(xhrMock, requestDescriptor); expect(request.method).toBe(HttpMethod.GET); @@ -38,7 +38,7 @@ describe('HttpRequest', () => { expect(request.data).toBeUndefined(); }); - it('должен сохранять переданные данные', () => { + it('should сохранять переданные данные', () => { const data = { test: 'value' }; requestDescriptor.data = data; @@ -47,7 +47,7 @@ describe('HttpRequest', () => { expect(request.data).toBe(data); }); - it('должен добавлять заголовок с помощью setHeader', () => { + it('should добавлять заголовок с помощью setHeader', () => { const request = new HttpRequest(xhrMock, requestDescriptor); request.setHeader('Authorization', 'Bearer token123'); @@ -56,7 +56,7 @@ describe('HttpRequest', () => { expect(xhrMock.setRequestHeader).toHaveBeenCalledWith('Authorization', 'Bearer token123'); }); - it('должен добавлять параметры запроса с помощью setQueryParam', () => { + it('should добавлять параметры запроса с помощью setQueryParam', () => { const request = new HttpRequest(xhrMock, requestDescriptor); request.setQueryParam('id', '123'); @@ -65,7 +65,7 @@ describe('HttpRequest', () => { expect(xhrMock.open).toHaveBeenCalledWith('GET', 'https://test.com/api?id=123', true); }); - it('должен правильно объединять несколько параметров запроса', () => { + it('should правильно объединять несколько параметров запроса', () => { requestDescriptor.queryParams = { 'id': '123', 'name': 'test' @@ -77,7 +77,7 @@ describe('HttpRequest', () => { expect(xhrMock.open).toHaveBeenCalledWith('GET', 'https://test.com/api?id=123&name=test', true); }); - it('должен возвращать XMLHttpRequest через getXMLHttpRequest', () => { + it('should return XMLHttpRequest через getXMLHttpRequest', () => { const request = new HttpRequest(xhrMock, requestDescriptor); const xhr = request.getXMLHttpRequest(); @@ -85,7 +85,7 @@ describe('HttpRequest', () => { expect(xhr).toBe(xhrMock); }); - it('должен парсить заголовки ответа через getResponseHeaders', () => { + it('should парсить заголовки ответа через getResponseHeaders', () => { xhrMock.readyState = xhrMock.HEADERS_RECEIVED; const request = new HttpRequest(xhrMock, requestDescriptor); @@ -97,7 +97,7 @@ describe('HttpRequest', () => { }); }); - it('должен возвращать пустой объект заголовков когда readyState не HEADERS_RECEIVED', () => { + it('should return пустой объект заголовков когда readyState не HEADERS_RECEIVED', () => { xhrMock.readyState = xhrMock.UNSENT; const request = new HttpRequest(xhrMock, requestDescriptor); @@ -106,7 +106,7 @@ describe('HttpRequest', () => { expect(headers).toBeObject({}); }); - it('должен открывать запрос с правильными параметрами', () => { + it('should открывать запрос с правильными параметрами', () => { const request = new HttpRequest(xhrMock, requestDescriptor); request.open(); @@ -116,7 +116,7 @@ describe('HttpRequest', () => { expect(xhrMock.setRequestHeader).toHaveBeenCalledWith('Content-Type', 'application/json'); }); - it('не должен открывать запрос повторно если он уже открыт', () => { + it('не should открывать запрос повторно если он уже открыт', () => { xhrMock.readyState = 1; // OPENED const request = new HttpRequest(xhrMock, requestDescriptor); @@ -125,7 +125,7 @@ describe('HttpRequest', () => { expect(xhrMock.open).not.toHaveBeenCalled(); }); - it('должен вызывать abort у XMLHttpRequest при вызове abort', () => { + it('should вызывать abort у XMLHttpRequest при вызове abort', () => { const request = new HttpRequest(xhrMock, requestDescriptor); request.abort(); @@ -133,19 +133,19 @@ describe('HttpRequest', () => { expect(xhrMock.abort).toHaveBeenCalled(); }); - it('должен корректно кодировать URL с параметрами запроса', () => { + it('should correctly кодировать URL с параметрами запроса', () => { const request = new HttpRequest(xhrMock, requestDescriptor); request.setQueryParam('name', 'John Doe'); request.setQueryParam('tags', 'tag1,tag2'); request.open(); - // URL должен быть закодирован правильно + // URL should быть закодирован правильно expect(xhrMock.open).toHaveBeenCalledWith('GET', 'https://test.com/api?name=John%20Doe&tags=tag1%2Ctag2', true); }); - it('должен поддерживать различные HTTP методы', () => { + it('should поддерживать различные HTTP методы', () => { // Тестируем метод POST requestDescriptor.method = HttpMethod.POST; let request = new HttpRequest(xhrMock, requestDescriptor); diff --git a/easydata.js/packs/core/tests/i18n.test.ts b/easydata.js/packs/core/tests/i18n.test.ts index 1e8db278..e8881293 100644 --- a/easydata.js/packs/core/tests/i18n.test.ts +++ b/easydata.js/packs/core/tests/i18n.test.ts @@ -5,15 +5,15 @@ import { DataType } from '../src/types/data_type'; describe('i18n module', () => { beforeEach(() => { - // Сбрасываем локали перед каждым тестом + // Reset локали перед каждым тестом i18n.resetLocales(); }); - it('должен иметь правильную текущую локаль по умолчанию', () => { + it('should иметь правильную текущую локаль по умолчанию', () => { expect(i18n.getCurrentLocale()).toBe('en-US'); }); - it('должен возвращать список доступных локалей', () => { + it('should return список доступных локалей', () => { // По умолчанию должна быть только одна локаль const locales = i18n.getLocales(); expect(locales.length).toBe(1); @@ -31,7 +31,7 @@ describe('i18n module', () => { } }); - // Теперь должно быть две локали, отсортированные по englishName + // Теперь should be две локали, отсортированные по englishName const updatedLocales = i18n.getLocales(); expect(updatedLocales.length).toBe(2); expect(updatedLocales[0].locale).toBe('en-US'); @@ -40,8 +40,8 @@ describe('i18n module', () => { expect(updatedLocales[1].englishName).toBe('Ukrainian'); }); - it('должен получать тексты из текущей локали', () => { - // Проверка стандартных текстов в en-US + it('should получать тексты из текущей локали', () => { + // Check стандартных текстов в en-US expect(i18n.getText('ButtonOK')).toBe('OK'); expect(i18n.getText('ButtonCancel')).toBe('Cancel'); expect(i18n.getText('Yes')).toBe('Yes'); @@ -67,7 +67,7 @@ describe('i18n module', () => { expect(i18n.getText('No')).toBe('Nein'); }); - it('должен работать с вложенными текстовыми ресурсами', () => { + it('should работать с вложенными текстовыми ресурсами', () => { i18n.updateLocaleTexts({ Menu: { File: 'File', @@ -86,7 +86,7 @@ describe('i18n module', () => { expect(i18n.getText('Menu', 'Settings', 'Advanced')).toBe('Advanced'); }); - it('должен обновлять тексты для текущей локали', () => { + it('should обновлять тексты для текущей локали', () => { // Обновление текстов текущей локали i18n.updateLocaleTexts({ ButtonOK: 'Okay', @@ -94,16 +94,16 @@ describe('i18n module', () => { NewText: 'Brand new text' }); - // Проверка обновленных текстов + // Check обновленных текстов expect(i18n.getText('ButtonOK')).toBe('Okay'); expect(i18n.getText('ButtonCancel')).toBe('Dismiss'); expect(i18n.getText('NewText')).toBe('Brand new text'); - // Проверка что неизменные тексты остались прежними + // Check что неизменные тексты остались прежними expect(i18n.getText('Yes')).toBe('Yes'); }); - it('должен обновлять тексты по умолчанию для всех локалей', () => { + it('should обновлять тексты по умолчанию для всех локалей', () => { // Добавим новую локаль i18n.addLocale('fr-FR', { englishName: 'French', @@ -119,64 +119,64 @@ describe('i18n module', () => { NewGlobalText: 'New text for all locales' }); - // Проверяем английскую локаль + // Check английскую локаль i18n.setCurrentLocale('en-US'); expect(i18n.getText('NewGlobalText')).toBe('New text for all locales'); - // Проверяем французскую локаль + // Check французскую локаль i18n.setCurrentLocale('fr-FR'); expect(i18n.getText('NewGlobalText')).toBe('New text for all locales'); // Убеждаемся что специфичные для локали тексты остались expect(i18n.getText('ButtonCancel')).toBe('Annuler'); }); - it('должен работать с настройками локали', () => { + it('should работать с настройками локали', () => { const settings = i18n.getLocaleSettings(); - // Проверка форматов даты и времени + // Check форматов даты и времени expect(settings.shortDateFormat).toBe('MM/dd/yyyy'); expect(settings.longDateFormat).toBe('dd MMM, yyyy'); expect(settings.shortTimeFormat).toBe('HH:mm'); expect(settings.longTimeFormat).toBe('HH:mm:ss'); - // Проверка названий месяцев + // Check названий месяцев expect(settings.shortMonthNames.length).toBe(12); expect(settings.shortMonthNames[0]).toBe('Jan'); expect(settings.longMonthNames[0]).toBe('January'); - // Проверка названий дней недели + // Check названий дней недели expect(settings.shortWeekDayNames.length).toBe(7); expect(settings.shortWeekDayNames[0]).toBe('Sun'); expect(settings.longWeekDayNames[0]).toBe('Sunday'); - // Проверка разделителя десятичных и валюты + // Check разделителя десятичных и валюты expect(settings.decimalSeparator).toBe('.'); expect(settings.currency).toBe('USD'); }); - it('должен возвращать названия месяцев', () => { + it('should return названия месяцев', () => { expect(i18n.getShortMonthName(1)).toBe('Jan'); expect(i18n.getLongMonthName(1)).toBe('January'); expect(i18n.getShortMonthName(12)).toBe('Dec'); expect(i18n.getLongMonthName(12)).toBe('December'); - // Проверка исключений при неверном номере месяца + // Check исключений при неверном номере месяца expect(() => i18n.getShortMonthName(0)).toThrow(); expect(() => i18n.getLongMonthName(13)).toThrow(); }); - it('должен возвращать названия дней недели', () => { + it('should return названия дней недели', () => { expect(i18n.getShortWeekDayName(1)).toBe('Sun'); expect(i18n.getLongWeekDayName(1)).toBe('Sunday'); expect(i18n.getShortWeekDayName(7)).toBe('Sat'); expect(i18n.getLongWeekDayName(7)).toBe('Saturday'); - // Проверка исключений при неверном номере дня + // Check исключений при неверном номере дня expect(() => i18n.getShortWeekDayName(0)).toThrow(); expect(() => i18n.getLongWeekDayName(8)).toThrow(); }); - it('должен обновлять настройки локали', () => { + it('should обновлять настройки локали', () => { // Обновление настроек локали i18n.updateLocaleSettings({ shortDateFormat: 'dd.MM.yyyy', @@ -187,17 +187,17 @@ describe('i18n module', () => { const settings = i18n.getLocaleSettings(); - // Проверка обновленных настроек + // Check обновленных настроек expect(settings.shortDateFormat).toBe('dd.MM.yyyy'); expect(settings.longDateFormat).toBe('dd MMMM yyyy'); expect(settings.decimalSeparator).toBe(','); expect(settings.currency).toBe('EUR'); - // Проверка, что неизмененные настройки остались прежними + // Check, что неизмененные настройки остались прежними expect(settings.shortTimeFormat).toBe('HH:mm'); }); - it('должен форматировать дату и время по формату', () => { + it('should форматировать дату и время по формату', () => { const date = new Date(2023, 4, 15, 14, 30, 45); // 15 мая 2023 14:30:45 // Форматирование по шаблону @@ -208,11 +208,11 @@ describe('i18n module', () => { expect(i18n.dateTimeToStr(date, 'HH:mm:ss')).toBe('14:30:45'); expect(i18n.dateTimeToStr(date, 'hh:mm tt')).toBe('02:30 PM'); - // Проверка форматирования с текстом в скобках + // Check форматирования с текстом в скобках expect(i18n.dateTimeToStr(date, 'yyyy-MM-dd [at] HH:mm')).toBe('2023-05-15 at 14:30'); }); - it('должен форматировать дату и время с использованием расширенной функции', () => { + it('should форматировать дату и время с использованием расширенной функции', () => { const date = new Date(2023, 4, 15, 14, 30, 45); // 15 мая 2023 14:30:45 // Форматирование с использованием настроек текущей локали @@ -235,7 +235,7 @@ describe('i18n module', () => { expect(i18n.dateTimeToStrEx(date, DataType.DateTime, 'u')).toBe('2023-05-15 14:30'); }); - it('должен форматировать числа', () => { + it('should форматировать числа', () => { // Форматирование без указания формата i18n.updateLocaleSettings({ decimalSeparator: ',' }); expect(i18n.numberToStr(123.45)).toBe('123,45'); @@ -265,7 +265,7 @@ describe('i18n module', () => { expect(i18n.numberToStr(4, 'SOne=1|Two=2|Three=3')).toBe('4'); }); - it('должен форматировать логические значения', () => { + it('should форматировать логические значения', () => { // По умолчанию (без формата) expect(i18n.booleanToStr(true)).toBe('true'); expect(i18n.booleanToStr(false)).toBe('false'); @@ -284,7 +284,7 @@ describe('i18n module', () => { expect(i18n.booleanToStr(false, 'SNo|Yes')).toBe('No'); }); - it('должен добавлять и вызывать маппер', () => { + it('should добавлять и вызывать маппер', () => { let mapperCalled = false; let mapperLocaleId: string; let mapperTexts: any; @@ -301,7 +301,7 @@ describe('i18n module', () => { NewText: 'Test text' }); - // Проверка что маппер был вызван с правильными параметрами + // Check что маппер был вызван с правильными параметрами expect(mapperCalled).toBe(true); expect(mapperLocaleId).toBe('en-US'); expect(mapperTexts).toBeObject({ NewText: 'Test text' }); diff --git a/easydata.js/packs/core/tests/liquid.test.ts b/easydata.js/packs/core/tests/liquid.test.ts index 94f74821..72d6362d 100644 --- a/easydata.js/packs/core/tests/liquid.test.ts +++ b/easydata.js/packs/core/tests/liquid.test.ts @@ -1,7 +1,7 @@ import { liquid } from '../src/utils/liquid'; describe('liquid', () => { - it('должен заменять одну переменную в шаблоне', () => { + it('should заменять одну переменную в шаблоне', () => { const template = 'Hello, {{name}}!'; const vars = { name: 'World' }; @@ -10,7 +10,7 @@ describe('liquid', () => { expect(result).toBe('Hello, World!'); }); - it('должен заменять несколько разных переменных', () => { + it('should заменять несколько разных переменных', () => { const template = 'Hello, {{name}}! Today is {{day}}.'; const vars = { name: 'John', day: 'Monday' }; @@ -19,7 +19,7 @@ describe('liquid', () => { expect(result).toBe('Hello, John! Today is Monday.'); }); - it('должен заменять повторяющиеся переменные', () => { + it('should заменять повторяющиеся переменные', () => { const template = '{{name}} wrote: "Hello, {{name}}!"'; const vars = { name: 'Alice' }; @@ -28,7 +28,7 @@ describe('liquid', () => { expect(result).toBe('Alice wrote: "Hello, Alice!"'); }); - it('должен корректно обрабатывать числовые значения', () => { + it('should correctly handle числовые значения', () => { const template = 'The answer is {{answer}}.'; const vars = { answer: 42 }; @@ -37,7 +37,7 @@ describe('liquid', () => { expect(result).toBe('The answer is 42.'); }); - it('должен игнорировать неопределенные переменные', () => { + it('should игнорировать неопределенные переменные', () => { const template = 'Hello, {{name}}! Your age is {{age}}.'; const vars = { name: 'Bob' }; @@ -47,7 +47,7 @@ describe('liquid', () => { expect(result).toBe('Hello, Bob! Your age is {{age}}.'); }); - it('должен возвращать оригинальный шаблон, если vars равен null', () => { + it('should return оригинальный шаблон, если vars равен null', () => { const template = 'Hello, {{name}}!'; const result = liquid.renderLiquidTemplate(template, null); @@ -55,7 +55,7 @@ describe('liquid', () => { expect(result).toBe('Hello, {{name}}!'); }); - it('должен возвращать оригинальный шаблон, если vars равен undefined', () => { + it('should return оригинальный шаблон, если vars равен undefined', () => { const template = 'Hello, {{name}}!'; const result = liquid.renderLiquidTemplate(template, undefined); @@ -63,7 +63,7 @@ describe('liquid', () => { expect(result).toBe('Hello, {{name}}!'); }); - it('должен корректно работать с пустым шаблоном', () => { + it('should correctly работать с пустым шаблоном', () => { const template = ''; const vars = { name: 'World' }; @@ -72,7 +72,7 @@ describe('liquid', () => { expect(result).toBe(''); }); - it('должен корректно работать с пустым объектом vars', () => { + it('should correctly работать с пустым объектом vars', () => { const template = 'Hello, {{name}}!'; const vars = {}; @@ -81,7 +81,7 @@ describe('liquid', () => { expect(result).toBe('Hello, {{name}}!'); }); - it('должен корректно заменять на пустые значения', () => { + it('should correctly заменять на пустые значения', () => { const template = 'Name: "{{name}}"'; const vars = { name: '' }; @@ -90,7 +90,7 @@ describe('liquid', () => { expect(result).toBe('Name: ""'); }); - it('должен корректно работать с шаблоном без переменных', () => { + it('should correctly работать с шаблоном без переменных', () => { const template = 'Simple text without variables'; const vars = { name: 'World' }; @@ -99,7 +99,7 @@ describe('liquid', () => { expect(result).toBe('Simple text without variables'); }); - it('должен корректно заменять переменные с истинным логическим значением', () => { + it('should correctly заменять переменные с истинным логическим значением', () => { const template = 'Is active: {{isActive}}'; const vars = { isActive: true }; @@ -108,7 +108,7 @@ describe('liquid', () => { expect(result).toBe('Is active: true'); }); - it('должен корректно заменять переменные с ложным логическим значением', () => { + it('should correctly заменять переменные с ложным логическим значением', () => { const template = 'Is deleted: {{isDeleted}}'; const vars = { isDeleted: false }; diff --git a/easydata.js/packs/core/tests/meta_data.test.ts b/easydata.js/packs/core/tests/meta_data.test.ts index 47a70b0b..e435a165 100644 --- a/easydata.js/packs/core/tests/meta_data.test.ts +++ b/easydata.js/packs/core/tests/meta_data.test.ts @@ -15,7 +15,7 @@ describe('MetaData', () => { metaData = new MetaData(); }); - it('должен создаваться с правильными значениями по умолчанию', () => { + it('should создаваться с правильными значениями по умолчанию', () => { expect(metaData.id).toBe('__none'); expect(metaData.name).toBe('Empty model'); expect(metaData.isEmpty()).toBe(true); @@ -23,7 +23,7 @@ describe('MetaData', () => { expect(metaData.rootEntity).toBeDefined(); }); - it('должен создавать и возвращать объекты сущностей и атрибутов', () => { + it('should создавать и возвращать объекты сущностей и атрибутов', () => { const entity = metaData.createEntity(); expect(entity).toBeInstanceOf(MetaEntity); @@ -34,7 +34,7 @@ describe('MetaData', () => { expect(editor).toBeInstanceOf(ValueEditor); }); - it('должен загружать данные из JSON-строки', () => { + it('should загружать данные из JSON-строки', () => { const jsonData = JSON.stringify({ id: 'test-model', name: 'Test Model', @@ -53,7 +53,7 @@ describe('MetaData', () => { expect(metaData.version).toBe('1.0.0'); }); - it('должен загружать данные из объекта', () => { + it('should загружать данные из объекта', () => { const data: MetaDataDTO = { id: 'test-model', name: 'Test Model', @@ -72,7 +72,7 @@ describe('MetaData', () => { expect(metaData.version).toBe('1.0.0'); }); - it('должен загружать редакторы из данных', () => { + it('should загружать редакторы из данных', () => { const data: MetaDataDTO = { id: 'test-model', name: 'Test Model', @@ -100,7 +100,7 @@ describe('MetaData', () => { expect(metaData.editors[0].resType).toBe(DataType.String); }); - it('должен загружать форматы отображения из данных', () => { + it('should загружать форматы отображения из данных', () => { const data: MetaDataDTO = { id: 'test-model', name: 'Test Model', @@ -131,7 +131,7 @@ describe('MetaData', () => { expect(dateTimeFormats[0].isdef).toBe(true); }); - it('должен возвращать формат отображения по умолчанию для типа', () => { + it('should return формат отображения по умолчанию для типа', () => { const data: MetaDataDTO = { id: 'test-model', name: 'Test Model', @@ -157,18 +157,18 @@ describe('MetaData', () => { expect(defaultFormat.format).toBe('dd MMMM yyyy HH:mm'); }); - it('должен возвращать пустой массив для несуществующего типа формата', () => { + it('should return пустой массив для несуществующего типа формата', () => { const formats = metaData.getDisplayFormatsForType(DataType.Bool); expect(formats).toBeArray(); expect(formats).toBeEmpty(); }); - it('должен возвращать null для формата по умолчанию несуществующего типа', () => { + it('should return null для формата по умолчанию несуществующего типа', () => { const defaultFormat = metaData.getDefaultFormat(DataType.Bool); expect(defaultFormat).toBeNull(); }); - it('должен обнаруживать пустую модель данных', () => { + it('should обнаруживать пустую модель данных', () => { expect(metaData.isEmpty()).toBe(true); // Добавляем атрибут к корневой сущности @@ -179,7 +179,7 @@ describe('MetaData', () => { expect(metaData.isEmpty()).toBe(false); }); - it('должен находить редактор по его ID', () => { + it('should находить редактор по его ID', () => { const editor1 = metaData.createValueEditor(); editor1.id = 'editor1'; @@ -195,8 +195,8 @@ describe('MetaData', () => { expect(notFoundEditor).toBeNull(); }); - it('должен находить атрибут по его ID', () => { - // Создаем атрибуты + it('should находить атрибут по его ID', () => { + // Create attributes const attr1 = metaData.createEntityAttr(metaData.rootEntity); attr1.id = 'attr1'; metaData.rootEntity.attributes.push(attr1); @@ -211,7 +211,7 @@ describe('MetaData', () => { attr2.id = 'attr2'; subEntity.attributes.push(attr2); - // Проверяем поиск по ID + // Check поиск по ID const foundAttr1 = metaData.getAttributeById('attr1'); expect(foundAttr1).toBe(attr1); @@ -222,7 +222,7 @@ describe('MetaData', () => { expect(notFoundAttr).toBeNull(); }); - it('должен проверять свойства атрибута', () => { + it('should проверять свойства атрибута', () => { // Создаем атрибут const attr = metaData.createEntityAttr(metaData.rootEntity); attr.id = 'attr1'; @@ -230,16 +230,16 @@ describe('MetaData', () => { attr.description = 'Test description'; metaData.rootEntity.attributes.push(attr); - // Проверяем существующее свойство + // Check существующее свойство const hasDescription = metaData.checkAttrProperty('attr1', 'description'); expect(hasDescription).toBe(true); - // Проверяем несуществующее свойство + // Check несуществующее свойство expect(() => { metaData.checkAttrProperty('attr1', 'nonExistentProperty'); }).toThrow('No such property: nonExistentProperty'); - // Проверяем атрибут с lookup + // Check атрибут с lookup const lookupAttr = metaData.createEntityAttr(metaData.rootEntity); lookupAttr.id = 'lookupAttr'; lookupAttr.description = 'Lookup description'; @@ -251,12 +251,12 @@ describe('MetaData', () => { attrWithLookup.description = ''; // Пустое описание metaData.rootEntity.attributes.push(attrWithLookup); - // Проверяем свойство через lookup + // Check свойство через lookup const hasDescriptionViaLookup = metaData.checkAttrProperty('attrWithLookup', 'description'); expect(hasDescriptionViaLookup).toBe(true); }); - it('должен очищать модель данных', () => { + it('should очищать модель данных', () => { // Заполняем модель metaData.id = 'test-model'; metaData.name = 'Test Model'; @@ -269,31 +269,31 @@ describe('MetaData', () => { // Очищаем модель metaData.clear(); - // Проверяем результат + // Check результат expect(metaData.rootEntity).toBeDefined(); expect(metaData.editors).toBeArray(); expect(metaData.editors).toBeEmpty(); expect(metaData.version).toBe(''); }); - it('должен добавлять редакторы значений по умолчанию', () => { + it('should добавлять редакторы значений по умолчанию', () => { metaData.addDefaultValueEditors(); expect(metaData.editors.length).toBe(3); - // Проверяем первый редактор + // Check первый редактор const stringEditor = metaData.getEditorById('_DTE'); expect(stringEditor).not.toBeNull(); expect(stringEditor.tag).toBe(EditorTag.Edit); expect(stringEditor.resType).toBe(DataType.String); expect(stringEditor.defValue).toBe(''); - // Проверяем остальные редакторы + // Check остальные редакторы expect(metaData.getEditorById('_DPDE')).not.toBeNull(); expect(metaData.getEditorById('_DPTE')).not.toBeNull(); }); - it('должен добавлять или обновлять редактор значений', () => { + it('should добавлять или обновлять редактор значений', () => { // Добавляем новый редактор const newEditor = metaData.addOrUpdateValueEditor('testEditor', EditorTag.Edit, DataType.String); @@ -311,7 +311,7 @@ describe('MetaData', () => { expect(updatedEditor.resType).toBe(DataType.DateTime); }); - it('должен получать дерево сущностей с фильтрацией', () => { + it('should получать дерево сущностей с фильтрацией', () => { // Подготовка данных для теста // Создаем иерархию сущностей const subEntity1 = metaData.createEntity(metaData.rootEntity); @@ -335,19 +335,19 @@ describe('MetaData', () => { attr2.dataType = DataType.Int32; subEntity2.attributes.push(attr2); - // Получаем дерево сущностей с фильтром по типу данных + // Get дерево сущностей с фильтром по типу данных const filterFunc = (entity: MetaEntity, attr: MetaEntityAttr) => { return !attr || attr.dataType === DataType.Int32; }; const tree = metaData.getEntitiesTreeWithFilter(filterFunc); - // Проверяем результат + // Check результат expect(tree.length).toBe(1); // Должна быть только одна сущность expect(tree[0].id).toBe('Entity2'); }); - it('должен получать дерево сущностей с опциями', () => { + it('should получать дерево сущностей с опциями', () => { // Подготовка данных для теста const subEntity1 = metaData.createEntity(metaData.rootEntity); subEntity1.name = 'Entity1'; @@ -359,7 +359,7 @@ describe('MetaData', () => { attr1.caption = 'Attribute 1'; subEntity1.attributes.push(attr1); - // Получаем дерево сущностей с опциями + // Get дерево сущностей с опциями const opts = { includeRootData: true, sortEntities: true, @@ -368,12 +368,12 @@ describe('MetaData', () => { const tree = metaData.getEntitiesTree(opts); - // Проверяем результат + // Check результат expect(tree.id).toBe('Root'); expect(tree.items.length).toBe(1); // Одна дочерняя сущность }); - it('должен получать путь сущности по атрибуту', () => { + it('should получать путь сущности по атрибуту', () => { // Подготовка данных для теста // Создаем иерархию сущностей и атрибутов const subEntity1 = metaData.createEntity(metaData.rootEntity); @@ -391,14 +391,14 @@ describe('MetaData', () => { attr.caption = 'Deep Attribute'; subEntity2.attributes.push(attr); - // Получаем путь + // Get путь const path = metaData.getFullEntityPathByAttr('DeepAttribute', ' > '); - // Проверяем результат + // Check результат expect(path).toBe('Entity 1 > Entity 2'); }); - it('должен получать текст атрибута', () => { + it('should получать текст атрибута', () => { // Подготовка данных для теста const subEntity = metaData.createEntity(metaData.rootEntity); subEntity.name = 'TestEntity'; @@ -410,16 +410,16 @@ describe('MetaData', () => { attr.caption = 'Original Caption'; subEntity.attributes.push(attr); - // Получаем текст атрибута без формата + // Get текст атрибута без формата const simpleText = metaData.getAttributeText(attr, ''); expect(simpleText).toBe('Тестовый Атрибут'); // Берем из i18n - // Получаем текст атрибута с форматом + // Get текст атрибута с форматом const formattedText = metaData.getAttributeText(attr, '{entity}: {attr}'); expect(formattedText).toBe('Тестовая Сущность: Тестовый Атрибут'); }); - it('должен сканировать сущности и их атрибуты', () => { + it('should сканировать сущности и их атрибуты', () => { // Подготовка данных для теста const subEntity1 = metaData.createEntity(metaData.rootEntity); subEntity1.name = 'Entity1'; @@ -446,12 +446,12 @@ describe('MetaData', () => { () => { entityCount++; } ); - // Проверяем результат: 2 атрибута и 3 сущности (корневая + 2 дочерние) + // Check результат: 2 атрибута и 3 сущности (корневая + 2 дочерние) expect(attrCount).toBe(2); expect(entityCount).toBe(3); }); - it('должен находить первый атрибут по фильтру', () => { + it('should находить первый атрибут по фильтру', () => { // Подготовка данных для теста const entity1 = metaData.createEntity(metaData.rootEntity); entity1.name = 'Entity1'; @@ -472,12 +472,12 @@ describe('MetaData', () => { (attr) => attr.dataType === DataType.Int32 ); - // Проверяем результат + // Check результат expect(intAttr).not.toBeNull(); expect(intAttr.id).toBe('Attr2'); }); - it('должен устанавливать данные используя setData', () => { + it('should устанавливать данные используя setData', () => { // Через JSON-строку const jsonData = JSON.stringify({ id: 'test-json', diff --git a/easydata.js/packs/core/tests/meta_entity.test.ts b/easydata.js/packs/core/tests/meta_entity.test.ts index dbfe3837..86b51487 100644 --- a/easydata.js/packs/core/tests/meta_entity.test.ts +++ b/easydata.js/packs/core/tests/meta_entity.test.ts @@ -17,7 +17,7 @@ describe('MetaEntity', () => { } as unknown as MetaData; }); - it('должен инициализироваться с правильными значениями по умолчанию', () => { + it('should initialize with correct values по умолчанию', () => { const entity = new MetaEntity(); expect(entity.name).toBe(''); @@ -31,14 +31,14 @@ describe('MetaEntity', () => { expect(entity.parent).toBeUndefined(); }); - it('должен принимать родительскую сущность в конструкторе', () => { + it('should принимать родительскую сущность в конструкторе', () => { const parent = new MetaEntity(); const child = new MetaEntity(parent); expect(child.parent).toBe(parent); }); - it('должен загружать данные из DTO', () => { + it('should загружать данные из DTO', () => { const entity = new MetaEntity(); const dto: MetaEntityDTO = { id: 'test-entity', @@ -60,7 +60,7 @@ describe('MetaEntity', () => { expect(entity.isEditable).toBe(false); }); - it('должен загружать дочерние сущности из DTO', () => { + it('should загружать дочерние сущности из DTO', () => { const entity = new MetaEntity(); const dto: MetaEntityDTO = { id: 'parent', @@ -94,7 +94,7 @@ describe('MetaEntity', () => { expect(entity.subEntities[1].parent).toBe(entity); }); - it('должен загружать атрибуты из DTO', () => { + it('should загружать атрибуты из DTO', () => { const entity = new MetaEntity(); const dto: MetaEntityDTO = { id: 'test-entity', @@ -133,7 +133,7 @@ describe('MetaEntity', () => { expect(entity.attributes[1].isPrimaryKey).toBe(false); }); - it('должен сканировать сущность с атрибутами и подсущностями', () => { + it('should сканировать сущность with attributes и подсущностями', () => { // Настройка сложной иерархии сущностей const rootEntity = new MetaEntity(); rootEntity.id = 'root'; @@ -190,10 +190,10 @@ describe('MetaEntity', () => { expect(attrCount).toBe(2); // attr1 и attr2 }); - it('должен находить первичные атрибуты', () => { + it('should находить первичные атрибуты', () => { const entity = new MetaEntity(); - // Создаем атрибуты + // Create attributes const attr1 = new MetaEntityAttr(entity); attr1.id = 'id'; attr1.isPrimaryKey = true; @@ -209,19 +209,19 @@ describe('MetaEntity', () => { attr3.isPrimaryKey = true; entity.attributes.push(attr3); - // Проверка getPrimaryAttrs + // Check getPrimaryAttrs const primaryAttrs = entity.getPrimaryAttrs(); expect(primaryAttrs.length).toBe(2); expect(primaryAttrs[0].id).toBe('id'); expect(primaryAttrs[1].id).toBe('code'); - // Проверка getFirstPrimaryAttr + // Check getFirstPrimaryAttr const firstPrimaryAttr = entity.getFirstPrimaryAttr(); expect(firstPrimaryAttr).not.toBeNull(); expect(firstPrimaryAttr!.id).toBe('id'); }); - it('должен возвращать null из getFirstPrimaryAttr если первичных ключей нет', () => { + it('should return null из getFirstPrimaryAttr если первичных ключей нет', () => { const entity = new MetaEntity(); const attr = new MetaEntityAttr(entity); @@ -250,7 +250,7 @@ describe('MetaEntityAttr', () => { } as unknown as MetaData; }); - it('должен инициализироваться с правильными значениями по умолчанию', () => { + it('should initialize with correct values по умолчанию', () => { const attr = new MetaEntityAttr(mockEntity); expect(attr.id).toBe(''); @@ -269,7 +269,7 @@ describe('MetaEntityAttr', () => { expect(attr.entity).toBe(mockEntity); }); - it('должен загружать данные из DTO', () => { + it('should загружать данные из DTO', () => { const attr = new MetaEntityAttr(mockEntity); const dto: MetaEntityAttrDTO = { id: 'test-attr', @@ -309,10 +309,10 @@ describe('MetaEntityAttr', () => { expect(attr.userData).toBe('{"custom":"data"}'); }); - it('должен правильно обрабатывать даты в defaultValue', () => { + it('should правильно обрабатывать даты в defaultValue', () => { const attr = new MetaEntityAttr(mockEntity); - // Проверяем Date в defaultValue + // Check Date в defaultValue const dateDto: MetaEntityAttrDTO = { id: 'date-attr', cptn: 'Date Field', @@ -330,7 +330,7 @@ describe('MetaEntityAttr', () => { expect(attr.defaultValue.getDate()).toBe(15); }); - it('должен корректно обрабатывать lookup атрибуты', () => { + it('should correctly handle lookup атрибуты', () => { const attr = new MetaEntityAttr(mockEntity); const dto: MetaEntityAttrDTO = { id: 'fk-attr', @@ -352,7 +352,7 @@ describe('MetaEntityAttr', () => { expect(attr.lookupDataAttr).toBe('lookup-data-attr'); }); - it('должен устанавливать редактор по умолчанию', () => { + it('should устанавливать редактор по умолчанию', () => { const editor = { id: 'test-editor' }; mockMetaData = { @@ -375,7 +375,7 @@ describe('MetaEntityAttr', () => { expect(attr.defaultEditor).toBe(editor); }); - it('должен использовать старый формат ivis как синоним для sov', () => { + it('should использовать старый формат ivis как синоним для sov', () => { const attr = new MetaEntityAttr(mockEntity); // Используем старый формат ivis diff --git a/easydata.js/packs/core/tests/string_utils.test.ts b/easydata.js/packs/core/tests/string_utils.test.ts index 8e3a6ff4..de6229e5 100644 --- a/easydata.js/packs/core/tests/string_utils.test.ts +++ b/easydata.js/packs/core/tests/string_utils.test.ts @@ -1,14 +1,14 @@ import { repeatString, reverseString, strEndsWith, combinePath } from '../src/utils/string_utils'; describe('String Utils', () => { - it('должен правильно повторять строку указанное количество раз', () => { + it('should правильно повторять строку указанное количество раз', () => { expect(repeatString('a', 3)).toBe('aaa'); expect(repeatString('abc', 2)).toBe('abcabc'); expect(repeatString('x', 0)).toBe(''); expect(repeatString('', 5)).toBe(''); }); - it('должен правильно переворачивать строку', () => { + it('should правильно переворачивать строку', () => { expect(reverseString('abc')).toBe('cba'); expect(reverseString('hello')).toBe('olleh'); expect(reverseString('')).toBe(''); @@ -16,7 +16,7 @@ describe('String Utils', () => { expect(reverseString('12345')).toBe('54321'); }); - it('должен правильно проверять, заканчивается ли строка указанным символом', () => { + it('should правильно проверять, заканчивается ли строка указанным символом', () => { expect(strEndsWith('test.js', '.js')).toBe(true); expect(strEndsWith('hello world', 'world')).toBe(true); expect(strEndsWith('file.txt', '.doc')).toBe(false); @@ -25,12 +25,12 @@ describe('String Utils', () => { expect(strEndsWith('abc', '')).toBe(true); }); - it('должен корректно обрабатывать случай, когда strEndsWith получает null или undefined', () => { + it('should correctly handle случай, когда strEndsWith получает null или undefined', () => { expect(strEndsWith(null as any, '.js')).toBe(false); expect(strEndsWith(undefined as any, '.txt')).toBe(false); }); - it('должен корректно объединять пути', () => { + it('should correctly объединять пути', () => { expect(combinePath('path/to', 'file')).toBe('path/to/file'); expect(combinePath('path/to/', 'file')).toBe('path/to/file'); expect(combinePath('', 'file')).toBe('file'); @@ -38,7 +38,7 @@ describe('String Utils', () => { expect(combinePath('path', '')).toBe('path/'); }); - it('должен корректно обрабатывать различные сценарии объединения путей', () => { + it('should correctly handle различные сценарии объединения путей', () => { expect(combinePath('root', 'path/to/file')).toBe('root/path/to/file'); expect(combinePath('/api', '/data')).toBe('/api/data'); expect(combinePath('http://example.com', 'api')).toBe('http://example.com/api'); diff --git a/easydata.js/packs/core/tests/time_utils.test.ts b/easydata.js/packs/core/tests/time_utils.test.ts index 892ae850..5e274d89 100644 --- a/easydata.js/packs/core/tests/time_utils.test.ts +++ b/easydata.js/packs/core/tests/time_utils.test.ts @@ -1,15 +1,15 @@ import { TimeValue, TimeSettings, SpecialDatesResolver, registerSpecialDatesResolver } from '../src/types/time_utils'; describe('Time Utils', () => { - // Мок для даты, чтобы тесты не зависели от текущего времени + // Mock for даты, чтобы тесты не зависели от текущего времени let originalDate: DateConstructor; let fixedDate: Date; beforeEach(() => { - // Сохраняем оригинальный конструктор Date + // Save оригинальный конструктор Date originalDate = global.Date; - // Устанавливаем фиксированную дату для тестов: 15 мая 2023, 10:30:00 + // Set фиксированную дату для тестов: 15 мая 2023, 10:30:00 fixedDate = new Date(2023, 4, 15, 10, 30, 0); // Мокаем конструктор Date @@ -24,11 +24,11 @@ describe('Time Utils', () => { }); afterEach(() => { - // Восстанавливаем оригинальный Date + // Restore оригинальный Date global.Date = originalDate; }); - it('должен создавать TimeValue с датой', () => { + it('should создавать TimeValue с датой', () => { const date = new Date(2023, 0, 15); const timeValue = new TimeValue(date); @@ -36,14 +36,14 @@ describe('Time Utils', () => { expect(timeValue['_name']).toBeUndefined(); }); - it('должен создавать TimeValue со строковым именем', () => { + it('should создавать TimeValue со строковым именем', () => { const timeValue = new TimeValue('Today'); expect(timeValue['date']).toBeUndefined(); expect(timeValue['_name']).toBe('Today'); }); - it('должен возвращать дату через asTime', () => { + it('should return дату через asTime', () => { const date = new Date(2023, 0, 15); const timeValue = new TimeValue(date); @@ -51,17 +51,17 @@ describe('Time Utils', () => { expect(result).toBe(date); }); - it('должен возвращать специальную дату через asTime', () => { + it('should return специальную дату через asTime', () => { const timeValue = new TimeValue('Today'); expect(timeValue.asTime()).toBeInstanceOf(Date); }); - it('должен иметь getter для name', () => { + it('should иметь getter для name', () => { const timeValue = new TimeValue('Today'); expect(timeValue.name).toBe('Today'); }); - it('должен разрешать специальную дату Today', () => { + it('should разрешать специальную дату Today', () => { const resolver = new SpecialDatesResolver(); const result = resolver.Today(); @@ -71,7 +71,7 @@ describe('Time Utils', () => { expect(result.getDate()).toBe(15); }); - it('должен разрешать специальную дату Yesterday', () => { + it('should разрешать специальную дату Yesterday', () => { const resolver = new SpecialDatesResolver(); const result = resolver.Yesterday(); @@ -81,7 +81,7 @@ describe('Time Utils', () => { expect(result.getDate()).toBe(14); // 15 - 1 = 14 }); - it('должен разрешать специальную дату Tomorrow', () => { + it('should разрешать специальную дату Tomorrow', () => { const resolver = new SpecialDatesResolver(); const result = resolver.Tomorrow(); @@ -91,7 +91,7 @@ describe('Time Utils', () => { expect(result.getDate()).toBe(16); // 15 + 1 = 16 }); - it('должен разрешать специальную дату FirstDayOfMonth', () => { + it('should разрешать специальную дату FirstDayOfMonth', () => { const resolver = new SpecialDatesResolver(); const result = resolver.FirstDayOfMonth(); @@ -101,7 +101,7 @@ describe('Time Utils', () => { expect(result.getDate()).toBe(1); }); - it('должен разрешать специальную дату LastDayOfMonth', () => { + it('should разрешать специальную дату LastDayOfMonth', () => { const resolver = new SpecialDatesResolver(); const result = resolver.LastDayOfMonth(); @@ -111,7 +111,7 @@ describe('Time Utils', () => { expect(result.getDate()).toBe(31); // май имеет 31 день }); - it('должен разрешать специальную дату FirstDayOfNextMonth', () => { + it('should разрешать специальную дату FirstDayOfNextMonth', () => { const resolver = new SpecialDatesResolver(); const result = resolver.FirstDayOfNextMonth(); @@ -121,7 +121,7 @@ describe('Time Utils', () => { expect(result.getDate()).toBe(1); }); - it('должен разрешать специальную дату FirstDayOfPrevMonth', () => { + it('should разрешать специальную дату FirstDayOfPrevMonth', () => { const resolver = new SpecialDatesResolver(); const result = resolver.FirstDayOfPrevMonth(); @@ -131,7 +131,7 @@ describe('Time Utils', () => { expect(result.getDate()).toBe(1); }); - it('должен разрешать специальную дату FirstDayOfYear', () => { + it('should разрешать специальную дату FirstDayOfYear', () => { const resolver = new SpecialDatesResolver(); const result = resolver.FirstDayOfYear(); @@ -141,7 +141,7 @@ describe('Time Utils', () => { expect(result.getDate()).toBe(1); }); - it('должен разрешать специальную дату FirstDayOfPrevYear', () => { + it('should разрешать специальную дату FirstDayOfPrevYear', () => { const resolver = new SpecialDatesResolver(); const result = resolver.FirstDayOfPrevYear(); @@ -151,7 +151,7 @@ describe('Time Utils', () => { expect(result.getDate()).toBe(1); }); - it('должен разрешать специальную дату FirstDayOfNextYear', () => { + it('should разрешать специальную дату FirstDayOfNextYear', () => { const resolver = new SpecialDatesResolver(); const result = resolver.FirstDayOfNextYear(); @@ -161,7 +161,7 @@ describe('Time Utils', () => { expect(result.getDate()).toBe(1); }); - it('должен разрешать специальную дату FirstDayOfWeek для середины недели', () => { + it('should разрешать специальную дату FirstDayOfWeek для середины недели', () => { // 15 мая 2023 - понедельник fixedDate = new Date(2023, 4, 15); // 15 мая 2023 const resolver = new SpecialDatesResolver(); @@ -173,7 +173,7 @@ describe('Time Utils', () => { expect(result.getDate()).toBe(15); // понедельник }); - it('должен разрешать специальную дату FirstDayOfPrevWeek', () => { + it('should разрешать специальную дату FirstDayOfPrevWeek', () => { const resolver = new SpecialDatesResolver(); const result = resolver.FirstDayOfPrevWeek(); @@ -182,7 +182,7 @@ describe('Time Utils', () => { expect(result).toBeInstanceOf(Date); }); - it('должен разрешать специальную дату FirstDayOfNextWeek', () => { + it('should разрешать специальную дату FirstDayOfNextWeek', () => { const resolver = new SpecialDatesResolver(); const result = resolver.FirstDayOfNextWeek(); @@ -191,7 +191,7 @@ describe('Time Utils', () => { expect(result).toBeInstanceOf(Date); }); - it('должен возвращать дату по имени через getDateByName', () => { + it('should return дату по имени через getDateByName', () => { const resolver = new SpecialDatesResolver(); const result = resolver.getDateByName('Today'); @@ -202,15 +202,15 @@ describe('Time Utils', () => { expect(result.getDate()).toBe(15); }); - it('должен возвращать undefined для неизвестного имени даты', () => { + it('should return undefined для неизвестного имени даты', () => { const resolver = new SpecialDatesResolver(); const result = resolver.getDateByName('NonExistentDate'); expect(result).toBeUndefined(); }); - it('должен регистрировать новый резолвер через registerSpecialDatesResolver', () => { - // Создаем кастомный резолвер + it('should регистрировать новый резолвер через registerSpecialDatesResolver', () => { + // Create custom резолвер class CustomResolver extends SpecialDatesResolver { public Custom(): Date { return new Date(2000, 0, 1); @@ -222,7 +222,7 @@ describe('Time Utils', () => { // Регистрируем его registerSpecialDatesResolver(customResolver); - // Проверяем, что теперь используется новый резолвер + // Check, что теперь используется новый резолвер const timeValue = new TimeValue('Custom'); const result = timeValue.asTime(); diff --git a/easydata.js/packs/core/tests/utils.test.ts b/easydata.js/packs/core/tests/utils.test.ts index c5935b1b..42802ee3 100644 --- a/easydata.js/packs/core/tests/utils.test.ts +++ b/easydata.js/packs/core/tests/utils.test.ts @@ -5,7 +5,7 @@ import { DataType } from '../src/types/data_type'; describe('utils', () => { // Тесты для функций с типами данных - it('должен вернуть все типы данных через getAllDataTypes', () => { + it('should вернуть все типы данных через getAllDataTypes', () => { const dataTypes = utils.getAllDataTypes(); expect(dataTypes).toContain(DataType.String); expect(dataTypes).toContain(DataType.Int32); @@ -15,7 +15,7 @@ describe('utils', () => { expect(typeof dataTypes[0]).toBe('number'); }); - it('должен вернуть все типы для дат через getDateDataTypes', () => { + it('should вернуть все типы для дат через getDateDataTypes', () => { const dateTypes = utils.getDateDataTypes(); expect(dateTypes.length).toBe(3); expect(dateTypes).toContain(DataType.Date); @@ -23,7 +23,7 @@ describe('utils', () => { expect(dateTypes).toContain(DataType.Time); }); - it('должен вернуть все строковые типы через getStringDataTypes', () => { + it('should вернуть все строковые типы через getStringDataTypes', () => { const stringTypes = utils.getStringDataTypes(); expect(stringTypes.length).toBe(3); expect(stringTypes).toContain(DataType.String); @@ -31,14 +31,14 @@ describe('utils', () => { expect(stringTypes).toContain(DataType.FixedChar); }); - it('должен вернуть все числовые типы через getNumericDataTypes', () => { + it('should вернуть все числовые типы через getNumericDataTypes', () => { const numericTypes = utils.getNumericDataTypes(); expect(numericTypes).toContain(DataType.Int32); expect(numericTypes).toContain(DataType.Float); expect(numericTypes).toContain(DataType.Currency); }); - it('должен проверять, является ли тип числовым через isNumericType', () => { + it('should проверять, является ли тип числовым через isNumericType', () => { expect(utils.isNumericType(DataType.Int32)).toBe(true); expect(utils.isNumericType(DataType.Float)).toBe(true); expect(utils.isNumericType(DataType.Currency)).toBe(true); @@ -46,7 +46,7 @@ describe('utils', () => { expect(utils.isNumericType(DataType.Date)).toBe(false); }); - it('должен проверять, является ли тип целочисленным через isIntType', () => { + it('should проверять, является ли тип целочисленным через isIntType', () => { expect(utils.isIntType(DataType.Int32)).toBe(true); expect(utils.isIntType(DataType.Int64)).toBe(true); expect(utils.isIntType(DataType.Byte)).toBe(true); @@ -54,7 +54,7 @@ describe('utils', () => { expect(utils.isIntType(DataType.String)).toBe(false); }); - it('должен проверять, совместимы ли типы данных через areCompatibleDataTypes', () => { + it('should проверять, совместимы ли типы данных через areCompatibleDataTypes', () => { expect(utils.areCompatibleDataTypes(DataType.Int32, DataType.Int32)).toBe(true); expect(utils.areCompatibleDataTypes(DataType.Date, DataType.DateTime)).toBe(true); expect(utils.areCompatibleDataTypes(DataType.DateTime, DataType.Date)).toBe(true); @@ -64,21 +64,21 @@ describe('utils', () => { }); // Тесты для объектных функций - it('должен объединять объекты через assign', () => { + it('should объединять объекты через assign', () => { const target = { a: 1, b: 2 }; const source1 = { b: 3, c: 4 }; const source2 = { d: 5 }; const result = utils.assign(target, source1, source2); - expect(result).toBe(target); // Проверка, что assign возвращает target + expect(result).toBe(target); // Check, что assign возвращает target expect(result.a).toBe(1); expect(result.b).toBe(3); // Значение из source1 перезаписало значение из target expect(result.c).toBe(4); expect(result.d).toBe(5); }); - it('должен обрабатывать пустые объекты и null в assign', () => { + it('should обрабатывать пустые объекты и null в assign', () => { const target = null; const source = { a: 1 }; @@ -87,7 +87,7 @@ describe('utils', () => { expect(result).toBeObject({ a: 1 }); }); - it('должен делать глубокое копирование объектов через assignDeep', () => { + it('should делать глубокое копирование объектов через assignDeep', () => { const target = { a: 1, nested: { x: 10, y: 20 } @@ -106,12 +106,12 @@ describe('utils', () => { expect(result.nested.y).toBe(30); // Значение из source перезаписало значение из target expect(result.nested.z).toBe(40); - // Проверка, что объекты действительно скопированы, а не ссылаются на тот же объект + // Check, что объекты действительно скопированы, а не ссылаются на тот же объект source.nested.y = 100; expect(result.nested.y).toBe(30); }); - it('должен обрабатывать циклические ссылки в assignDeep', () => { + it('should обрабатывать циклические ссылки в assignDeep', () => { const target = {}; const source = { a: 1 }; source.self = source; // Циклическая ссылка @@ -123,7 +123,7 @@ describe('utils', () => { expect(result.self).toBe(result); // Цикл сохранен, но ссылается на новый объект }); - it('должен копировать массивы в assignDeep', () => { + it('should копировать массивы в assignDeep', () => { const target = { arr: [1, 2] }; const source = { arr: [3, 4, 5] }; @@ -131,19 +131,19 @@ describe('utils', () => { expect(result.arr).toBeArrayEqual([3, 4, 5]); - // Проверка, что массивы действительно скопированы + // Check, что массивы действительно скопированы source.arr.push(6); expect(result.arr).toBeArrayEqual([3, 4, 5]); // Не изменился после изменения source }); - it('должен возвращать значение по умолчанию через getIfDefined', () => { + it('should return значение по умолчанию через getIfDefined', () => { expect(utils.getIfDefined(undefined, 'default')).toBe('default'); expect(utils.getIfDefined(null, 'default')).toBe(null); expect(utils.getIfDefined(0, 'default')).toBe(0); expect(utils.getIfDefined('value', 'default')).toBe('value'); }); - it('должен проверять определенность и не-null значения через IsDefinedAndNotNull', () => { + it('should проверять определенность и не-null значения через IsDefinedAndNotNull', () => { expect(utils.IsDefinedAndNotNull(undefined)).toBe(false); expect(utils.IsDefinedAndNotNull(null)).toBe(false); expect(utils.IsDefinedAndNotNull(0)).toBe(true); @@ -151,7 +151,7 @@ describe('utils', () => { expect(utils.IsDefinedAndNotNull(false)).toBe(true); }); - it('должен проверять, является ли значение объектом через isObject', () => { + it('should проверять, является ли значение объектом через isObject', () => { expect(utils.isObject({})).toBe(true); expect(utils.isObject([])).toBe(true); expect(utils.isObject(function() {})).toBe(true); @@ -161,7 +161,7 @@ describe('utils', () => { expect(utils.isObject('string')).toBe(false); }); - it('должен проверять, установлено ли свойство через isPropSet', () => { + it('should проверять, установлено ли свойство через isPropSet', () => { const obj = { prop1: 'value1', PROP2: 'value2', @@ -170,14 +170,14 @@ describe('utils', () => { }; expect(utils.isPropSet(obj, 'prop1')).toBe('value1'); - expect(utils.isPropSet(obj, 'prop2')).toBe('value2'); // Проверка регистронезависимости + expect(utils.isPropSet(obj, 'prop2')).toBe('value2'); // Check регистронезависимости expect(utils.isPropSet(obj, 'prop3')).toBe(null); expect(utils.isPropSet(obj, 'prop4')).toBe(undefined); expect(utils.isPropSet(obj, 'nonExistent')).toBe(undefined); }); // Тесты для функций с массивами - it('должен копировать элементы одного массива в другой через copyArrayTo', () => { + it('should копировать элементы одного массива в другой через copyArrayTo', () => { const source = [1, 2, 3, 4]; const target = [0, 0, 0, 0, 0]; @@ -186,7 +186,7 @@ describe('utils', () => { expect(target).toBeArrayEqual([1, 2, 3, 4, 0]); }); - it('должен создавать новый массив из коллекции через createArrayFrom', () => { + it('should создавать новый массив из коллекции через createArrayFrom', () => { const collection = { 0: 'a', 1: 'b', @@ -206,7 +206,7 @@ describe('utils', () => { expect(result === collection).toBe(false); // Новый массив, не исходная коллекция }); - it('должен находить элемент по id через findItemById', () => { + it('should находить элемент по id через findItemById', () => { const array = [ { id: 1, value: 'one' }, { id: 2, value: 'two' }, @@ -222,7 +222,7 @@ describe('utils', () => { expect(notFound).toBeNull(); }); - it('должен находить индекс элемента по id через findItemIndexById', () => { + it('should находить индекс элемента по id через findItemIndexById', () => { const array = [ { id: 1, value: 'one' }, { id: 2, value: 'two' }, @@ -236,14 +236,14 @@ describe('utils', () => { expect(notFoundIndex).toBe(-1); }); - it('должен находить индекс элемента в массиве через indexOfArrayItem', () => { + it('should находить индекс элемента в массиве через indexOfArrayItem', () => { const array = ['a', 'b', 'c', 'd']; expect(utils.indexOfArrayItem(array, 'b')).toBe(1); expect(utils.indexOfArrayItem(array, 'e')).toBe(-1); }); - it('должен перемещать элемент в массиве через moveArrayItem', () => { + it('should перемещать элемент в массиве через moveArrayItem', () => { const array = ['a', 'b', 'c', 'd', 'e']; utils.moveArrayItem(array, 1, 3); @@ -251,7 +251,7 @@ describe('utils', () => { expect(array).toBeArrayEqual(['a', 'c', 'd', 'b', 'e']); }); - it('должен выбрасывать ошибку при перемещении несуществующего элемента', () => { + it('should throw error при перемещении несуществующего элемента', () => { const array = ['a', 'b', 'c']; expect(() => { @@ -259,15 +259,15 @@ describe('utils', () => { }).toThrow('Index out of bounds: 5'); }); - it('должен корректно обрабатывать перемещение за пределы массива', () => { + it('should correctly handle перемещение за пределы массива', () => { const array = ['a', 'b', 'c', 'd', 'e']; - utils.moveArrayItem(array, 1, 10); // Индекс 10 вне массива, должен быть скорректирован + utils.moveArrayItem(array, 1, 10); // Индекс 10 вне массива, should быть скорректирован expect(array).toBeArrayEqual(['a', 'c', 'd', 'e', 'b']); }); - it('должен удалять элемент из массива через removeArrayItem', () => { + it('should удалять элемент из массива через removeArrayItem', () => { const array = ['a', 'b', 'c', 'd']; const removed = utils.removeArrayItem(array, 'b'); @@ -280,7 +280,7 @@ describe('utils', () => { expect(array).toBeArrayEqual(['a', 'c', 'd']); }); - it('должен вставлять элемент в массив через insertArrayItem', () => { + it('should вставлять элемент в массив через insertArrayItem', () => { const array = ['a', 'c', 'd']; utils.insertArrayItem(array, 1, 'b'); @@ -288,7 +288,7 @@ describe('utils', () => { expect(array).toBeArrayEqual(['a', 'b', 'c', 'd']); }); - it('должен заполнять массив значениями через fillArray', () => { + it('should заполнять массив значениями через fillArray', () => { const array = ['a', 'b', 'c', 'd', 'e']; utils.fillArray(array, 'x', 1, 4); @@ -296,7 +296,7 @@ describe('utils', () => { expect(array).toBeArrayEqual(['a', 'x', 'x', 'x', 'e']); }); - it('должен обрабатывать отрицательные индексы в fillArray', () => { + it('should обрабатывать отрицательные индексы в fillArray', () => { const array = ['a', 'b', 'c', 'd', 'e']; utils.fillArray(array, 'x', -3, -1); @@ -304,7 +304,7 @@ describe('utils', () => { expect(array).toBeArrayEqual(['a', 'b', 'x', 'x', 'e']); }); - it('должен заполнять до конца массива при отсутствии end в fillArray', () => { + it('should заполнять до конца массива при отсутствии end в fillArray', () => { const array = ['a', 'b', 'c', 'd', 'e']; utils.fillArray(array, 'x', 3); @@ -313,7 +313,7 @@ describe('utils', () => { }); // Тесты для функций с проверками - it('должен проверять, является ли значение числовым через isNumeric', () => { + it('should проверять, является ли значение числовым через isNumeric', () => { expect(utils.isNumeric(42)).toBe(true); expect(utils.isNumeric('42')).toBe(true); expect(utils.isNumeric(-1.5)).toBe(true); @@ -327,7 +327,7 @@ describe('utils', () => { }); // Тесты для функций генерации ID - it('должен генерировать уникальные ID через generateId', () => { + it('should генерировать уникальные ID через generateId', () => { const id1 = utils.generateId('test'); const id2 = utils.generateId('test'); @@ -336,18 +336,18 @@ describe('utils', () => { expect(id2.startsWith('test-')).toBe(true); }); - it('должен использовать префикс easy при вызове generateId без аргументов', () => { + it('should использовать префикс easy при вызове generateId без аргументов', () => { const id = utils.generateId(null); expect(id.startsWith('easy-')).toBe(true); }); - it('должен сокращать длинные префиксы в generateId', () => { + it('should сокращать длинные префиксы в generateId', () => { const id = utils.generateId('veryLongPrefix'); expect(id.startsWith('vryL-')).toBe(true); }); // Тесты для функций с датами - it('должен конвертировать строку в дату через strToDateTime', () => { + it('should конвертировать строку в дату через strToDateTime', () => { const dateStr = '2023-05-15-14-30-45'; const format = 'yyyy-MM-dd-HH-mm-ss'; @@ -362,7 +362,7 @@ describe('utils', () => { expect(result.getSeconds()).toBe(45); }); - it('должен обрабатывать различные форматы разделителей в strToDateTime', () => { + it('should обрабатывать различные форматы разделителей в strToDateTime', () => { const dateStr = '2023/05/15 14:30:45'; const format = 'yyyy/MM/dd HH:mm:ss'; @@ -377,13 +377,13 @@ describe('utils', () => { expect(result.getSeconds()).toBe(45); }); - it('должен выбрасывать ошибку при некорректной дате в strToDateTime', () => { + it('should throw error при некорректной дате в strToDateTime', () => { expect(() => { utils.strToDateTime('2023-13-32', 'yyyy-MM-dd'); }).toThrow(); }); - it('должен конвертировать строку во время через strToTime', () => { + it('should конвертировать строку во время через strToTime', () => { const timeStr = '14:30:45'; const result = utils.strToTime(timeStr); @@ -394,7 +394,7 @@ describe('utils', () => { expect(result.getSeconds()).toBe(0); // Bug in implementation, should be 45 }); - it('должен выбрасывать ошибку при некорректном времени в strToTime', () => { + it('should throw error при некорректном времени в strToTime', () => { expect(() => { utils.strToTime('25:70'); }).toThrow(); diff --git a/easydata.js/packs/core/tests/value_editor.test.ts b/easydata.js/packs/core/tests/value_editor.test.ts index 79a3efad..70700007 100644 --- a/easydata.js/packs/core/tests/value_editor.test.ts +++ b/easydata.js/packs/core/tests/value_editor.test.ts @@ -6,7 +6,7 @@ import { EditorTag } from '../src/types/editor_tag'; import { DataType } from '../src/types/data_type'; describe('ValueEditor', () => { - it('должен создаваться с правильными значениями по умолчанию', () => { + it('should be created with correct default values', () => { const editor = new ValueEditor(); expect(editor.id).toBe(''); @@ -22,7 +22,7 @@ describe('ValueEditor', () => { expect(editor.processValues).toBeUndefined(); }); - it('должен загружать обязательные данные из DTO', () => { + it('should load required data from DTO', () => { const editor = new ValueEditor(); const dto: ValueEditorDTO = { id: 'test-editor', @@ -39,7 +39,7 @@ describe('ValueEditor', () => { expect(editor.defValue).toBe('default'); }); - it('должен загружать все данные из DTO', () => { + it('should load all data from DTO', () => { const editor = new ValueEditor(); const dto: ValueEditorDTO = { id: 'test-editor', @@ -60,7 +60,7 @@ describe('ValueEditor', () => { expect(editor.id).toBe('test-editor'); expect(editor.tag).toBe(EditorTag.MultipleChoice); - // subType должен перезаписать rtype + // subType should перезаписать rtype expect(editor.resType).toBe(DataType.Int32); expect(editor.defValue).toBe('default'); expect(editor.name).toBe('Test Editor'); @@ -72,7 +72,7 @@ describe('ValueEditor', () => { ]); }); - it('должен возвращать пустую строку для getValueText когда нет values', () => { + it('should return пустую строку для getValueText когда нет values', () => { const editor = new ValueEditor(); editor.id = 'test-editor'; @@ -80,7 +80,7 @@ describe('ValueEditor', () => { expect(result).toBe(''); }); - it('должен возвращать текст для строкового значения через getValueText', () => { + it('should return текст для строкового значения через getValueText', () => { const editor = new ValueEditor(); editor.values = [ { id: '1', text: 'Option 1' }, @@ -92,7 +92,7 @@ describe('ValueEditor', () => { expect(result).toBe('Option 2'); }); - it('должен возвращать пустую строку для несуществующего значения через getValueText', () => { + it('should return пустую строку для несуществующего значения через getValueText', () => { const editor = new ValueEditor(); editor.values = [ { id: '1', text: 'Option 1' }, @@ -103,7 +103,7 @@ describe('ValueEditor', () => { expect(result).toBe(''); }); - it('должен объединять тексты для массива значений через getValueText', () => { + it('should объединять тексты для массива значений через getValueText', () => { const editor = new ValueEditor(); editor.values = [ { id: '1', text: 'Option 1' }, @@ -115,7 +115,7 @@ describe('ValueEditor', () => { expect(result).toBe('Option 1,Option 3'); }); - it('должен возвращать только найденные тексты для массива значений через getValueText', () => { + it('should return только найденные тексты для массива значений через getValueText', () => { const editor = new ValueEditor(); editor.values = [ { id: '1', text: 'Option 1' }, @@ -127,7 +127,7 @@ describe('ValueEditor', () => { expect(result).toBe('Option 1,Option 3'); }); - it('должен обрабатывать случай когда в values пустой массив', () => { + it('should обрабатывать случай когда в values пустой массив', () => { const editor = new ValueEditor(); editor.values = []; @@ -138,7 +138,7 @@ describe('ValueEditor', () => { expect(result2).toBe(''); }); - it('должен загружать данные даже если DTO неполное', () => { + it('should загружать данные даже если DTO неполное', () => { const editor = new ValueEditor(); const incompleteDTO = { id: 'test-editor' @@ -153,7 +153,7 @@ describe('ValueEditor', () => { expect(editor.defValue).toBe(''); }); - it('должен не менять текущие значения если DTO равно null или undefined', () => { + it('should не менять текущие значения если DTO равно null или undefined', () => { const editor = new ValueEditor(); editor.id = 'existing-id'; editor.tag = EditorTag.Edit; diff --git a/easydata.js/packs/crud/tests/data_context.test.ts b/easydata.js/packs/crud/tests/data_context.test.ts index 1bb999a6..ae4d6b04 100644 --- a/easydata.js/packs/crud/tests/data_context.test.ts +++ b/easydata.js/packs/crud/tests/data_context.test.ts @@ -17,13 +17,13 @@ describe('DataContext', () => { let processEndCount: number; beforeEach(() => { - // Создаем мок для HttpClient + // Create mock for HttpClient mockHttpClient = { get: mock(), post: mock(), } as unknown as HttpClient; - // Создаем мок для HttpActionResult + // Create mock for HttpActionResult mockActionResult = { then: mock().mockImplementation((callback) => { callback({ @@ -53,11 +53,11 @@ describe('DataContext', () => { (mockHttpClient.get as jest.Mock).mockReturnValue(mockActionResult); (mockHttpClient.post as jest.Mock).mockReturnValue(mockActionResult); - // Счетчики для методов onProcessStart и onProcessEnd + // Counters for onProcessStart and onProcessEnd methods processStartCount = 0; processEndCount = 0; - // Создаем экземпляр DataContext с моками + // Create DataContext instance with mocks dataContext = new DataContext({ metaDataId: 'test-model', endpoint: '/api/test', @@ -65,12 +65,12 @@ describe('DataContext', () => { onProcessEnd: () => { processEndCount++; } }); - // Подменяем HttpClient в DataContext на наш мок + // Replace HttpClient in DataContext with our mock (dataContext as any).http = mockHttpClient; }); - it('должен инициализироваться с правильными значениями', () => { - // Проверка базовых свойств + it('should initialize with correct values', () => { + // Check basic properties const metaData = dataContext.getMetaData(); expect(metaData).toBeInstanceOf(MetaData); expect(metaData.id).toBe('test-model'); @@ -82,8 +82,8 @@ describe('DataContext', () => { expect(dataLoader).toBeInstanceOf(EasyDataServerLoader); }); - it('должен правильно настраивать эндпоинты по умолчанию', () => { - // Проверка эндпоинтов по умолчанию + it('should correctly set default endpoints', () => { + // Check default endpoints expect(() => { const endpoint = dataContext.resolveEndpoint('GetMetaData'); expect(endpoint).toBe('/api/test/models/test-model'); @@ -91,58 +91,58 @@ describe('DataContext', () => { expect(() => { const endpoint = dataContext.resolveEndpoint('FetchDataset'); - // На данном этапе activeEntity не установлен, поэтому должна быть ошибка - // Но сам эндпоинт должен существовать + // At this stage activeEntity is not set, so there should be an error + // But the endpoint itself should exist }).toThrow(); }); - it('должен устанавливать и получать активную сущность', () => { - // Загружаем метаданные + it('should set and get active entity', () => { + // Load metadata dataContext.loadMetaData().then(() => { - // Устанавливаем активную сущность + // Set active entity dataContext.setActiveSource('entity1'); - // Проверяем, что активная сущность установлена + // Check, что активная сущность установлена const activeEntity = dataContext.getActiveEntity(); expect(activeEntity).toBeDefined(); expect(activeEntity.id).toBe('entity1'); }); }); - it('должен устанавливать кастомные эндпоинты', () => { - // Устанавливаем кастомный эндпоинт + it('should set custom endpoints', () => { + // Set кастомный эндпоинт dataContext.setEndpoint('GetMetaData', '/custom/api/metadata'); - // Проверяем что эндпоинт установлен + // Check что эндпоинт установлен const endpoint = dataContext.resolveEndpoint('GetMetaData'); expect(endpoint).toBe('/custom/api/metadata'); }); - it('не должен перезаписывать существующие эндпоинты при использовании setEnpointIfNotExist', () => { - // Устанавливаем эндпоинт + it('should not overwrite existing endpoints when using setEndpointIfNotExist', () => { + // Set эндпоинт dataContext.setEndpoint('GetMetaData', '/custom/api/metadata'); // Пытаемся установить другой эндпоинт через setEnpointIfNotExist dataContext.setEnpointIfNotExist('GetMetaData', '/another/endpoint'); - // Проверяем что эндпоинт не изменился + // Check что эндпоинт не изменился const endpoint = dataContext.resolveEndpoint('GetMetaData'); expect(endpoint).toBe('/custom/api/metadata'); }); - it('должен разрешать эндпоинты с параметрами', () => { - // Устанавливаем эндпоинт с параметрами + it('should resolve endpoints with parameters', () => { + // Set эндпоинт с параметрами dataContext.setEndpoint('CustomEndpoint', '/api/custom/{param1}/{param2}'); // Разрешаем эндпоинт с параметрами const endpoint = dataContext.resolveEndpoint('CustomEndpoint', { param1: 'value1', param2: 'value2' }); - // Проверяем что параметры подставлены + // Check что параметры подставлены expect(endpoint).toBe('/api/custom/value1/value2'); }); - it('должен выбрасывать ошибку при отсутствии обязательного параметра', () => { - // Устанавливаем эндпоинт с параметрами + it('should throw error when required parameter is missing', () => { + // Set эндпоинт с параметрами dataContext.setEndpoint('CustomEndpoint', '/api/custom/{param1}/{param2}'); // Пытаемся разрешить эндпоинт с отсутствующим параметром @@ -151,26 +151,26 @@ describe('DataContext', () => { }).toThrow('Parameter [param2] is not defined'); }); - it('должен загружать метаданные', () => { + it('should load metadata', () => { const promise = dataContext.loadMetaData(); - // Проверяем что загрузка вызвала правильный метод HTTP клиента + // Check что загрузка вызвала правильный метод HTTP клиента expect(mockHttpClient.get).toHaveBeenCalled(); expect((mockHttpClient.get as jest.Mock).mock.calls[0][0]).toContain('/api/test/models/test-model'); return promise.then((model) => { - // Проверяем что модель была загружена + // Check что модель была загружена expect(model).toBeDefined(); expect(model.id).toBe('test-model'); - // Проверяем что были вызваны методы startProcess и endProcess + // Check что были вызваны методы startProcess и endProcess expect(processStartCount).toBe(1); expect(processEndCount).toBe(1); }); }); - it('должен создавать фильтр данных', () => { - // Загружаем метаданные и устанавливаем активную сущность + it('should create data filter', () => { + // Load metadata and set active entity return dataContext.loadMetaData() .then(() => { dataContext.setActiveSource('entity1'); @@ -178,13 +178,13 @@ describe('DataContext', () => { // Создаем фильтр const filter = dataContext.createFilter(); - // Проверяем созданный фильтр + // Check созданный фильтр expect(filter).toBeInstanceOf(TextDataFilter); }); }); - it('должен загружать датасет', () => { - // Настраиваем мок для loadChunk + it('should load dataset', () => { + // Configure mock for loadChunk const mockData = new EasyDataTable(); const mockColumns = new DataColumnList(); mockColumns.add({ id: 'id', label: 'ID', type: DataType.Int32 }); @@ -198,7 +198,7 @@ describe('DataContext', () => { total: 1 }); - // Загружаем метаданные и устанавливаем активную сущность + // Load metadata and set active entity return dataContext.loadMetaData() .then(() => { dataContext.setActiveSource('entity1'); @@ -207,27 +207,27 @@ describe('DataContext', () => { return dataContext.fetchDataset(); }) .then((data) => { - // Проверяем результат + // Check результат expect(data).toBeDefined(); expect(data.columns.count).toBe(2); expect(data.getCachedCount()).toBe(1); - // Проверяем что dataLoader.loadChunk был вызван + // Check что dataLoader.loadChunk был вызван expect(dataLoader.loadChunk).toHaveBeenCalled(); }); }); - it('должен получать запись', () => { - // Загружаем метаданные и устанавливаем активную сущность + it('should get record', () => { + // Load metadata and set active entity return dataContext.loadMetaData() .then(() => { dataContext.setActiveSource('entity1'); - // Получаем запись + // Get запись return dataContext.fetchRecord({ id: 1 }); }) .then(() => { - // Проверяем что был вызван правильный метод HTTP клиента + // Check что был вызван правильный метод HTTP клиента expect(mockHttpClient.get).toHaveBeenCalled(); const lastCall = (mockHttpClient.get).mock.calls.pop(); @@ -235,26 +235,26 @@ describe('DataContext', () => { expect(lastCall[1].queryParams).toBeDefined(); expect(lastCall[1].queryParams.id).toBe(1); - // Проверяем что были вызваны методы startProcess и endProcess + // Check что были вызваны методы startProcess и endProcess expect(processStartCount).toBeGreaterThan(0); expect(processEndCount).toBeGreaterThan(0); }); }); - it('должен создавать запись', () => { - // Загружаем метаданные и устанавливаем активную сущность + it('should create record', () => { + // Load metadata and set active entity return dataContext.loadMetaData() .then(() => { dataContext.setActiveSource('entity1'); - // Тестовый объект для создания + // Test object for creation const testObj = { name: 'Test Record', value: 123 }; // Создаем запись return dataContext.createRecord(testObj); }) .then(() => { - // Проверяем что был вызван правильный метод HTTP клиента + // Check что был вызван правильный метод HTTP клиента expect(mockHttpClient.post).toHaveBeenCalled(); const lastCall = (mockHttpClient.post).mock.calls.pop(); @@ -263,26 +263,26 @@ describe('DataContext', () => { expect(lastCall[1].name).toBe('Test Record'); expect(lastCall[1].value).toBe(123); - // Проверяем что были вызваны методы startProcess и endProcess + // Check что были вызваны методы startProcess и endProcess expect(processStartCount).toBeGreaterThan(0); expect(processEndCount).toBeGreaterThan(0); }); }); - it('должен обновлять запись', () => { - // Загружаем метаданные и устанавливаем активную сущность + it('should update record', () => { + // Load metadata and set active entity return dataContext.loadMetaData() .then(() => { dataContext.setActiveSource('entity1'); - // Тестовый объект для обновления + // Test object for update const testObj = { id: 1, name: 'Updated Record', value: 456 }; // Обновляем запись return dataContext.updateRecord(testObj); }) .then(() => { - // Проверяем что был вызван правильный метод HTTP клиента + // Check что был вызван правильный метод HTTP клиента expect(mockHttpClient.post).toHaveBeenCalled(); const lastCall = (mockHttpClient.post).mock.calls.pop(); @@ -291,26 +291,26 @@ describe('DataContext', () => { expect(lastCall[1].id).toBe(1); expect(lastCall[1].name).toBe('Updated Record'); - // Проверяем что были вызваны методы startProcess и endProcess + // Check что были вызваны методы startProcess и endProcess expect(processStartCount).toBeGreaterThan(0); expect(processEndCount).toBeGreaterThan(0); }); }); - it('должен удалять запись', () => { - // Загружаем метаданные и устанавливаем активную сущность + it('should delete record', () => { + // Load metadata and set active entity return dataContext.loadMetaData() .then(() => { dataContext.setActiveSource('entity1'); - // Тестовый объект для удаления + // Test object for deletion const testObj = { id: 1 }; - // Удаляем запись + // Remove запись return dataContext.deleteRecord(testObj); }) .then(() => { - // Проверяем что был вызван правильный метод HTTP клиента + // Check что был вызван правильный метод HTTP клиента expect(mockHttpClient.post).toHaveBeenCalled(); const lastCall = (mockHttpClient.post).mock.calls.pop(); @@ -318,13 +318,13 @@ describe('DataContext', () => { expect(lastCall[1]).toBeDefined(); expect(lastCall[1].id).toBe(1); - // Проверяем что были вызваны методы startProcess и endProcess + // Check что были вызваны методы startProcess и endProcess expect(processStartCount).toBeGreaterThan(0); expect(processEndCount).toBeGreaterThan(0); }); }); - it('должен возвращать HttpClient', () => { + it('should return HttpClient', () => { const httpClient = dataContext.getHttpClient(); expect(httpClient).toBe(mockHttpClient); }); diff --git a/easydata.js/packs/crud/tests/easy_data_view_dispatcher.test.ts b/easydata.js/packs/crud/tests/easy_data_view_dispatcher.test.ts index e9c800ef..1e21f6d9 100644 --- a/easydata.js/packs/crud/tests/easy_data_view_dispatcher.test.ts +++ b/easydata.js/packs/crud/tests/easy_data_view_dispatcher.test.ts @@ -8,17 +8,17 @@ import { RootDataView } from '../src/views/root_data_view'; import * as utils from '../src/utils/utils'; describe('EasyDataViewDispatcher', () => { - // Оригинальные объекты для восстановления после тестов + // Original objects for restoration after tests const originalLocation = window.location; const originalWindowAddEventListener = window.addEventListener; const originalWindowRemoveEventListener = window.removeEventListener; - // Мок элементы для DOM + // Mock elements for DOM let mockContainer: HTMLElement; let mockParent: HTMLElement; beforeEach(() => { - // Создаем моки для DOM элементов + // Create mocks for DOM elements mockContainer = document.createElement('div'); mockContainer.id = 'testContainer'; @@ -26,7 +26,7 @@ describe('EasyDataViewDispatcher', () => { mockParent.appendChild(mockContainer); document.body.appendChild(mockParent); - // Мок для window.location + // Mock for window.location delete (window as any).location; window.location = { ...originalLocation, @@ -34,57 +34,57 @@ describe('EasyDataViewDispatcher', () => { href: 'http://example.com/app/easydata/entity1' } as Location; - // Моки для addEventListener и removeEventListener + // Mocks for addEventListener and removeEventListener window.addEventListener = mock(); window.removeEventListener = mock(); - // Мок для loadMetaData + // Mock for loadMetaData // jest.spyOn(DataContext.prototype, 'loadMetaData').mockImplementation(() => { // return Promise.resolve(new MetaData()); // }); // - // // Мок для setActiveSource + // // Mock for setActiveSource // jest.spyOn(DataContext.prototype, 'setActiveSource').mockImplementation(() => {}); // - // // Мок для setLocation + // // Mock for setLocation // jest.spyOn(utils, 'setLocation').mockImplementation(() => {}); // - // // Сохраняем оригинальные конструкторы представлений + // // Save original view constructors // jest.spyOn(EntityDataView.prototype, 'constructor').mockImplementation(() => {}); // jest.spyOn(RootDataView.prototype, 'constructor').mockImplementation(() => {}); }); afterEach(() => { - // Восстанавливаем оригинальные объекты и методы + // Restore original objects and methods window.location = originalLocation; window.addEventListener = originalWindowAddEventListener; window.removeEventListener = originalWindowRemoveEventListener; - // Удаляем добавленные элементы + // Remove added elements if (mockParent.parentNode) { mockParent.parentNode.removeChild(mockParent); } - // Сбрасываем все моки + // Reset all mocks // jest.restoreAllMocks(); - // Удаляем глобальную переменную EDView + // Remove global EDView variable delete window['EDView']; }); - it('должен быть создан с настройками по умолчанию', () => { + it('should be created with default settings', () => { const dispatcher = new EasyDataViewDispatcher(); expect(dispatcher).toBeDefined(); - // Проверяем private свойства через any + // Check private properties through any const options = (dispatcher as any).options; expect(options).toBeDefined(); expect(options.container).toBe('#EasyDataContainer'); expect(options.basePath).toBe('easydata'); }); - it('должен быть создан с пользовательскими настройками', () => { + it('should be created with custom settings', () => { const dispatcher = new EasyDataViewDispatcher({ container: '#customContainer', basePath: 'custom-path', @@ -102,7 +102,7 @@ describe('EasyDataViewDispatcher', () => { expect(options.showBackToEntities).toBe(true); }); - it('должен корректно обрабатывать настройку rootEntity', () => { + it('should correctly handle rootEntity configuration', () => { const dispatcher = new EasyDataViewDispatcher({ container: '#testContainer', rootEntity: 'entity1' @@ -113,23 +113,23 @@ describe('EasyDataViewDispatcher', () => { expect(options.rootEntity).toBe('entity1'); expect(options.showBackToEntities).toBe(false); - // basePath должен быть установлен в "/" + // basePath should be set to "/" expect((dispatcher as any).basePath).toBe('/'); }); - it('должен корректно нормализовать базовый путь', () => { + it('should correctly normalize base path', () => { const dispatcher = new EasyDataViewDispatcher({ container: '#testContainer', basePath: 'easydata' }); - // Используя приватный метод напрямую через any + // Using private method directly through any expect((dispatcher as any).normalizeBasePath('easydata')).toBe('/app/easydata'); expect((dispatcher as any).normalizeBasePath('/easydata/')).toBe('/app/easydata'); expect((dispatcher as any).normalizeBasePath('nonexistent')).toBe('/'); }); - it('должен корректно обрезать слеши в пути', () => { + it('should correctly trim slashes in path', () => { const dispatcher = new EasyDataViewDispatcher({ container: '#testContainer' }); @@ -142,7 +142,7 @@ describe('EasyDataViewDispatcher', () => { expect((dispatcher as any).trimSlashes('')).toBe(''); }); - it('должен корректно устанавливать контейнер по ID', () => { + it('should correctly set container by ID', () => { const dispatcher = new EasyDataViewDispatcher({ container: '#testContainer' }); @@ -150,8 +150,8 @@ describe('EasyDataViewDispatcher', () => { expect((dispatcher as any).container).toBe(mockContainer); }); - it('должен корректно устанавливать контейнер по классу', () => { - // Добавляем класс контейнеру + it('should correctly set container by class', () => { + // Add class to container mockContainer.className = 'test-class'; const dispatcher = new EasyDataViewDispatcher({ @@ -161,7 +161,7 @@ describe('EasyDataViewDispatcher', () => { expect((dispatcher as any).container).toBe(mockContainer); }); - it('должен корректно устанавливать контейнер как HTML-элемент', () => { + it('should correctly set container as HTML element', () => { const dispatcher = new EasyDataViewDispatcher({ container: mockContainer }); @@ -169,7 +169,7 @@ describe('EasyDataViewDispatcher', () => { expect((dispatcher as any).container).toBe(mockContainer); }); - it('должен выбрасывать ошибку при некорректном контейнере', () => { + it('should throw error with incorrect container', () => { expect(() => { new EasyDataViewDispatcher({ container: '#nonexistentContainer' @@ -183,7 +183,7 @@ describe('EasyDataViewDispatcher', () => { }).toThrow('Container is undefined'); }); - it('должен корректно определять ID активного источника', () => { + it('should correctly determine active source ID', () => { const dispatcher = new EasyDataViewDispatcher({ container: '#testContainer', basePath: 'easydata' @@ -191,7 +191,7 @@ describe('EasyDataViewDispatcher', () => { expect((dispatcher as any).getActiveSourceId()).toBe('entity1'); - // Меняем путь на корневой + // Change path to root window.location = { ...window.location, pathname: '/app/easydata', @@ -201,7 +201,7 @@ describe('EasyDataViewDispatcher', () => { expect((dispatcher as any).getActiveSourceId()).toBeNull(); }); - it('должен корректно определять ID активного источника с rootEntity', () => { + it('should correctly determine active source ID with rootEntity', () => { const dispatcher = new EasyDataViewDispatcher({ container: '#testContainer', rootEntity: 'rootEntity' @@ -210,7 +210,7 @@ describe('EasyDataViewDispatcher', () => { expect((dispatcher as any).getActiveSourceId()).toBe('rootEntity'); }); - it('должен запускаться и загружать метаданные', async () => { + it('should start and load metadata', async () => { // const dispatcher = new EasyDataViewDispatcher({ // container: '#testContainer' // }); @@ -223,20 +223,20 @@ describe('EasyDataViewDispatcher', () => { // expect(setActiveViewSpy).toHaveBeenCalled(); }); - it('должен устанавливать активное представление Entity', async () => { + it('should set active Entity view', async () => { const dispatcher = new EasyDataViewDispatcher({ container: '#testContainer' }); await dispatcher.run(); - // Так как путь включает ID сущности, должен быть создан EntityDataView + // Since path includes entity ID, EntityDataView should be created expect(DataContext.prototype.setActiveSource).toHaveBeenCalledWith('entity1'); expect(window['EDView']).toBeDefined(); }); - it('должен устанавливать активное представление Root', async () => { - // Меняем путь на корневой + it('should set active Root view', async () => { + // Change path to root window.location = { ...window.location, pathname: '/app/easydata', @@ -249,23 +249,23 @@ describe('EasyDataViewDispatcher', () => { await dispatcher.run(); - // Так как путь не включает ID сущности, должен быть создан RootDataView + // Since path does not include entity ID, RootDataView should be created expect(DataContext.prototype.setActiveSource).not.toHaveBeenCalled(); expect(window['EDView']).toBeDefined(); }); - it('должен подключать слушатели событий при запуске', async () => { + it('should connect event listeners on startup', async () => { const dispatcher = new EasyDataViewDispatcher({ container: '#testContainer' }); await dispatcher.run(); - // Проверяем, что были добавлены слушатели для событий + // Check that event listeners were added expect(window.addEventListener).toHaveBeenCalledWith(['ed_set_location', 'popstate']); }); - it('должен отключать слушатели событий при detach', async () => { + it('should remove event listeners on detach', async () => { const dispatcher = new EasyDataViewDispatcher({ container: '#testContainer' }); @@ -274,31 +274,31 @@ describe('EasyDataViewDispatcher', () => { dispatcher.detach(); - // Проверяем, что были удалены слушатели событий + // Check that event listeners were removed expect(window.removeEventListener).toHaveBeenCalledWith(['ed_set_location', 'popstate']); }); - it('должен очищать контейнер и данные при смене представления', async () => { + it('should clear container and data on view change', async () => { const dispatcher = new EasyDataViewDispatcher({ container: '#testContainer' }); - // Добавляем содержимое в контейнер + // Add content to container mockContainer.innerHTML = '
Test content
'; - // Мок для метода clear у таблицы данных + // Mock for data table clear method const clearMock = mock(); (dispatcher as any).context.getData = mock().mockReturnValue({ clear: clearMock }); - // Вызываем метод setActiveView напрямую + // Call setActiveView method directly (dispatcher as any).setActiveView(); - // Проверяем, что контейнер был очищен + // Check that container was cleared expect(mockContainer.innerHTML).toBe(''); - // Проверяем, что данные были очищены + // Check that data was cleared expect(clearMock).toHaveBeenCalled(); }); }); diff --git a/easydata.js/packs/crud/tests/entity_data_view.test.ts b/easydata.js/packs/crud/tests/entity_data_view.test.ts index c5579a46..866a1d8f 100644 --- a/easydata.js/packs/crud/tests/entity_data_view.test.ts +++ b/easydata.js/packs/crud/tests/entity_data_view.test.ts @@ -16,7 +16,7 @@ import { TextFilterWidget } from '../src/widgets/text_filter_widget'; import * as utils from '../src/utils/utils'; describe('EntityDataView', () => { - // Моки для DOM и объектов + // Mocks for DOM and objects let mockSlot: HTMLElement; let mockContext: DataContext; let mockDialogService: DialogService; @@ -27,7 +27,7 @@ describe('EntityDataView', () => { let mockFilterWidget: TextFilterWidget; let view: EntityDataView; - // Вспомогательная функция для создания мока атрибута + // Helper function to create mock attribute function createMockAttr(id: string, options: any = {}): MetaEntityAttr { return { id, @@ -39,11 +39,11 @@ describe('EntityDataView', () => { } beforeEach(() => { - // Мок для HTMLElement + // Mock for HTMLElement mockSlot = document.createElement('div'); document.body.appendChild(mockSlot); - // Мок для атрибутов сущности + // Mock for entity attributes const mockAttrs = [ createMockAttr('Entity.id', { caption: 'ID', dataType: DataType.Int32, isPrimaryKey: true }), createMockAttr('Entity.name', { caption: 'Name', dataType: DataType.String }), @@ -51,7 +51,7 @@ describe('EntityDataView', () => { createMockAttr('Entity.active', { caption: 'Active', dataType: DataType.Bool }) ]; - // Мок для сущности + // Mock for entities mockEntity = { id: 'Entity', name: 'Entity', @@ -62,19 +62,19 @@ describe('EntityDataView', () => { getPrimaryAttrs: mock().mockReturnValue([mockAttrs[0]]) } as unknown as MetaEntity; - // Мок для метаданных + // Mock for metadata mockMetaData = { getAttributeById: (id: string) => mockAttrs.find(attr => attr.id === id) || null } as unknown as MetaData; - // Мок для колонок данных + // Mock for data columns const mockColumns = new DataColumnList(); mockColumns.add({ id: 'Entity.id', label: 'ID', type: DataType.Int32 }); mockColumns.add({ id: 'Entity.name', label: 'Name', type: DataType.String }); mockColumns.add({ id: 'Entity.description', label: 'Description', type: DataType.String }); mockColumns.add({ id: 'Entity.active', label: 'Active', type: DataType.Bool }); - // Мок для таблицы данных + // Mock for data table mockDataTable = { columns: mockColumns, getRow: mock().mockImplementation((rowIndex: number) => { @@ -94,7 +94,7 @@ describe('EntityDataView', () => { getCachedRows: mock().mockReturnValue([]) } as unknown as EasyDataTable; - // Мок для контекста данных + // Mock for data context mockContext = { getMetaData: mock().mockReturnValue(mockMetaData), getActiveEntity: mock().mockReturnValue(mockEntity), @@ -105,7 +105,7 @@ describe('EntityDataView', () => { deleteRecord: mock().mockResolvedValue({}) } as unknown as DataContext; - // Мок для диалогового сервиса + // Mock for dialog service mockDialogService = { open: mock().mockReturnValue({ submit: mock() @@ -120,7 +120,7 @@ describe('EntityDataView', () => { // (title, message) => mockDialogService.openConfirm(title, message) // ); - // Мок для EasyGrid + // Mock for EasyGrid mockGrid = { refresh: mock(), getData: mock().mockReturnValue(mockDataTable) @@ -131,7 +131,7 @@ describe('EntityDataView', () => { writable: true }); - // Мок для TextFilterWidget + // Mock for TextFilterWidget mockFilterWidget = { applyFilter: mock().mockReturnValue(true) } as unknown as TextFilterWidget; @@ -140,7 +140,7 @@ describe('EntityDataView', () => { // (refresh) => mockFilterWidget.applyFilter(refresh) // ); - // Мок для i18n + // Mock for i18n // jest.spyOn(i18n, 'getText').mockImplementation((key: string) => { // if (key === 'BackToEntities') return 'Back to Entities'; // if (key === 'AddRecordBtnTitle') return 'Add Record'; @@ -153,48 +153,48 @@ describe('EntityDataView', () => { // return key; // }); // - // // Мок для utils.setLocation + // // Mock for utils.setLocation // jest.spyOn(utils, 'setLocation').mockImplementation(() => {}); }); afterEach(() => { - // Удаляем добавленные элементы + // Remove added elements if (mockSlot.parentNode) { mockSlot.parentNode.removeChild(mockSlot); } - // Сбрасываем моки + // Reset mocks // jest.restoreAllMocks(); }); - it('должен создаваться с правильными настройками по умолчанию', () => { + it('should be created with correct default settings', () => { view = new EntityDataView(mockSlot, mockContext, '/basePath', {}); - // Проверяем, что контекст установлен + // Check that context is set expect((view as any).context).toBe(mockContext); - // Проверяем, что базовый путь установлен + // Check that base path is set expect((view as any).basePath).toBe('/basePath'); - // Проверяем опции по умолчанию + // Check default options const options = (view as any).options; expect(options).toBeDefined(); expect(options.showFilterBox).toBe(true); expect(options.showBackToEntities).toBe(true); }); - it('должен рендерить заголовок и кнопку возврата', () => { + it('should render title and back button', () => { view = new EntityDataView(mockSlot, mockContext, '/basePath', {}); - // Проверяем, что заголовок содержит название сущности + // Check, что заголовок содержит название сущности expect(mockSlot.innerHTML).toContain('

Entities

'); - // Проверяем наличие кнопки возврата + // Check наличие кнопки возврата const backLink = mockSlot.querySelector('a'); expect(backLink).toBeDefined(); expect(backLink.textContent).toBe('← Back to Entities'); - // Проверяем, что обработчик клика установлен + // Check, что обработчик клика установлен const clickEvent = new MouseEvent('click'); const preventDefaultSpy = jest.spyOn(clickEvent, 'preventDefault'); backLink.dispatchEvent(clickEvent); @@ -203,67 +203,67 @@ describe('EntityDataView', () => { expect(utils.setLocation).toHaveBeenCalledWith('/basePath'); }); - it('не должен рендерить кнопку возврата если showBackToEntities=false', () => { + it('should not render back button if showBackToEntities=false', () => { view = new EntityDataView(mockSlot, mockContext, '/basePath', { showBackToEntities: false }); - // Проверяем отсутствие кнопки возврата + // Check отсутствие кнопки возврата const backLink = mockSlot.querySelector('a'); expect(backLink).toBeNull(); }); - it('должен вызывать fetchDataset и создавать грид', () => { + it('should call fetchDataset and create grid', () => { view = new EntityDataView(mockSlot, mockContext, '/basePath', {}); - // Проверяем, что fetchDataset был вызван + // Check, что fetchDataset был вызван expect(mockContext.fetchDataset).toHaveBeenCalled(); }); - it('должен создавать фильтр если showFilterBox=true', () => { - // Подменяем setTimeout, чтобы дождаться асинхронных операций + it('should create filter if showFilterBox=true', () => { + // Replace setTimeout to wait for async operations jest.useFakeTimers(); view = new EntityDataView(mockSlot, mockContext, '/basePath', { showFilterBox: true }); jest.runAllTimers(); - // Проверяем создание фильтра + // Check создание фильтра expect(mockContext.createFilter).toHaveBeenCalled(); }); - it('не должен создавать фильтр если showFilterBox=false', () => { + it('should not create filter if showFilterBox=false', () => { jest.useFakeTimers(); view = new EntityDataView(mockSlot, mockContext, '/basePath', { showFilterBox: false }); jest.runAllTimers(); - // Проверяем что фильтр не создается + // Check that filter is not created expect(mockContext.createFilter).not.toHaveBeenCalled(); }); - it('должен правильно обрабатывать клик по кнопке добавления', () => { + it('should correctly handle add button click', () => { view = new EntityDataView(mockSlot, mockContext, '/basePath', {}); - // Напрямую вызываем обработчик клика кнопки добавления + // Directly call обработчик клика кнопки добавления (view as any).addClickHandler(); - // Проверяем вызов диалога + // Check вызов диалога expect(mockDialogService.open).toHaveBeenCalled(); const openArgs = (mockDialogService.open as jest.Mock).mock.calls[0][0]; expect(openArgs).toBeObject(); expect(openArgs.title).toBe('Add Entity'); }); - it('должен правильно обрабатывать клик по кнопке редактирования', () => { + it('should correctly handle edit button click', () => { view = new EntityDataView(mockSlot, mockContext, '/basePath', {}); - // Напрямую вызываем обработчик клика кнопки редактирования + // Directly call обработчик клика кнопки редактирования (view as any).editClickHandler(new MouseEvent('click'), 0); - // Проверяем вызов getRow + // Check вызов getRow expect(mockDataTable.getRow).toHaveBeenCalledWith(0); - // Проверяем что диалог редактирования открывается + // Check что диалог редактирования открывается return mockDataTable.getRow(0).then(() => { expect(mockDialogService.open).toHaveBeenCalled(); const openArgs = (mockDialogService.open as jest.Mock).mock.calls[0][0]; @@ -272,67 +272,67 @@ describe('EntityDataView', () => { }); }); - it('должен правильно обрабатывать клик по кнопке удаления', () => { + it('should correctly handle delete button click', () => { view = new EntityDataView(mockSlot, mockContext, '/basePath', {}); - // Напрямую вызываем обработчик клика кнопки удаления + // Directly call обработчик клика кнопки удаления (view as any).deleteClickHandler(new MouseEvent('click'), 0); - // Проверяем вызов getRow + // Check вызов getRow expect(mockDataTable.getRow).toHaveBeenCalledWith(0); - // Проверяем открытие диалога подтверждения + // Check открытие диалога подтверждения return mockDataTable.getRow(0).then(() => { expect(mockDialogService.openConfirm).toHaveBeenCalled(); - // Проверяем вызов deleteRecord после подтверждения + // Check deleteRecord call after confirmation return mockDialogService.openConfirm("", "").then(() => { expect(mockContext.deleteRecord).toHaveBeenCalledWith({ id: 1 }); }); }); }); - it('должен обновлять данные после операций CRUD', () => { + it('should update data after CRUD operations', () => { view = new EntityDataView(mockSlot, mockContext, '/basePath', {}); - // Подменяем filterWidget + // Replace filterWidget (view as any).filterWidget = mockFilterWidget; - // Напрямую вызываем refreshData + // Directly call refreshData return (view as any).refreshData().then(() => { - // Проверяем вызов fetchDataset + // Check вызов fetchDataset expect(mockContext.fetchDataset).toHaveBeenCalled(); - // Проверяем применение фильтра + // Check применение фильтра expect(mockFilterWidget.applyFilter).toHaveBeenCalledWith(false); }); }); - it('должен обновлять грид, если фильтр не применен', () => { + it('should update grid if filter is not applied', () => { view = new EntityDataView(mockSlot, mockContext, '/basePath', {}); - // Подменяем filterWidget с флагом что фильтр не был применен + // Replace filterWidget с флагом что фильтр не был применен (view as any).filterWidget = { applyFilter: mock().mockReturnValue(false) }; - // Напрямую вызываем refreshData + // Directly call refreshData return (view as any).refreshData().then(() => { - // Проверяем вызов refresh у грида + // Check вызов refresh у грида expect(mockGrid.refresh).toHaveBeenCalled(); }); }); - it('должен корректно обрабатывать ошибки', () => { + it('should correctly handle errors', () => { view = new EntityDataView(mockSlot, mockContext, '/basePath', {}); // Создаем ошибку const error = new Error('Test error'); - // Напрямую вызываем обработчик ошибок + // Directly call обработчик ошибок (view as any).processError(error); - // Проверяем открытие диалога с ошибкой + // Check открытие диалога с ошибкой expect(mockDialogService.open).toHaveBeenCalled(); const openArgs = (mockDialogService.open as jest.Mock).mock.calls[0][0]; expect(openArgs).toBeObject(); @@ -340,65 +340,65 @@ describe('EntityDataView', () => { expect(openArgs.body).toBe('Test error'); }); - it('должен корректно управлять рендерером ячеек', () => { + it('should correctly управлять рендерером ячеек', () => { view = new EntityDataView(mockSlot, mockContext, '/basePath', {}); - // Создаем колонку с номером строки + // Create column с номером строки const column: GridColumn = { isRowNum: true } as GridColumn; - // Создаем мок для defaultRenderer + // Create mock for defaultRenderer const defaultRenderer = mock() as GridCellRenderer; - // Вызываем метод manageCellRenderer + // Call метод manageCellRenderer const renderer = (view as any).manageCellRenderer(column, defaultRenderer); - // Проверяем, что возвращается функция рендерера + // Check that renderer function is returned expect(typeof renderer).toBe('function'); - // Проверяем, что ширина колонки установлена + // Check, что ширина колонки установлена expect(column.width).toBe(110); - // Подготавливаем элементы для проверки рендерера + // Prepare elements for renderer testing const cell = document.createElement('div'); const rowEl = document.createElement('tr'); rowEl.setAttribute('data-row-idx', '0'); - // Вызываем рендерер + // Call рендерер renderer('value', column, cell, rowEl); - // Проверяем, что в ячейке появились кнопки Edit и Delete + // Check, что в ячейке появились кнопки Edit и Delete expect(cell.innerHTML).toContain('Edit'); expect(cell.innerHTML).toContain('Delete'); }); - it('должен синхронизировать видимость колонок грида с метаданными', () => { + it('should synchronize grid column visibility with metadata', () => { view = new EntityDataView(mockSlot, mockContext, '/basePath', {}); - // Создаем колонку с dataColumn + // Create column с dataColumn const column: GridColumn = { dataColumn: { id: 'Entity.name' } } as GridColumn; - // Устанавливаем showOnView в false для атрибута + // Set showOnView в false для атрибута const attr = mockMetaData.getAttributeById('Entity.name'); attr.showOnView = false; - // Вызываем метод syncGridColumnHandler + // Call метод syncGridColumnHandler (view as any).syncGridColumnHandler(column); - // Проверяем, что видимость колонки установлена в соответствии с showOnView атрибута + // Check, что видимость колонки установлена в соответствии с showOnView атрибута expect(column.isVisible).toBe(false); }); - it('должен выбрасывать ошибку если активная сущность не найдена', () => { - // Подменяем getActiveEntity, чтобы вернуть null + it('should throw error если активная сущность не найдена', () => { + // Replace getActiveEntity to return null (mockContext.getActiveEntity as jest.Mock).mockReturnValue(null); - // Проверяем, что конструктор выбрасывает ошибку + // Check, что конструктор выбрасывает ошибку expect(() => { new EntityDataView(mockSlot, mockContext, '/basePath', {}); }).toThrow("Can't find active entity for " + window.location.pathname); diff --git a/easydata.js/packs/crud/tests/entity_edit_form.test.ts b/easydata.js/packs/crud/tests/entity_edit_form.test.ts index a3beb4c5..063bc5bb 100644 --- a/easydata.js/packs/crud/tests/entity_edit_form.test.ts +++ b/easydata.js/packs/crud/tests/entity_edit_form.test.ts @@ -18,7 +18,7 @@ describe('EntityEditForm', () => { let mockErrorsDiv: HTMLElement; beforeEach(() => { - // Создаем мок для метаданных с атрибутами + // Create mock for metadata with attributes mockMetaData = { getAttributeById: (id: string): MetaEntityAttr => { if (id === 'Person.name') { @@ -67,42 +67,42 @@ describe('EntityEditForm', () => { } } as MetaData; - // Создаем мок для контекста данных + // Create mock for data context context = { getMetaData: () => mockMetaData } as DataContext; - // Создаем HTML для формы + // Create HTML for form mockFormHtml = document.createElement('div'); mockErrorsDiv = document.createElement('div'); mockErrorsDiv.className = 'errors-block'; mockFormHtml.appendChild(mockErrorsDiv); - // Создаем форму + // Create form form = new EntityEditForm(context); - // Устанавливаем HTML через приватное свойство + // Set HTML through private property (form as any).setHtmlInt(mockFormHtml); }); - it('должен создаваться с контекстом данных', () => { + it('should be created with data context', () => { expect(form).toBeDefined(); expect((form as any).context).toBe(context); }); - it('должен возвращать HTML через getHtml', () => { + it('should return HTML through getHtml', () => { const html = form.getHtml(); expect(html).toBe(mockFormHtml); }); - it('должен считать форму валидной, если нет полей', () => { + it('should consider form valid if no fields', () => { const isValid = form.validate(); expect(isValid).toBe(true); expect(mockErrorsDiv.innerHTML).toBe(''); }); - it('должен валидировать поля формы', () => { - // Создаем тестовые поля + it('should validate form fields', () => { + // Create test fields const nameInput = document.createElement('input'); nameInput.name = 'Person.name'; nameInput.value = 'John'; @@ -113,16 +113,17 @@ describe('EntityEditForm', () => { ageInput.value = '25'; mockFormHtml.appendChild(ageInput); - // Проверяем валидацию + // Check validation const isValid = form.validate(); expect(isValid).toBe(true); expect(nameInput.classList.contains('is-valid')).toBe(true); expect(ageInput.classList.contains('is-valid')).toBe(true); }); - it('должен отображать ошибки валидации', () => { - // Создаем пользовательский валидатор, который считает поле "age" невалидным + it('should display validation errors', () => { + // Create custom validator that considers "age" field invalid const mockValidator: Validator = { + name: 'test-validator', validate: (attr: MetaEntityAttr, value: any): ValidationResult => { if (attr.id === 'Person.age' && parseInt(value) < 18) { return { @@ -137,10 +138,10 @@ describe('EntityEditForm', () => { } }; - // Добавляем валидатор к форме + // Add validator to form form.useValidator(mockValidator); - // Создаем тестовые поля + // Create test fields const nameInput = document.createElement('input'); nameInput.name = 'Person.name'; nameInput.value = 'John'; @@ -148,23 +149,23 @@ describe('EntityEditForm', () => { const ageInput = document.createElement('input'); ageInput.name = 'Person.age'; - ageInput.value = '16'; // Невалидное значение + ageInput.value = '16'; // Invalid value mockFormHtml.appendChild(ageInput); - // Проверяем валидацию + // Check validation const isValid = form.validate(); expect(isValid).toBe(false); expect(nameInput.classList.contains('is-valid')).toBe(true); expect(ageInput.classList.contains('is-invalid')).toBe(true); - // Проверяем, что ошибка отображается + // Check that error is displayed const errorsList = mockErrorsDiv.querySelector('ul'); expect(errorsList).toBeDefined(); expect(errorsList.innerHTML).toContain('Age: Age must be at least 18'); }); - it('должен получать данные из формы через getData', async () => { - // Создаем тестовые поля + it('should get data from form through getData', async () => { + // Create test fields const nameInput = document.createElement('input'); nameInput.name = 'Person.name'; nameInput.value = 'John'; @@ -181,18 +182,18 @@ describe('EntityEditForm', () => { isActiveInput.checked = true; mockFormHtml.appendChild(isActiveInput); - // Получаем данные формы + // Get form data const data = await form.getData(); - // Проверяем, что получен объект с правильными данными + // Check that we got object with correct data expect(data).toBeObject(); expect(data.name).toBe('John'); - expect(data.age).toBe(25); // Преобразовано в число + expect(data.age).toBe(25); // Converted to number expect(data.isActive).toBe(true); }); - it('должен обрабатывать различные типы данных в getData', async () => { - // Создаем поля разных типов + it('should handle different data types in getData', async () => { + // Create fields of different types const nameInput = document.createElement('input'); nameInput.name = 'Person.name'; nameInput.value = 'John'; @@ -213,23 +214,24 @@ describe('EntityEditForm', () => { descriptionTextarea.value = 'Some description'; mockFormHtml.appendChild(descriptionTextarea); - // Получаем данные формы + // Get form data const data = await form.getData(); - // Проверяем преобразование типов + // Check type conversion expect(data).toBeObject(); expect(data.name).toBe('John'); expect(data.age).toBe(25); - // Для даты проверяем, что значение не null + // For date check that value is not null expect(data.birthDate).not.toBeNull(); expect(data.description).toBe('Some description'); }); - it('должен использовать несколько валидаторов', () => { - // Создаем два пользовательских валидатора + it('should use multiple validators', () => { + // Create two custom validators const nameValidator: Validator = { + name: 'name-validator', validate: (attr: MetaEntityAttr, value: any): ValidationResult => { if (attr.id === 'Person.name' && (!value || value.length < 3)) { return { @@ -242,6 +244,7 @@ describe('EntityEditForm', () => { }; const ageValidator: Validator = { + name: 'age-validator', validate: (attr: MetaEntityAttr, value: any): ValidationResult => { if (attr.id === 'Person.age' && parseInt(value) < 18) { return { @@ -253,33 +256,34 @@ describe('EntityEditForm', () => { } }; - // Добавляем валидаторы к форме + // Add validators to form form.useValidators([nameValidator, ageValidator]); - // Создаем тестовые поля с невалидными значениями + // Create test fields with invalid values const nameInput = document.createElement('input'); nameInput.name = 'Person.name'; - nameInput.value = 'Jo'; // Слишком короткое имя + nameInput.value = 'Jo'; // Name too short mockFormHtml.appendChild(nameInput); const ageInput = document.createElement('input'); ageInput.name = 'Person.age'; - ageInput.value = '16'; // Слишком малый возраст + ageInput.value = '16'; // Age too low mockFormHtml.appendChild(ageInput); - // Проверяем валидацию + // Check validation const isValid = form.validate(); expect(isValid).toBe(false); - // Проверяем, что ошибки от обоих валидаторов отображаются + // Check that errors from both validators are displayed const errorsList = mockErrorsDiv.querySelector('ul'); expect(errorsList.innerHTML).toContain('Name: Name must be at least 3 characters'); expect(errorsList.innerHTML).toContain('Age: Age must be at least 18'); }); - it('должен очищать ошибки при повторной валидации', () => { - // Создаем пользовательский валидатор + it('should clear errors on re-validation', () => { + // Create custom validator const mockValidator: Validator = { + name: 'age-validator', validate: (attr: MetaEntityAttr, value: any): ValidationResult => { if (attr.id === 'Person.age' && parseInt(value) < 18) { return { @@ -293,32 +297,33 @@ describe('EntityEditForm', () => { form.useValidator(mockValidator); - // Создаем поле возраста с невалидным значением + // Create age field with invalid value const ageInput = document.createElement('input'); ageInput.name = 'Person.age'; ageInput.value = '16'; mockFormHtml.appendChild(ageInput); - // Проверяем первую валидацию - должны быть ошибки + // Check first validation - should have errors let isValid = form.validate(); expect(isValid).toBe(false); expect(ageInput.classList.contains('is-invalid')).toBe(true); expect(mockErrorsDiv.innerHTML).not.toBe(''); - // Меняем значение и валидируем снова + // Change value and validate again ageInput.value = '18'; isValid = form.validate(); - // Теперь должно быть валидно и ошибки должны исчезнуть + // Now should be valid and errors should disappear expect(isValid).toBe(true); expect(ageInput.classList.contains('is-valid')).toBe(true); expect(ageInput.classList.contains('is-invalid')).toBe(false); expect(mockErrorsDiv.innerHTML).toBe(''); }); - it('должен игнорировать чекбоксы при проверке на пустое значение', () => { - // Создаем валидатор, который проверяет, что все поля заполнены + it('should ignore checkboxes when checking for empty values', () => { + // Create validator that checks that all fields are filled const requiredValidator: Validator = { + name: 'required-validator', validate: (attr: MetaEntityAttr, value: any): ValidationResult => { if (!value) { return { @@ -332,11 +337,11 @@ describe('EntityEditForm', () => { form.useValidator(requiredValidator); - // Создаем чекбокс и текстовое поле + // Create checkbox and text field const isActiveInput = document.createElement('input'); isActiveInput.type = 'checkbox'; isActiveInput.name = 'Person.isActive'; - isActiveInput.checked = false; // Не выбран + isActiveInput.checked = false; // Not selected mockFormHtml.appendChild(isActiveInput); const nameInput = document.createElement('input'); @@ -344,7 +349,7 @@ describe('EntityEditForm', () => { nameInput.value = 'John'; mockFormHtml.appendChild(nameInput); - // Валидация должна пройти успешно, т.к. чекбоксы игнорируются + // Validation should pass successfully, because checkboxes are ignored const isValid = form.validate(); expect(isValid).toBe(true); }); diff --git a/easydata.js/packs/crud/tests/entity_form_builder.test.ts b/easydata.js/packs/crud/tests/entity_form_builder.test.ts index 9a124e77..5ea655be 100644 --- a/easydata.js/packs/crud/tests/entity_form_builder.test.ts +++ b/easydata.js/packs/crud/tests/entity_form_builder.test.ts @@ -15,7 +15,7 @@ describe('EntityEditFormBuilder', () => { let builder: EntityEditFormBuilder; let dataRow: DataRow; - // Вспомогательная функция для создания атрибута + // Helper function для создания атрибута const createAttr = (id: string, caption: string, dataType: DataType, options: any = {}): MetaEntityAttr => { const attr = { id, @@ -41,7 +41,7 @@ describe('EntityEditFormBuilder', () => { }; beforeEach(() => { - // Создаем атрибуты для нашей сущности + // Create attributes для нашей сущности const attributes = [ createAttr('Person.id', 'ID', DataType.Int32, { isPrimaryKey: true, @@ -85,7 +85,7 @@ describe('EntityEditFormBuilder', () => { }) ]; - // Создаем сущность с атрибутами + // Create entity with attributes mockEntity = { id: 'Person', name: 'Person', @@ -140,7 +140,7 @@ describe('EntityEditFormBuilder', () => { getValue: (id: string) => rowData[id] } as DataRow; - // Создаем мок для контекста данных + // Create mock for контекста данных mockDataContext = { getMetaData: () => mockMetaData, getActiveEntity: () => mockEntity, @@ -158,17 +158,17 @@ describe('EntityEditFormBuilder', () => { }) } as unknown as DataContext; - // Создаем экземпляр builder'а + // Create instance builder'а builder = new EntityEditFormBuilder(mockDataContext); }); - it('должен создаваться с пустыми параметрами и сбрасываться на значения по умолчанию', () => { + it('should создаваться с пустыми параметрами и сбрасываться на значения по умолчанию', () => { expect(builder).toBeDefined(); - // Проверяем, что свойство context установлено + // Check, что свойство context установлено expect((builder as any).context).toBe(mockDataContext); - // Проверяем метод reset + // Check метод reset const formBeforeReset = (builder as any).form; builder.reset(); const formAfterReset = (builder as any).form; @@ -177,25 +177,25 @@ describe('EntityEditFormBuilder', () => { expect(formAfterReset).toBeInstanceOf(EntityEditForm); }); - it('должен создавать форму с правильными полями', () => { + it('should создавать форму с правильными полями', () => { const form = builder.build(); - // Проверяем что форма создана + // Check что форма создана expect(form).toBeInstanceOf(EntityEditForm); - // Получаем HTML формы + // Get HTML формы const formHtml = form.getHtml(); expect(formHtml).toBeDefined(); - // Проверяем наличие блока для ошибок + // Check наличие блока для ошибок const errorsBlock = formHtml.querySelector('.errors-block'); expect(errorsBlock).toBeDefined(); - // Проверяем наличие полей формы + // Check наличие полей формы const inputs = formHtml.querySelectorAll('input'); expect(inputs.length).toBeGreaterThan(0); - // Проверяем что каждый атрибут создал соответствующее поле + // Check что каждый атрибут создал соответствующее поле for (const attr of mockEntity.attributes) { // Пропустить атрибуты, которые не должны отображаться в форме создания if (!attr.showOnCreate) continue; @@ -216,7 +216,7 @@ describe('EntityEditFormBuilder', () => { } }); - it('должен устанавливать значения из dataRow при создании формы', () => { + it('should устанавливать значения из dataRow при создании формы', () => { const params: FormBuildParams = { values: dataRow }; @@ -225,7 +225,7 @@ describe('EntityEditFormBuilder', () => { const form = builder.build(); const formHtml = form.getHtml(); - // Проверяем заполнение полей значениями из dataRow + // Check заполнение полей значениями из dataRow const nameInput = formHtml.querySelector('[name="Person.name"]') as HTMLInputElement; expect(nameInput.value).toBe('John Doe'); @@ -233,7 +233,7 @@ describe('EntityEditFormBuilder', () => { expect(isActiveInput.checked).toBe(true); }); - it('должен создавать форму редактирования с правильными параметрами', () => { + it('should создавать форму редактирования с правильными параметрами', () => { const params: FormBuildParams = { values: dataRow, isEditForm: true @@ -243,12 +243,12 @@ describe('EntityEditFormBuilder', () => { const form = builder.build(); const formHtml = form.getHtml(); - // Проверяем, что первичные ключи имеют атрибут readonly + // Check, что первичные ключи имеют атрибут readonly const idInput = formHtml.querySelector('[name="Person.id"]') as HTMLInputElement; expect(idInput).toBeDefined(); expect(idInput.getAttribute('readonly')).toBeDefined(); - // Проверяем, что неизменяемые атрибуты имеют readonly + // Check, что неизменяемые атрибуты имеют readonly const attrNonEditable = createAttr('Person.createdAt', 'Created At', DataType.DateTime, { isEditable: false, showOnEdit: true @@ -264,86 +264,86 @@ describe('EntityEditFormBuilder', () => { expect(createdAtInput.getAttribute('readonly')).toBeDefined(); }); - it('должен создавать текстовые поля правильного типа', () => { + it('should создавать текстовые поля правильного типа', () => { const form = builder.build(); const formHtml = form.getHtml(); - // Проверяем текстовые поля + // Check текстовые поля const nameInput = formHtml.querySelector('[name="Person.name"]') as HTMLInputElement; expect(nameInput.type).toBe('text'); - // Проверяем поля checkbox + // Check поля checkbox const isActiveInput = formHtml.querySelector('[name="Person.isActive"]') as HTMLInputElement; expect(isActiveInput.type).toBe('checkbox'); - // Проверяем поля file + // Check поля file const photoInput = formHtml.querySelector('[name="Person.photo"]') as HTMLInputElement; expect(photoInput.type).toBe('file'); expect(photoInput.getAttribute('accept')).toBe('image/*'); }); - it('должен создавать текстовую область для многострочных редакторов', () => { + it('should создавать текстовую область для многострочных редакторов', () => { const form = builder.build(); const formHtml = form.getHtml(); - // Проверяем textarea для многострочных полей + // Check textarea для многострочных полей const notesTextarea = formHtml.querySelector('[name="Person.notes"]') as HTMLTextAreaElement; expect(notesTextarea).toBeDefined(); expect(notesTextarea.tagName.toLowerCase()).toBe('textarea'); }); - it('должен создавать select для списков', () => { + it('should создавать select для списков', () => { const form = builder.build(); const formHtml = form.getHtml(); - // Проверяем select для списков + // Check select для списков const statusSelect = formHtml.querySelector('[name="Person.status"]') as HTMLSelectElement; expect(statusSelect).toBeDefined(); expect(statusSelect.tagName.toLowerCase()).toBe('select'); - // Проверяем опции + // Check опции const options = statusSelect.querySelectorAll('option'); expect(options.length).toBe(2); expect(options[0].value).toBe('Active'); expect(options[1].value).toBe('Inactive'); }); - it('должен создавать специальные поля для дат и времени', () => { + it('should создавать специальные поля для дат и времени', () => { const form = builder.build(); const formHtml = form.getHtml(); - // Проверяем поля для дат + // Check поля для дат const birthDateField = formHtml.querySelector('[name="Person.birthDate"]').closest('.kfrm-fields, .kfrm-fields-ie'); expect(birthDateField).toBeDefined(); - // Проверяем наличие кнопки с календарем + // Check наличие кнопки с календарем const calendarButton = birthDateField.querySelector('button'); expect(calendarButton).toBeDefined(); - // Проверяем наличие иконки календаря + // Check наличие иконки календаря const calendarIcon = calendarButton.querySelector('i.ed-calendar-icon'); expect(calendarIcon).toBeDefined(); }); - it('должен создавать поля для lookup атрибутов', () => { + it('should создавать поля для lookup атрибутов', () => { const form = builder.build(); const formHtml = form.getHtml(); - // Проверяем наличие поля для lookup + // Check наличие поля для lookup const departmentField = formHtml.querySelector('[name="Person.departmentId"]').closest('.kfrm-fields, .kfrm-fields-ie'); expect(departmentField).toBeDefined(); - // Проверяем наличие кнопки для открытия lookup диалога + // Check наличие кнопки для открытия lookup диалога const lookupButton = departmentField.querySelector('button'); expect(lookupButton).toBeDefined(); expect(lookupButton.textContent).toBe('...'); }); - it('должен добавлять информацию о подсказках для полей с описанием', () => { + it('should добавлять информацию о подсказках для полей с описанием', () => { const form = builder.build(); const formHtml = form.getHtml(); - // Проверяем наличие подсказки для поля с описанием + // Check наличие подсказки для поля с описанием const nameLabel = formHtml.querySelector(`label[for="Person.name"]`); expect(nameLabel).toBeDefined(); @@ -352,25 +352,25 @@ describe('EntityEditFormBuilder', () => { expect(helpIcon.getAttribute('title')).toBe('Person full name'); }); - it('должен устанавливать обработчик submit и вызывать его при нажатии Enter', () => { + it('should устанавливать обработчик submit и вызывать его при нажатии Enter', () => { // Создаем мок-функцию для onSubmit const submitCallback = mock(); - // Устанавливаем callback через onSubmit + // Set callback через onSubmit builder.onSubmit(submitCallback); // Строим форму const form = builder.build(); const formHtml = form.getHtml(); - // Получаем поле ввода + // Get поле ввода const nameInput = formHtml.querySelector('[name="Person.name"]') as HTMLInputElement; - // Эмулируем нажатие клавиши Enter + // Emulate нажатие клавиши Enter const enterKeyEvent = new KeyboardEvent('keypress', { keyCode: 13 }); nameInput.dispatchEvent(enterKeyEvent); - // Проверяем, что callback был вызван + // Check, что callback был вызван expect(submitCallback).toHaveBeenCalled(); }); }); diff --git a/easydata.js/packs/crud/tests/root_data_view.test.ts b/easydata.js/packs/crud/tests/root_data_view.test.ts index 5a59e3a6..65401f34 100644 --- a/easydata.js/packs/crud/tests/root_data_view.test.ts +++ b/easydata.js/packs/crud/tests/root_data_view.test.ts @@ -4,7 +4,7 @@ import { RootDataView } from '../src/views/root_data_view'; import * as utils from '../src/utils/utils'; describe('RootDataView', () => { - // Моки для DOM и объектов + // Mocks for DOM и объектов let mockSlot: HTMLElement; let mockContext: DataContext; let mockMetaData: MetaData; @@ -37,27 +37,27 @@ describe('RootDataView', () => { ]; beforeEach(() => { - // Создаем DOM-элемент для слота + // Create DOM element for slot mockSlot = document.createElement('div'); document.body.appendChild(mockSlot); - // Мок для корневой сущности + // Mock for корневой сущности mockRootEntity = { subEntities: mockEntities } as unknown as MetaEntity; - // Мок для метаданных + // Mock for метаданных mockMetaData = { getRootEntity: mock().mockReturnValue(mockRootEntity), isEmpty: mock().mockReturnValue(false) } as unknown as MetaData; - // Мок для контекста данных + // Mock for контекста данных mockContext = { getMetaData: mock().mockReturnValue(mockMetaData) } as unknown as DataContext; - // Мок для i18n + // Mock for i18n jest.spyOn(i18n, 'getText').mockImplementation((key: string) => { if (key === 'RootViewTitle') return 'Entities'; if (key === 'EntityMenuDesc') return 'Select an entity from the list below'; @@ -65,98 +65,98 @@ describe('RootDataView', () => { return key; }); - // Мок для функции setLocation + // Mock for функции setLocation jest.spyOn(utils, 'setLocation').mockImplementation(() => {}); }); afterEach(() => { - // Удаляем добавленные элементы + // Remove added elements if (mockSlot.parentNode) { mockSlot.parentNode.removeChild(mockSlot); } - // Сбрасываем моки + // Reset моки jest.restoreAllMocks(); }); - it('должен создаваться с правильными настройками по умолчанию', () => { + it('should создаваться с правильными настройками по умолчанию', () => { view = new RootDataView(mockSlot, mockContext, '/basePath'); - // Проверяем, что контекст и метаданные установлены + // Check that context and metadata are set expect((view as any).context).toBe(mockContext); expect((view as any).metaData).toBe(mockMetaData); - // Проверяем опции по умолчанию + // Check опции по умолчанию const options = (view as any).options; expect(options).toBeObject(); expect(options.usePluralNames).toBe(true); }); - it('должен применять пользовательские настройки', () => { + it('should применять пользовательские настройки', () => { view = new RootDataView(mockSlot, mockContext, '/basePath', { usePluralNames: false }); - // Проверяем пользовательские опции + // Check пользовательские опции const options = (view as any).options; expect(options).toBeObject(); expect(options.usePluralNames).toBe(false); }); - it('должен рендерить заголовок', () => { + it('should рендерить заголовок', () => { view = new RootDataView(mockSlot, mockContext, '/basePath'); - // Проверяем, что заголовок содержит текст + // Check, что заголовок содержит текст const header = mockSlot.querySelector('h1'); expect(header).toBeDefined(); expect(header.textContent).toBe('Entities'); }); - it('должен рендерить список сущностей', () => { + it('should рендерить список сущностей', () => { view = new RootDataView(mockSlot, mockContext, '/basePath'); - // Проверяем, что есть описание меню + // Check, что есть описание меню const menuDescription = mockSlot.querySelector('.ed-menu-description'); expect(menuDescription).toBeDefined(); expect(menuDescription.textContent).toBe('Select an entity from the list below'); - // Проверяем, что есть список сущностей + // Check, что есть список сущностей const entityMenu = mockSlot.querySelector('.ed-entity-menu'); expect(entityMenu).toBeDefined(); - // Проверяем количество элементов в списке + // Check number of elements in list const entityItems = entityMenu.querySelectorAll('.ed-entity-item'); expect(entityItems.length).toBe(3); }); - it('должен использовать множественные имена сущностей, когда usePluralNames=true', () => { + it('should использовать множественные имена сущностей, когда usePluralNames=true', () => { view = new RootDataView(mockSlot, mockContext, '/basePath', { usePluralNames: true }); - // Проверяем, что используются множественные имена + // Check, что используются множественные имена const entityItems = mockSlot.querySelectorAll('.ed-entity-item-caption'); expect(entityItems[0].textContent).toBe('Customers'); expect(entityItems[1].textContent).toBe('Products'); expect(entityItems[2].textContent).toBe('Orders'); }); - it('должен использовать обычные имена сущностей, когда usePluralNames=false', () => { + it('should использовать обычные имена сущностей, когда usePluralNames=false', () => { view = new RootDataView(mockSlot, mockContext, '/basePath', { usePluralNames: false }); - // Проверяем, что используются обычные имена + // Check, что используются обычные имена const entityItems = mockSlot.querySelectorAll('.ed-entity-item-caption'); expect(entityItems[0].textContent).toBe('Customer'); expect(entityItems[1].textContent).toBe('Product'); expect(entityItems[2].textContent).toBe('Order'); }); - it('должен отображать описания сущностей', () => { + it('should отображать описания сущностей', () => { view = new RootDataView(mockSlot, mockContext, '/basePath'); - // Проверяем, что описания отображаются + // Check, что описания отображаются const descriptionItems = mockSlot.querySelectorAll('.ed-entity-item-descr'); // Должно быть 2 описания (у entity2 нет описания) @@ -165,38 +165,38 @@ describe('RootDataView', () => { expect(descriptionItems[1].textContent).toBe('Order entity description'); }); - it('должен обрабатывать клик по сущности', () => { + it('should обрабатывать клик по сущности', () => { view = new RootDataView(mockSlot, mockContext, '/basePath'); - // Получаем первый элемент списка + // Get first list element const firstEntityItem = mockSlot.querySelector('.ed-entity-item'); - // Эмулируем клик + // Emulate клик firstEntityItem.click(); - // Проверяем, что setLocation был вызван с правильными параметрами + // Check, что setLocation был вызван с правильными параметрами expect(utils.setLocation).toHaveBeenCalledWith('/basePath/entity1'); }); - it('должен отображать сообщение, если модель пуста', () => { - // Меняем мок, чтобы метаданные были пустыми + it('should отображать сообщение, если модель пуста', () => { + // Change mock so metadata is empty (mockMetaData.isEmpty as jest.Mock).mockReturnValue(true); (mockRootEntity.subEntities as any) = []; view = new RootDataView(mockSlot, mockContext, '/basePath'); - // Проверяем, что отображается сообщение о пустой модели + // Check, что отображается сообщение о пустой модели const menuDescription = mockSlot.querySelector('.ed-menu-description'); expect(menuDescription).toBeDefined(); expect(menuDescription.textContent).toBe('The model is empty'); - // Проверяем, что список сущностей пуст + // Check, что список сущностей пуст const entityItems = mockSlot.querySelectorAll('.ed-entity-item'); expect(entityItems.length).toBe(0); }); - it('должен корректно декодировать ID сущностей при навигации', () => { - // Создаем сущность с ID, требующим кодирования + it('should correctly декодировать ID сущностей при навигации', () => { + // Create entity с ID, требующим кодирования const encodedEntity = { id: 'entity%20with%20space', name: 'EncodedEntity', @@ -210,13 +210,13 @@ describe('RootDataView', () => { view = new RootDataView(mockSlot, mockContext, '/basePath'); - // Получаем элемент списка + // Get элемент списка const entityItem = mockSlot.querySelector('.ed-entity-item'); - // Эмулируем клик + // Emulate клик entityItem.click(); - // Проверяем, что setLocation был вызван с декодированным ID + // Check, что setLocation был вызван с декодированным ID expect(utils.setLocation).toHaveBeenCalledWith('/basePath/entity with space'); }); }); diff --git a/easydata.js/packs/crud/tests/text_data_filter.test.ts b/easydata.js/packs/crud/tests/text_data_filter.test.ts index ca151fca..37733b91 100644 --- a/easydata.js/packs/crud/tests/text_data_filter.test.ts +++ b/easydata.js/packs/crud/tests/text_data_filter.test.ts @@ -10,20 +10,20 @@ import { TextDataFilter } from '../src/filter/text_data_filter'; import { DataFilter } from '../src/filter/data_filter'; describe('TextDataFilter', () => { - // Мок для DataLoader + // Mock for DataLoader let mockLoader: DataLoader; - // Исходная таблица данных + // Source data table let sourceTable: EasyDataTable; - // Тестируемый фильтр + // Filter under test let filter: TextDataFilter; - // Колонки для тестовой таблицы + // Columns for test table let columns: DataColumnDescriptor[]; - // Данные для тестовой таблицы + // Data for test table let tableData: any[][]; - // Настройка тестового окружения перед каждым тестом + // Test environment setup before each test beforeEach(() => { - // Определяем колонки для тестовой таблицы + // Define columns for test table columns = [ { id: 'id', label: 'ID', type: DataType.Int32 }, { id: 'name', label: 'Name', type: DataType.String }, @@ -31,7 +31,7 @@ describe('TextDataFilter', () => { { id: 'price', label: 'Price', type: DataType.Currency } ]; - // Определяем данные для тестовой таблицы + // Define data for test table tableData = [ [1, 'Apple', 'Fresh red apple', 1.99], [2, 'Banana', 'Yellow fruit', 0.99], @@ -47,10 +47,10 @@ describe('TextDataFilter', () => { inMemory: true }); - // Создаем мок для DataLoader + // Create mock for DataLoader mockLoader = { loadChunk: (chunkInfo: ChunkInfo): Promise<{ table: EasyDataTable, total: number }> => { - // Эмулируем фильтрацию на сервере + // Emulate фильтрацию на сервере const filterValue = chunkInfo['filters']?.[0]?.value?.toLowerCase(); const filteredData = filterValue ? tableData.filter(row => { @@ -73,21 +73,21 @@ describe('TextDataFilter', () => { } }; - // Создаем тестируемый фильтр + // Create filter under test filter = new TextDataFilter(mockLoader, sourceTable, 'products'); }); - it('должен быть экземпляром класса DataFilter', () => { + it('should быть экземпляром класса DataFilter', () => { expect(filter).toBeInstanceOf(DataFilter); expect(filter).toBeObject(); }); - it('должен возвращать пустую строку для getValue() после создания', () => { + it('should return пустую строку для getValue() после создания', () => { const value = filter.getValue(); expect(value).toBe(''); }); - it('должен корректно устанавливать и возвращать значение фильтра', () => { + it('should correctly устанавливать и возвращать значение фильтра', () => { return filter.apply('apple') .then(() => { const value = filter.getValue(); @@ -95,14 +95,14 @@ describe('TextDataFilter', () => { }); }); - it('должен возвращать исходную таблицу при пустом значении фильтра', () => { + it('should return исходную таблицу при пустом значении фильтра', () => { return filter.apply('') .then(result => { expect(result).toBe(sourceTable); }); }); - it('должен возвращать исходную таблицу после очистки фильтра', () => { + it('should return исходную таблицу после очистки фильтра', () => { return filter.apply('apple') .then(() => filter.clear()) .then(result => { @@ -111,7 +111,7 @@ describe('TextDataFilter', () => { }); }); - it('должен фильтровать данные в памяти при полностью загруженной таблице', () => { + it('should фильтровать данные в памяти при полностью загруженной таблице', () => { return filter.apply('apple') .then(filteredTable => { expect(filteredTable).not.toBe(sourceTable); @@ -121,15 +121,15 @@ describe('TextDataFilter', () => { expect(rows).toBeArray(); expect(rows.length).toBe(2); - // Проверяем первую строку (Apple) + // Check первую строку (Apple) expect(rows[0].getValue('name')).toBe('Apple'); - // Проверяем вторую строку (Pineapple) + // Check вторую строку (Pineapple) expect(rows[1].getValue('name')).toBe('Pineapple'); }); }); - it('должен использовать серверную фильтрацию когда таблица не полностью загружена', () => { + it('should использовать серверную фильтрацию когда таблица не полностью загружена', () => { // Создаем частично загруженную таблицу и новый фильтр const partialTable = new EasyDataTable({ columns: columns, @@ -148,22 +148,22 @@ describe('TextDataFilter', () => { return serverFilter.apply('orange') .then(filteredTable => { - // Проверяем, что был вызван метод loadChunk + // Check, что был вызван метод loadChunk expect(loadChunkSpy).toHaveBeenCalled(); - // Проверяем, что фильтр был передан в запрос + // Check, что фильтр был передан в запрос const callArgs = loadChunkSpy.mock.calls[0][0]; expect(callArgs).toBeObject(); expect(callArgs.filters).toBeArray(); expect(callArgs.filters[0].value).toBe('orange'); - // Проверяем результаты фильтрации + // Check результаты фильтрации expect(filteredTable.getCachedCount()).toBe(1); expect(filteredTable.getCachedRows()[0].getValue('name')).toBe('Orange'); }); }); - it('должен поддерживать множественные поисковые слова через разделитель ||', () => { + it('should поддерживать множественные поисковые слова через разделитель ||', () => { return filter.apply('apple || melon') .then(filteredTable => { const rows = filteredTable.getCachedRows(); @@ -177,7 +177,7 @@ describe('TextDataFilter', () => { }); }); - it('должен фильтровать по нескольким колонкам', () => { + it('should фильтровать по нескольким колонкам', () => { return filter.apply('fruit') .then(filteredTable => { const rows = filteredTable.getCachedRows(); @@ -191,7 +191,7 @@ describe('TextDataFilter', () => { }); }); - it('должен фильтровать без учета регистра', () => { + it('should фильтровать без учета регистра', () => { return filter.apply('APPLE') .then(filteredTable => { const rows = filteredTable.getCachedRows(); @@ -204,7 +204,7 @@ describe('TextDataFilter', () => { }); }); - it('должен возвращать пустую таблицу если нет соответствий', () => { + it('should return пустую таблицу если нет соответствий', () => { return filter.apply('nonexistent') .then(filteredTable => { expect(filteredTable.getCachedCount()).toBe(0); @@ -212,7 +212,7 @@ describe('TextDataFilter', () => { }); }); - it('должен фильтровать по числовым значениям', () => { + it('should фильтровать по числовым значениям', () => { return filter.apply('1.99') .then(filteredTable => { const rows = filteredTable.getCachedRows(); diff --git a/easydata.js/packs/crud/tests/text_filter_widget.test.ts b/easydata.js/packs/crud/tests/text_filter_widget.test.ts index 01134154..ff5efac6 100644 --- a/easydata.js/packs/crud/tests/text_filter_widget.test.ts +++ b/easydata.js/packs/crud/tests/text_filter_widget.test.ts @@ -9,7 +9,7 @@ import { TextFilterWidget } from '../src/widgets/text_filter_widget'; import { DataFilter } from '../src/filter/data_filter'; describe('TextFilterWidget', () => { - // Переменные для тестов + // Variables for tests let mockSlot: HTMLElement; let mockGrid: EasyGrid; let mockFilter: DataFilter; @@ -18,17 +18,17 @@ describe('TextFilterWidget', () => { let defaultStringRenderer: GridCellRenderer; let defaultNumberRenderer: GridCellRenderer; - // Инициализация перед каждым тестом + // Initialization before each test beforeEach(() => { - // Создаем DOM для монтирования виджета + // Create DOM для монтирования виджета mockSlot = document.createElement('div'); document.body.appendChild(mockSlot); - // Создаем моки для рендереров ячеек + // Create mocks for рендереров ячеек defaultStringRenderer = mock(); defaultNumberRenderer = mock(); - // Мок для хранилища рендереров + // Mock for хранилища рендереров mockCellRendererStore = { getDefaultRendererByType: mock((type: CellRendererType) => { if (type === CellRendererType.STRING) return defaultStringRenderer; @@ -38,40 +38,40 @@ describe('TextFilterWidget', () => { setDefaultRenderer: mock() }; - // Мок для EasyGrid + // Mock for EasyGrid mockGrid = { cellRendererStore: mockCellRendererStore, setData: mock(), } as unknown as EasyGrid; - // Мок для DataFilter + // Mock for DataFilter mockFilter = { getValue: mock().mockReturnValue(''), apply: mock().mockResolvedValue(new EasyDataTable()) } as unknown as DataFilter; - // Мок для i18n.getText + // Mock for i18n.getText jest.spyOn(i18n, 'getText').mockImplementation((key: string) => { if (key === 'SearchInputPlaceholder') return 'Search...'; if (key === 'SearchBtn') return 'Search'; return key; }); - // Мок для browserUtils.isIE и isEdge + // Mock for browserUtils.isIE и isEdge jest.spyOn(browserUtils, 'isIE').mockReturnValue(false); jest.spyOn(browserUtils, 'isEdge').mockReturnValue(false); - // Мок для dataUtils.isNumericType и getStringDataTypes + // Mock for dataUtils.isNumericType и getStringDataTypes jest.spyOn(dataUtils, 'isNumericType').mockImplementation((type) => { return type === 'number'; }); jest.spyOn(dataUtils, 'getStringDataTypes').mockReturnValue(['string']); - // Создание экземпляра виджета для тестов + // Create widget instance for testing filterWidget = new TextFilterWidget(mockSlot, mockGrid, mockFilter); }); - // Очистка после каждого теста + // Cleanup after each test afterEach(() => { if (mockSlot.parentNode) { mockSlot.parentNode.removeChild(mockSlot); @@ -81,33 +81,33 @@ describe('TextFilterWidget', () => { jest.restoreAllMocks(); }); - it('должен создаваться с настройками по умолчанию', () => { + it('should создаваться with default settings', () => { expect(filterWidget).toBeDefined(); - // Проверка установки рендереров + // Check установки рендереров expect(mockCellRendererStore.getDefaultRendererByType).toHaveBeenCalledWith(CellRendererType.STRING); expect(mockCellRendererStore.getDefaultRendererByType).toHaveBeenCalledWith(CellRendererType.NUMBER); expect(mockCellRendererStore.setDefaultRenderer).toHaveBeenCalledTimes(2); }); - it('должен правильно рендерить HTML структуру', () => { - // Проверка наличия поля ввода + it('should правильно рендерить HTML структуру', () => { + // Check наличия поля ввода const input = mockSlot.querySelector('input'); expect(input).toBeDefined(); expect(input.getAttribute('placeholder')).toBe('Search...'); - // Проверка наличия кнопки поиска (вне режима instantMode) + // Check наличия кнопки поиска (вне режима instantMode) const button = mockSlot.querySelector('button'); expect(button).toBeDefined(); expect(button.textContent).toBe('Search'); - // Проверка наличия иконки очистки + // Check наличия иконки очистки const clearIcon = mockSlot.querySelector('span.icon'); expect(clearIcon).toBeDefined(); }); - it('должен рендерить HTML без кнопки поиска в instantMode', () => { - // Удаляем предыдущий виджет + it('should рендерить HTML без кнопки поиска в instantMode', () => { + // Remove предыдущий виджет mockSlot.innerHTML = ''; // Создаем новый виджет с instantMode @@ -115,34 +115,34 @@ describe('TextFilterWidget', () => { instantMode: true }); - // Проверка наличия поля ввода + // Check наличия поля ввода const input = mockSlot.querySelector('input'); expect(input).toBeDefined(); - // Проверка отсутствия кнопки поиска + // Check отсутствия кнопки поиска const button = mockSlot.querySelector('button'); expect(button).toBeNull(); }); - it('должен использовать другие классы для IE', () => { - // Удаляем предыдущий виджет + it('should использовать другие классы для IE', () => { + // Remove предыдущий виджет mockSlot.innerHTML = ''; - // Меняем мок для IE + // Change mock for IE (browserUtils.isIE as jest.Mock).mockReturnValue(true); // Создаем новый виджет filterWidget = new TextFilterWidget(mockSlot, mockGrid, mockFilter); - // Проверка наличия класса для IE + // Check наличия класса для IE expect(mockSlot.classList.contains('kfrm-fields-ie')).toBe(true); }); - it('должен устанавливать фокус на поле ввода, если опция focus=true', () => { - // Удаляем предыдущий виджет + it('should устанавливать фокус на поле ввода, если опция focus=true', () => { + // Remove предыдущий виджет mockSlot.innerHTML = ''; - // Мок для focus + // Mock for focus const focusMock = mock(); HTMLInputElement.prototype.focus = focusMock; @@ -151,68 +151,68 @@ describe('TextFilterWidget', () => { focus: true }); - // Проверка вызова focus + // Check вызова focus expect(focusMock).toHaveBeenCalled(); }); - it('должен применять фильтр при нажатии Enter', () => { - // Получаем поле ввода + it('should применять фильтр при нажатии Enter', () => { + // Get поле ввода const input = mockSlot.querySelector('input') as HTMLInputElement; - // Устанавливаем значение + // Set значение input.value = 'test'; - // Эмулируем нажатие Enter + // Emulate нажатие Enter const enterEvent = new KeyboardEvent('keydown', { keyCode: 13 }); input.dispatchEvent(enterEvent); - // Проверка вызова apply с правильным значением + // Check вызова apply с правильным значением expect(mockFilter.apply).toHaveBeenCalledWith('test'); }); - it('должен применять фильтр при нажатии кнопки Search', () => { - // Получаем поле ввода и кнопку + it('should применять фильтр при нажатии кнопки Search', () => { + // Get поле ввода и кнопку const input = mockSlot.querySelector('input') as HTMLInputElement; const button = mockSlot.querySelector('button') as HTMLButtonElement; - // Устанавливаем значение + // Set значение input.value = 'test'; - // Эмулируем клик по кнопке + // Emulate клик по кнопке button.click(); - // Проверка вызова apply с правильным значением + // Check вызова apply с правильным значением expect(mockFilter.apply).toHaveBeenCalledWith('test'); }); - it('должен очищать поле ввода при нажатии на иконку очистки', () => { - // Получаем поле ввода и иконку очистки + it('should очищать поле ввода при нажатии на иконку очистки', () => { + // Get поле ввода и иконку очистки const input = mockSlot.querySelector('input') as HTMLInputElement; const clearIcon = mockSlot.querySelector('span.icon') as HTMLSpanElement; - // Устанавливаем значение и фокус мок + // Set значение и фокус мок input.value = 'test'; const focusMock = mock(); input.focus = focusMock; - // Эмулируем клик по иконке очистки + // Emulate клик по иконке очистки clearIcon.click(); - // Проверка очистки значения + // Check очистки значения expect(input.value).toBe(''); - // Проверка установки фокуса + // Check установки фокуса expect(focusMock).toHaveBeenCalled(); - // Проверка вызова apply с пустым значением + // Check вызова apply с пустым значением expect(mockFilter.apply).toHaveBeenCalledWith(''); }); - it('должен применять фильтр с задержкой в instantMode при вводе', () => { - // Удаляем предыдущий виджет + it('should применять фильтр с задержкой в instantMode при вводе', () => { + // Remove предыдущий виджет mockSlot.innerHTML = ''; - // Мок для setTimeout + // Mock for setTimeout jest.useFakeTimers(); // Создаем новый виджет с instantMode и маленьким таймаутом @@ -221,31 +221,31 @@ describe('TextFilterWidget', () => { instantTimeout: 500 }); - // Получаем поле ввода + // Get поле ввода const input = mockSlot.querySelector('input') as HTMLInputElement; - // Устанавливаем значение и эмулируем keyup + // Set значение и эмулируем keyup input.value = 'test'; input.dispatchEvent(new KeyboardEvent('keyup')); - // Проверка, что apply не вызван сразу + // Check, что apply не вызван сразу expect(mockFilter.apply).not.toHaveBeenCalled(); // Проматываем таймеры jest.advanceTimersByTime(500); - // Проверка вызова apply с правильным значением + // Check вызова apply с правильным значением expect(mockFilter.apply).toHaveBeenCalledWith('test'); - // Восстанавливаем таймеры + // Restore таймеры jest.useRealTimers(); }); - it('должен очищать предыдущий таймер при новом событии keyup', () => { - // Удаляем предыдущий виджет + it('should очищать предыдущий таймер при новом событии keyup', () => { + // Remove предыдущий виджет mockSlot.innerHTML = ''; - // Мок для setTimeout и clearTimeout + // Mock for setTimeout и clearTimeout jest.useFakeTimers(); const clearTimeoutSpy = jest.spyOn(window, 'clearTimeout'); @@ -255,7 +255,7 @@ describe('TextFilterWidget', () => { instantTimeout: 500 }); - // Получаем поле ввода + // Get поле ввода const input = mockSlot.querySelector('input') as HTMLInputElement; // Первый ввод @@ -266,72 +266,72 @@ describe('TextFilterWidget', () => { input.value = 'test2'; input.dispatchEvent(new KeyboardEvent('keyup')); - // Проверка вызова clearTimeout + // Check вызова clearTimeout expect(clearTimeoutSpy).toHaveBeenCalled(); // Проматываем таймеры jest.advanceTimersByTime(500); - // Проверка вызова apply с последним значением + // Check вызова apply с последним значением expect(mockFilter.apply).toHaveBeenCalledWith('test2'); - // Восстанавливаем таймеры + // Restore таймеры jest.useRealTimers(); }); - it('метод applyFilter должен вернуть true если значение фильтра изменилось', () => { - // Получаем поле ввода + it('метод applyFilter should вернуть true если значение фильтра изменилось', () => { + // Get поле ввода const input = mockSlot.querySelector('input') as HTMLInputElement; - // Устанавливаем значение + // Set значение input.value = 'test'; - // Мок для getValue, возвращающий другое значение + // Mock for getValue, возвращающий другое значение (mockFilter.getValue as jest.Mock).mockReturnValue('old'); - // Вызываем метод и проверяем результат + // Call метод и проверяем результат const result = filterWidget.applyFilter(true); expect(result).toBe(true); - // Проверка вызова apply + // Check вызова apply expect(mockFilter.apply).toHaveBeenCalledWith('test'); }); - it('метод applyFilter должен вернуть false если значение фильтра не изменилось', () => { - // Получаем поле ввода + it('метод applyFilter should вернуть false если значение фильтра не изменилось', () => { + // Get поле ввода const input = mockSlot.querySelector('input') as HTMLInputElement; - // Устанавливаем значение + // Set значение input.value = 'test'; - // Мок для getValue, возвращающий то же самое значение + // Mock for getValue, возвращающий то же самое значение (mockFilter.getValue as jest.Mock).mockReturnValue('test'); - // Вызываем метод и проверяем результат + // Call метод и проверяем результат const result = filterWidget.applyFilter(true); expect(result).toBe(false); - // Проверка, что apply не вызван + // Check, что apply не вызван expect(mockFilter.apply).not.toHaveBeenCalled(); }); - it('должен правильно выделять текст, совпадающий с фильтром', () => { - // Мок для getValue, возвращающий искомое слово + it('should правильно выделять текст, совпадающий с фильтром', () => { + // Mock for getValue, возвращающий искомое слово (mockFilter.getValue as jest.Mock).mockReturnValue('test'); - // Вызываем highlightText напрямую через приватный метод + // Call highlightText напрямую через приватный метод const result = (filterWidget as any).highlightText('This is a test string'); - // Проверка типа результата + // Check типа результата expect(result instanceof HTMLElement).toBe(true); - // Проверка содержимого + // Check содержимого const div = result as HTMLElement; - // Должно быть 3 дочерних элемента: текст, span с выделением, текст + // Should have 3 child elements: text, span with highlight, text expect(div.childNodes.length).toBe(3); - // Проверка правильного выделения + // Check правильного выделения expect(div.childNodes[0].textContent).toBe('This is a '); const highlightSpan = div.childNodes[1] as HTMLSpanElement; @@ -342,50 +342,50 @@ describe('TextFilterWidget', () => { expect(div.childNodes[2].textContent).toBe(' string'); }); - it('должен правильно выделять несколько совпадений', () => { - // Мок для getValue, возвращающий искомое слово + it('should правильно выделять несколько совпадений', () => { + // Mock for getValue, возвращающий искомое слово (mockFilter.getValue as jest.Mock).mockReturnValue('test'); - // Вызываем highlightText напрямую через приватный метод + // Call highlightText напрямую через приватный метод const result = (filterWidget as any).highlightText('test another test'); - // Проверка типа результата + // Check типа результата expect(result instanceof HTMLElement).toBe(true); - // Проверка содержимого + // Check содержимого const div = result as HTMLElement; - // Должно быть 5 дочерних элементов: span, текст, span, текст + // Should have 5 child elements: span, text, span, text expect(div.childNodes.length).toBe(3); - // Проверка первого выделения + // Check первого выделения const firstSpan = div.childNodes[0] as HTMLSpanElement; expect(firstSpan.tagName).toBe('SPAN'); expect(firstSpan.textContent).toBe('test'); - // Проверка текста между выделениями + // Check текста между выделениями expect(div.childNodes[1].textContent).toBe(' another '); - // Проверка второго выделения + // Check второго выделения const secondSpan = div.childNodes[2] as HTMLSpanElement; expect(secondSpan.tagName).toBe('SPAN'); expect(secondSpan.textContent).toBe('test'); }); - it('должен правильно обрабатывать несколько искомых слов через разделитель ||', () => { - // Мок для getValue, возвращающий несколько слов через разделитель + it('should правильно обрабатывать несколько искомых слов через разделитель ||', () => { + // Mock for getValue, возвращающий несколько слов через разделитель (mockFilter.getValue as jest.Mock).mockReturnValue('apple || banana'); - // Вызываем highlightText напрямую через приватный метод + // Call highlightText напрямую через приватный метод const result = (filterWidget as any).highlightText('I have an apple and a banana'); - // Проверка типа результата + // Check типа результата expect(result instanceof HTMLElement).toBe(true); - // Проверка содержимого + // Check содержимого const div = result as HTMLElement; - // Должен содержать выделения для обоих слов + // Should contain highlights for both words let appleFound = false; let bananaFound = false; @@ -403,43 +403,43 @@ describe('TextFilterWidget', () => { expect(bananaFound).toBe(true); }); - it('должен выделять всю ячейку, если содержимое полностью совпадает с фильтром', () => { - // Мок для getValue, возвращающий полное значение ячейки + it('should выделять всю ячейку, если содержимое полностью совпадает с фильтром', () => { + // Mock for getValue, возвращающий полное значение ячейки (mockFilter.getValue as jest.Mock).mockReturnValue('exact match'); - // Вызываем highlightText напрямую через приватный метод + // Call highlightText напрямую через приватный метод const result = (filterWidget as any).highlightText('exact match'); - // Должен вернуть один span, выделяющий всё содержимое + // Should return one span highlighting all content expect(result instanceof HTMLSpanElement).toBe(true); expect((result as HTMLSpanElement).style.backgroundColor).toBe('yellow'); expect((result as HTMLSpanElement).textContent).toBe('exact match'); }); - it('должен возвращать исходный текст, если нет совпадений', () => { - // Мок для getValue, возвращающий слово, которого нет в тексте + it('should return original text if no matches', () => { + // Mock for getValue, возвращающий слово, которого нет в тексте (mockFilter.getValue as jest.Mock).mockReturnValue('missing'); - // Вызываем highlightText напрямую через приватный метод + // Call highlightText напрямую через приватный метод const result = (filterWidget as any).highlightText('This is a test string'); - // Должен вернуть оригинальную строку + // Should return original string expect(result).toBe('This is a test string'); }); - it('должен возвращать исходный текст, если фильтр пустой', () => { - // Мок для getValue, возвращающий пустую строку + it('should return original text if filter is empty', () => { + // Mock for getValue, возвращающий пустую строку (mockFilter.getValue as jest.Mock).mockReturnValue(''); - // Вызываем highlightText напрямую через приватный метод + // Call highlightText напрямую через приватный метод const result = (filterWidget as any).highlightText('This is a test string'); - // Должен вернуть оригинальную строку + // Should return original string expect(result).toBe('This is a test string'); }); - it('должен использовать пользовательский рендерер для строковых ячеек', () => { - // Создаем моки для параметров рендерера + it('should использовать пользовательский рендерер для строковых ячеек', () => { + // Create mocks for параметров рендерера const column: GridColumn = { dataColumn: { id: 'name' }, type: 'string' @@ -447,23 +447,23 @@ describe('TextFilterWidget', () => { const cellElement = document.createElement('div'); const rowElement = document.createElement('tr'); - // Получаем установленный рендерер для строк + // Get установленный рендерер для строк const stringRendererCall = (mockCellRendererStore.setDefaultRenderer as jest.Mock).mock.calls[0]; const customStringRenderer = stringRendererCall[1]; - // Мок для getValue, чтобы строки выделялись + // Mock for getValue, чтобы строки выделялись (mockFilter.getValue as jest.Mock).mockReturnValue('test'); - // Вызываем пользовательский рендерер + // Call пользовательский рендерер customStringRenderer('This is a test', column, cellElement, rowElement); - // Проверяем, что ячейка содержит выделенный текст + // Check, что ячейка содержит выделенный текст expect(cellElement.innerHTML).not.toBe(''); expect(cellElement.querySelector('span[style*="yellow"]')).toBeDefined(); }); - it('должен использовать пользовательский рендерер для числовых ячеек', () => { - // Создаем моки для параметров рендерера + it('should использовать пользовательский рендерер для числовых ячеек', () => { + // Create mocks for параметров рендерера const column: GridColumn = { dataColumn: { id: 'value' }, type: 'number' @@ -471,20 +471,20 @@ describe('TextFilterWidget', () => { const cellElement = document.createElement('div'); const rowElement = document.createElement('tr'); - // Получаем установленный рендерер для чисел + // Get установленный рендерер для чисел const numberRendererCall = (mockCellRendererStore.setDefaultRenderer as jest.Mock).mock.calls[1]; const customNumberRenderer = numberRendererCall[1]; - // Мок для getValue, чтобы числа выделялись + // Mock for getValue, чтобы числа выделялись (mockFilter.getValue as jest.Mock).mockReturnValue('42'); - // Мок для toLocaleString + // Mock for toLocaleString Number.prototype.toLocaleString = function() { return this.toString(); }; - // Вызываем пользовательский рендерер + // Call пользовательский рендерер customNumberRenderer(42, column, cellElement, rowElement); - // Проверяем, что ячейка содержит выделенный текст + // Check, что ячейка содержит выделенный текст expect(cellElement.innerHTML).not.toBe(''); expect(cellElement.querySelector('span[style*="yellow"]')).toBeDefined(); }); diff --git a/easydata.js/packs/crud/tests/utils.test.ts b/easydata.js/packs/crud/tests/utils.test.ts index cbec0553..7efacd97 100644 --- a/easydata.js/packs/crud/tests/utils.test.ts +++ b/easydata.js/packs/crud/tests/utils.test.ts @@ -2,91 +2,91 @@ import { DataType, i18n } from '@easydata/core'; import { getInternalDateTimeFormat, getEditDateTimeFormat, setLocation } from '../src/utils/utils'; describe('Utils', () => { - // Сохраняем оригинальные объекты перед тестами + // Save original objects before tests const originalWindow = { ...window }; const originalHistory = { ...window.history }; const originalDispatchEvent = window.dispatchEvent; - // Оригинальные настройки i18n + // Original i18n settings let originalSettings: any; beforeEach(() => { - // Сохраняем оригинальные настройки i18n + // Save original i18n settings originalSettings = i18n.getLocaleSettings(); - // Создаем мок для i18n.getLocaleSettings + // Create mock for i18n.getLocaleSettings jest.spyOn(i18n, 'getLocaleSettings').mockReturnValue({ ...originalSettings, editDateFormat: 'dd.MM.yyyy', editTimeFormat: 'HH:mm', }); - // Создаем моки для window.history + // Create mocks for window.history window.history.pushState = mock(); window.dispatchEvent = mock(); }); afterEach(() => { - // Восстанавливаем оригинальные объекты и функции + // Restore original objects and functions jest.restoreAllMocks(); window.history = originalHistory; window.dispatchEvent = originalDispatchEvent; }); - it('должен возвращать правильный внутренний формат для Date', () => { + it('should return правильный внутренний формат для Date', () => { const format = getInternalDateTimeFormat(DataType.Date); expect(format).toBe('yyyy-MM-dd'); }); - it('должен возвращать правильный внутренний формат для Time', () => { + it('should return правильный внутренний формат для Time', () => { const format = getInternalDateTimeFormat(DataType.Time); expect(format).toBe('HH:mm'); }); - it('должен возвращать правильный внутренний формат для DateTime', () => { + it('should return правильный внутренний формат для DateTime', () => { const format = getInternalDateTimeFormat(DataType.DateTime); expect(format).toBe('yyyy-MM-ddTHH:mm'); }); - it('должен возвращать формат редактирования из настроек локали для Date', () => { + it('should return формат редактирования из настроек локали для Date', () => { const format = getEditDateTimeFormat(DataType.Date); expect(format).toBe('dd.MM.yyyy'); }); - it('должен возвращать формат редактирования из настроек локали для Time', () => { + it('should return формат редактирования из настроек локали для Time', () => { const format = getEditDateTimeFormat(DataType.Time); expect(format).toBe('HH:mm'); }); - it('должен возвращать формат редактирования из настроек локали для DateTime', () => { + it('should return формат редактирования из настроек локали для DateTime', () => { const format = getEditDateTimeFormat(DataType.DateTime); expect(format).toBe('dd.MM.yyyy HH:mm'); }); - it('должен изменять локацию через pushState и генерировать событие', () => { - // Устанавливаем исходное значение state + it('should изменять локацию через pushState и генерировать событие', () => { + // Set initial state value const mockState = { test: 'state' }; window.history.state = mockState; document.title = 'Test Title'; - // Вызываем функцию + // Call функцию setLocation('/new-path'); - // Проверяем, что pushState был вызван с правильными аргументами + // Check, что pushState был вызван с правильными аргументами expect(window.history.pushState).toHaveBeenCalledWith( mockState, 'Test Title', '/new-path' ); - // Проверяем, что было сгенерировано правильное событие + // Check, что было сгенерировано правильное событие expect(window.dispatchEvent).toHaveBeenCalled(); const eventArg = (window.dispatchEvent as jest.Mock).mock.calls[0][0]; expect(eventArg).toBeInstanceOf(Event); expect(eventArg.type).toBe('ed_set_location'); }); - it('должен работать с различными форматами путей', () => { + it('should работать с различными форматами путей', () => { // Относительный путь setLocation('relative/path'); expect(window.history.pushState).toHaveBeenCalledWith( @@ -112,14 +112,14 @@ describe('Utils', () => { ); }); - it('должен использовать форматы из i18n для редактирования', () => { - // Перенастраиваем мок для i18n.getLocaleSettings с другими форматами + it('should использовать форматы из i18n для редактирования', () => { + // Reconfigure mock for i18n.getLocaleSettings with different formats (i18n.getLocaleSettings as jest.Mock).mockReturnValue({ editDateFormat: 'MM/dd/yyyy', editTimeFormat: 'hh:mm a', }); - // Проверяем обновленные форматы + // Check обновленные форматы expect(getEditDateTimeFormat(DataType.Date)).toBe('MM/dd/yyyy'); expect(getEditDateTimeFormat(DataType.Time)).toBe('hh:mm a'); expect(getEditDateTimeFormat(DataType.DateTime)).toBe('MM/dd/yyyy hh:mm a'); diff --git a/easydata.js/packs/crud/tests/validators.test.ts b/easydata.js/packs/crud/tests/validators.test.ts index 60551c94..67fe8b62 100644 --- a/easydata.js/packs/crud/tests/validators.test.ts +++ b/easydata.js/packs/crud/tests/validators.test.ts @@ -24,12 +24,12 @@ describe('TypeValidator', () => { jest.restoreAllMocks(); }); - it('должен иметь имя "Type"', () => { + it('should have name "Type"', () => { expect(validator.name).toBe('Type'); expect(validator).toBeInstanceOf(Validator); }); - it('должен успешно проходить валидацию для null или пустой строки', () => { + it('should successfully pass validation for null or empty string', () => { mockAttr = { dataType: DataType.Int32 } as MetaEntityAttr; const result1 = validator.validate(mockAttr, null); @@ -42,59 +42,59 @@ describe('TypeValidator', () => { expect(result3.successed).toBe(true); }); - it('должен проверять числовые значения для числового типа', () => { + it('should validate numeric values for numeric type', () => { mockAttr = { dataType: DataType.Int32 } as MetaEntityAttr; - // Валидные значения + // Valid values const result1 = validator.validate(mockAttr, '123'); expect(result1.successed).toBe(true); const result2 = validator.validate(mockAttr, 123); expect(result2.successed).toBe(true); - // Невалидные значения + // Invalid values const result3 = validator.validate(mockAttr, 'abc'); expect(result3.successed).toBe(false); expect(result3.messages).toBeArray(); expect(result3.messages[0]).toBe('Value must be a number'); }); - it('должен проверять дробные значения для целочисленного типа', () => { + it('should validate decimal values for integer type', () => { mockAttr = { dataType: DataType.Int32 } as MetaEntityAttr; - // Целочисленное значение + // Integer value const result1 = validator.validate(mockAttr, '123'); expect(result1.successed).toBe(true); - // Дробное значение не должно быть валидным для Int32 + // Decimal value не should be валидным для Int32 const result2 = validator.validate(mockAttr, '123.45'); expect(result2.successed).toBe(false); expect(result2.messages).toBeArray(); expect(result2.messages[0]).toBe('Value must be an integer number'); }); - it('должен принимать дробные значения для типа Float', () => { + it('should accept decimal values for Float type', () => { mockAttr = { dataType: DataType.Float } as MetaEntityAttr; - // Целочисленное значение + // Integer value const result1 = validator.validate(mockAttr, '123'); expect(result1.successed).toBe(true); - // Дробное значение должно быть валидным для Float + // Decimal value should be валидным для Float const result2 = validator.validate(mockAttr, '123.45'); expect(result2.successed).toBe(true); - // Строка не должна быть валидной + // String should not be valid const result3 = validator.validate(mockAttr, 'abc'); expect(result3.successed).toBe(false); expect(result3.messages).toBeArray(); expect(result3.messages[0]).toBe('Value must be a number'); }); - it('должен успешно проходить валидацию для нечисловых типов', () => { + it('should successfully pass validation for non-numeric types', () => { mockAttr = { dataType: DataType.String } as MetaEntityAttr; - // Любое значение должно быть валидным для строки + // Any value should be valid for string const result1 = validator.validate(mockAttr, '123'); expect(result1.successed).toBe(true); @@ -121,12 +121,12 @@ describe('RequiredValidator', () => { jest.restoreAllMocks(); }); - it('должен иметь имя "Required"', () => { + it('should иметь имя "Required"', () => { expect(validator.name).toBe('Required'); expect(validator).toBeInstanceOf(Validator); }); - it('должен проходить валидацию для nullable атрибутов', () => { + it('should проходить валидацию для nullable атрибутов', () => { mockAttr = { isNullable: true } as MetaEntityAttr; const result1 = validator.validate(mockAttr, null); @@ -139,10 +139,10 @@ describe('RequiredValidator', () => { expect(result3.successed).toBe(true); }); - it('должен проверять обязательные поля', () => { + it('should проверять обязательные поля', () => { mockAttr = { isNullable: false } as MetaEntityAttr; - // Не должно быть валидным для null, undefined или пустой строки + // Не should be валидным для null, undefined или пустой строки const result1 = validator.validate(mockAttr, null); expect(result1.successed).toBe(false); expect(result1.messages).toBeArray(); @@ -158,7 +158,7 @@ describe('RequiredValidator', () => { expect(result3.messages).toBeArray(); expect(result3.messages[0]).toBe('This field is required'); - // Должно быть валидным для непустых значений + // Should be valid for non-empty values const result4 = validator.validate(mockAttr, 'some value'); expect(result4.successed).toBe(true); @@ -183,7 +183,7 @@ describe('DateTimeValidator', () => { return key; }); - // Мок для getEditDateTimeFormat + // Mock for getEditDateTimeFormat jest.spyOn(utils, 'getEditDateTimeFormat').mockImplementation((dataType: DataType) => { if (dataType === DataType.Date) return 'dd.MM.yyyy'; if (dataType === DataType.Time) return 'HH:mm'; @@ -196,12 +196,12 @@ describe('DateTimeValidator', () => { jest.restoreAllMocks(); }); - it('должен иметь имя "DateTime"', () => { + it('should иметь имя "DateTime"', () => { expect(validator.name).toBe('DateTime'); expect(validator).toBeInstanceOf(Validator); }); - it('должен успешно проходить валидацию для null или пустой строки', () => { + it('should успешно проходить валидацию для null или пустой строки', () => { mockAttr = { dataType: DataType.Date } as MetaEntityAttr; const result1 = validator.validate(mockAttr, null); @@ -214,7 +214,7 @@ describe('DateTimeValidator', () => { expect(result3.successed).toBe(true); }); - it('должен проверять формат даты', () => { + it('should проверять формат даты', () => { mockAttr = { dataType: DataType.Date } as MetaEntityAttr; // Корректный формат @@ -227,14 +227,14 @@ describe('DateTimeValidator', () => { expect(result2.messages).toBeArray(); expect(result2.messages[0]).toBe('Invalid date format'); - // Некорректное значение + // Invalid value const result3 = validator.validate(mockAttr, '32.13.2023'); expect(result3.successed).toBe(false); expect(result3.messages).toBeArray(); expect(result3.messages[0]).toBe('Invalid date format'); }); - it('должен проверять формат времени', () => { + it('should проверять формат времени', () => { mockAttr = { dataType: DataType.Time } as MetaEntityAttr; // Корректный формат @@ -247,14 +247,14 @@ describe('DateTimeValidator', () => { expect(result2.messages).toBeArray(); expect(result2.messages[0]).toBe('Invalid date format'); - // Некорректное значение + // Invalid value const result3 = validator.validate(mockAttr, '25:70'); expect(result3.successed).toBe(false); expect(result3.messages).toBeArray(); expect(result3.messages[0]).toBe('Invalid date format'); }); - it('должен проверять формат даты и времени', () => { + it('should проверять формат даты и времени', () => { mockAttr = { dataType: DataType.DateTime } as MetaEntityAttr; // Корректный формат @@ -268,10 +268,10 @@ describe('DateTimeValidator', () => { expect(result2.messages[0]).toBe('Invalid date format'); }); - it('должен успешно проходить валидацию для нетиповых дат', () => { + it('should успешно проходить валидацию для нетиповых дат', () => { mockAttr = { dataType: DataType.String } as MetaEntityAttr; - // Любое значение должно быть валидным для строки + // Any value should be valid for string const result1 = validator.validate(mockAttr, '15.03.2023'); expect(result1.successed).toBe(true); From ed8c5bf4e7c9107f2ad0178fd5860c9930aa257c Mon Sep 17 00:00:00 2001 From: korzh Date: Sat, 19 Jul 2025 15:35:19 +0300 Subject: [PATCH 04/11] Update tests in Core part 2 --- .../packs/core/tests/easy_data_table.test.ts | 12 +- .../packs/core/tests/easy_guid.test.ts | 26 +-- .../packs/core/tests/event_emitter.test.ts | 54 +++--- .../core/tests/http_action_result.test.ts | 32 ++-- .../packs/core/tests/http_client.test.ts | 76 ++++---- .../packs/core/tests/http_request.test.ts | 20 +- easydata.js/packs/core/tests/i18n.test.ts | 118 ++++++------ easydata.js/packs/core/tests/liquid.test.ts | 28 +-- .../packs/core/tests/meta_data.test.ts | 178 +++++++++--------- .../packs/core/tests/meta_entity.test.ts | 44 ++--- .../packs/core/tests/string_utils.test.ts | 12 +- .../packs/core/tests/time_utils.test.ts | 50 ++--- easydata.js/packs/core/tests/utils.test.ts | 2 +- .../packs/core/tests/value_editor.test.ts | 10 +- 14 files changed, 331 insertions(+), 331 deletions(-) diff --git a/easydata.js/packs/core/tests/easy_data_table.test.ts b/easydata.js/packs/core/tests/easy_data_table.test.ts index 3e618111..5defb8a3 100644 --- a/easydata.js/packs/core/tests/easy_data_table.test.ts +++ b/easydata.js/packs/core/tests/easy_data_table.test.ts @@ -170,7 +170,7 @@ describe('EasyDataTable', () => { rows: testData }); - // Запрос за пределами доступных данных + // Request beyond available data return table.getRows({ offset: 10, limit: 5 }).then(rows => { expect(rows.length).toBe(0); }); @@ -189,7 +189,7 @@ describe('EasyDataTable', () => { }); }); - it('should return null при запросе несуществующей строки', () => { + it('should return null when requesting non-existent row', () => { const table = new EasyDataTable({ columns: defaultColumns, rows: testData @@ -212,7 +212,7 @@ describe('EasyDataTable', () => { expect(table.getTotal()).toBe(10); expect(table.getCachedCount()).toBe(4); // entire test dataset is cached - // Check кеширования + // Check caching return table.getRows({ offset: 2, limit: 2 }).then(moreRows => { expect(moreRows.length).toBe(2); expect(table.getCachedCount()).toBe(4); @@ -246,7 +246,7 @@ describe('EasyDataTable', () => { table.chunkSize = 50; expect(table.chunkSize).toBe(50); - expect(table.getCachedCount()).toBe(0); // Кеш очищен + expect(table.getCachedCount()).toBe(0); // Cache cleared }); it('should execute onUpdate callback on data update', () => { @@ -285,10 +285,10 @@ describe('EasyDataTable', () => { expect(table.getTotal()).toBe(0); }); - it('should throw error при запросе данных без loader', async () => { + it('should throw error when requesting data without loader', async () => { const table = new EasyDataTable(); - // Нет данных в кеше и нет loader + // No data in cache and no loader try { await table.getRows({ offset: 0, limit: 10 }); fail('Error should have been thrown'); diff --git a/easydata.js/packs/core/tests/easy_guid.test.ts b/easydata.js/packs/core/tests/easy_guid.test.ts index 68d18aa5..8622cb33 100644 --- a/easydata.js/packs/core/tests/easy_guid.test.ts +++ b/easydata.js/packs/core/tests/easy_guid.test.ts @@ -1,48 +1,48 @@ import { EasyGuid } from '../src/utils/easy_guid'; describe('EasyGuid', () => { - it('should генерировать строку в формате GUID', () => { + it('should generate string in GUID format', () => { const guid = EasyGuid.newGuid(); - // Check, что возвращаемое значение - строка + // Check that returned value is a string expect(typeof guid).toBe('string'); - // Check длину GUID (должна быть 36 символов включая дефисы) + // Check GUID length (should be 36 characters including dashes) expect(guid.length).toBe(36); - // Check формат GUID с помощью регулярного выражения - // Формат: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx, где y - это 8, 9, a или b + // Check GUID format using regular expression + // Format: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx, where y is 8, 9, a or b const guidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; expect(guidRegex.test(guid)).toBe(true); - // Check, что 4-й символ в третьей группе - это 4 (версия GUID) + // Check that the 4th character in the third group is 4 (GUID version) expect(guid.charAt(14)).toBe('4'); }); - it('should генерировать уникальные GUID при каждом вызове', () => { - const guidCount = 1000; // Check на большом количестве GUIDов + it('should generate unique GUIDs on each call', () => { + const guidCount = 1000; // Check on large number of GUIDs const guidSet = new Set(); for (let i = 0; i < guidCount; i++) { guidSet.add(EasyGuid.newGuid()); } - // Если все GUIDы уникальны, размер Set should равняться guidCount + // If all GUIDs are unique, Set size should equal guidCount expect(guidSet.size).toBe(guidCount); }); - it('should генерировать GUIDы с правильными позициями дефисов', () => { + it('should generate GUIDs with correct dash positions', () => { const guid = EasyGuid.newGuid(); - // Check позиции дефисов + // Check dash positions expect(guid.charAt(8)).toBe('-'); expect(guid.charAt(13)).toBe('-'); expect(guid.charAt(18)).toBe('-'); expect(guid.charAt(23)).toBe('-'); }); - it('should генерировать GUIDы с правильным значением в первом разряде третьей группы', () => { - // Согласно спецификации GUID первый разряд третьей группы should быть 8, 9, A или B + it('should generate GUIDs with correct value in first digit of third group', () => { + // According to GUID specification, first digit of third group should be 8, 9, A or B const allowedChars = ['8', '9', 'a', 'b', 'A', 'B']; for (let i = 0; i < 100; i++) { diff --git a/easydata.js/packs/core/tests/event_emitter.test.ts b/easydata.js/packs/core/tests/event_emitter.test.ts index 18838177..ce04a9b0 100644 --- a/easydata.js/packs/core/tests/event_emitter.test.ts +++ b/easydata.js/packs/core/tests/event_emitter.test.ts @@ -9,10 +9,10 @@ describe('EventEmitter', () => { eventEmitter = new EventEmitter(source); }); - it('should создаваться с указанным источником', () => { + it('should be created with a specified source', () => { expect(eventEmitter).toBeDefined(); - - // Нам нужно вызвать событие, чтобы проверить источник + + // We need to fire an event to check the source const testCallback = (event: EqEvent) => { expect(event.source).toBe(source); }; @@ -21,7 +21,7 @@ describe('EventEmitter', () => { eventEmitter.fire('testEvent'); }); - it('should return ID подписки при подписке на событие', () => { + it('should return ID of subscription when subscribing to an event', () => { const callback = (event: EqEvent) => {}; const subscriptionId = eventEmitter.subscribe('testEvent', callback); @@ -31,7 +31,7 @@ describe('EventEmitter', () => { expect(subscriptionId.length).toBeGreaterThan(0); }); - it('should вызывать колбэк при активации события', () => { + it('should call a callback when event is fired', () => { let callbackCalled = false; const testData = { test: 'data' }; @@ -48,7 +48,7 @@ describe('EventEmitter', () => { expect(callbackCalled).toBe(true); }); - it('should вызывать несколько колбэков для одного события', () => { + it('should call multiple callbacks for a single event', () => { let callCount = 0; const callback1 = () => { callCount++; }; @@ -64,7 +64,7 @@ describe('EventEmitter', () => { expect(callCount).toBe(3); }); - it('should вызывать только колбэки для указанного типа события', () => { + it('should call only callbacks for the specified event type', () => { let event1CallCount = 0; let event2CallCount = 0; @@ -77,7 +77,7 @@ describe('EventEmitter', () => { expect(event2CallCount).toBe(0); }); - it('не should вызывать колбэк после отписки', () => { + it('should not call a callback after unsubscribing', () => { let callbackCalled = false; const callback = () => { callbackCalled = true; }; @@ -89,21 +89,21 @@ describe('EventEmitter', () => { expect(callbackCalled).toBe(false); }); - it('should правильно работать при отписке несуществующего ID', () => { - // Не должно выбрасывать исключение + it('should work correctly when unsubscribing non-existent ID', () => { + // Should not throw an exception expect(() => { eventEmitter.unsubscribe('testEvent', 'non-existent-id'); }).not.toThrow(); }); - it('should правильно работать при попытке активации несуществующего события', () => { - // Не должно выбрасывать исключение + it('should work correctly when firing non-existent event', () => { + // Should not throw an exception expect(() => { eventEmitter.fire('non-existent-event'); }).not.toThrow(); }); - it('should откладывать выполнение события с параметром postpone', (done) => { + it('should postpone event execution with a postpone parameter', (done) => { let callbackCalled = false; const callback = () => { @@ -113,13 +113,13 @@ describe('EventEmitter', () => { eventEmitter.subscribe('testEvent', callback); - eventEmitter.fire('testEvent', null, 50); // отложить на 50 мс + eventEmitter.fire('testEvent', null, 50); // delay for 50 ms - // Сразу после вызова fire колбэк еще не should быть вызван + // Immediately after calling fire, callback should not be called yet expect(callbackCalled).toBe(false); }); - it('should входить в тихий режим и выходить из него', () => { + it('should enter silent mode and exit from it', () => { let callbackCalled = false; const callback = () => { callbackCalled = true; }; @@ -141,56 +141,56 @@ describe('EventEmitter', () => { eventEmitter.exitSilentMode(); expect(eventEmitter.isSilent()).toBe(false); - // После выхода из тихого режима колбэк should вызываться + // After exiting silent mode, callback should be called eventEmitter.fire('testEvent'); expect(callbackCalled).toBe(true); }); - it('should поддерживать вложенный тихий режим', () => { + it('should support nested silent mode', () => { let callbackCalled = false; const callback = () => { callbackCalled = true; }; eventEmitter.subscribe('testEvent', callback); - // Входим в тихий режим дважды + // Enter silent mode twice eventEmitter.enterSilentMode(); eventEmitter.enterSilentMode(); expect(eventEmitter.isSilent()).toBe(true); - // Выходим из тихого режима один раз - все еще должны быть в тихом режиме + // Exit silent mode once - should still be in silent mode eventEmitter.exitSilentMode(); expect(eventEmitter.isSilent()).toBe(true); - // В тихом режиме колбэк не should вызываться + // In silent mode, callback should not be called eventEmitter.fire('testEvent'); expect(callbackCalled).toBe(false); - // Выходим из тихого режима еще раз + // Exit silent mode once more eventEmitter.exitSilentMode(); expect(eventEmitter.isSilent()).toBe(false); - // После полного выхода из тихого режима колбэк should вызываться + // After fully exiting silent mode, callback should be called eventEmitter.fire('testEvent'); expect(callbackCalled).toBe(true); }); - it('should принудительно вызывать события в тихом режиме с параметром force', () => { + it('should force trigger events in silent mode with force parameter', () => { let callbackCalled = false; const callback = () => { callbackCalled = true; }; eventEmitter.subscribe('testEvent', callback); - // Входим в тихий режим + // Enter silent mode eventEmitter.enterSilentMode(); expect(eventEmitter.isSilent()).toBe(true); - // Обычный вызов не should срабатывать + // Normal call should not trigger eventEmitter.fire('testEvent'); expect(callbackCalled).toBe(false); - // Принудительный вызов should сработать даже в тихом режиме + // Forced call should work even in silent mode eventEmitter.fire('testEvent', null, 0, true); expect(callbackCalled).toBe(true); }); diff --git a/easydata.js/packs/core/tests/http_action_result.test.ts b/easydata.js/packs/core/tests/http_action_result.test.ts index 5f2b1b79..975075af 100644 --- a/easydata.js/packs/core/tests/http_action_result.test.ts +++ b/easydata.js/packs/core/tests/http_action_result.test.ts @@ -13,26 +13,26 @@ describe('HttpActionResult', () => { method: 'GET' } as HttpRequest; - // Создаем Promise, который успешно завершается со значением 'success' + // Create Promise that successfully resolves with value 'success' mockPromise = Promise.resolve('success'); // Create instance HttpActionResult actionResult = new HttpActionResult(mockRequest, mockPromise); }); - it('should сохранять request и promise переданные в конструктор', () => { + it('should store request and promise passed to constructor', () => { expect(actionResult.getRequest()).toBe(mockRequest); expect(actionResult.getPromise()).toBe(mockPromise); }); - it('should return исходный request через getRequest', () => { + it('should return original request via getRequest', () => { const request = actionResult.getRequest(); expect(request).toBe(mockRequest); expect(request.url).toBe('https://test.com/api'); expect(request.method).toBe('GET'); }); - it('should return исходный promise через getPromise', () => { + it('should return original promise via getPromise', () => { const promise = actionResult.getPromise(); expect(promise).toBe(mockPromise); @@ -41,7 +41,7 @@ describe('HttpActionResult', () => { }); }); - it('should реализовать метод then и получить значение из promise', () => { + it('should implement then method and get value from promise', () => { let resultValue = ''; return actionResult.then(value => { @@ -53,8 +53,8 @@ describe('HttpActionResult', () => { }); }); - it('should правильно обрабатывать rejected promise в методе then', () => { - // Создаем rejected promise + it('should properly handle rejected promise in then method', () => { + // Create rejected promise const rejectedPromise = Promise.reject(new Error('error message')); const rejectedResult = new HttpActionResult(mockRequest, rejectedPromise); @@ -64,11 +64,11 @@ describe('HttpActionResult', () => { return rejectedResult .then( () => { - // Этот код не should выполниться + // This code should not execute errorInThen = true; }, (error) => { - // Должны попасть сюда + // Should end up here errorCaught = true; expect(error.message).toBe('error message'); return 'recovered'; @@ -81,8 +81,8 @@ describe('HttpActionResult', () => { }); }); - it('should реализовать метод catch и обрабатывать ошибки', () => { - // Создаем rejected promise + it('should implement catch method and handle errors', () => { + // Create rejected promise const rejectedPromise = Promise.reject(new Error('test error')); const rejectedResult = new HttpActionResult(mockRequest, rejectedPromise); @@ -100,7 +100,7 @@ describe('HttpActionResult', () => { }); }); - it('should реализовать метод finally и вызвать его независимо от результата', () => { + it('should implement "finally" method and call it regardless of result', () => { let finallyCalled = false; return actionResult @@ -112,8 +112,8 @@ describe('HttpActionResult', () => { }); }); - it('should вызвать finally даже при ошибке', () => { - // Создаем rejected promise + it('should call finally even on error', () => { + // Create rejected promise const rejectedPromise = Promise.reject(new Error('test error')); const rejectedResult = new HttpActionResult(mockRequest, rejectedPromise); @@ -128,7 +128,7 @@ describe('HttpActionResult', () => { }); }); - it('should поддерживать цепочку методов then, catch и finally', () => { + it('should support method chaining of then, catch and finally', () => { let thenCalled = false; let catchCalled = false; let finallyCalled = false; @@ -140,7 +140,7 @@ describe('HttpActionResult', () => { return value.toUpperCase(); }) .catch(() => { - // Не should быть вызван + // Should not be called catchCalled = true; return 'error'; }) diff --git a/easydata.js/packs/core/tests/http_client.test.ts b/easydata.js/packs/core/tests/http_client.test.ts index d8611cb6..46af7177 100644 --- a/easydata.js/packs/core/tests/http_client.test.ts +++ b/easydata.js/packs/core/tests/http_client.test.ts @@ -27,7 +27,7 @@ describe('HttpClient', () => { onerror: null }; - // Save оригинальный XMLHttpRequest и заменяем его на мок + // Save original XMLHttpRequest and replace it with mock originalXMLHttpRequest = window.XMLHttpRequest; window.XMLHttpRequest = mock().mockImplementation(() => { const xhr = xhrMock; @@ -39,17 +39,17 @@ describe('HttpClient', () => { }); afterEach(() => { - // Restore оригинальный XMLHttpRequest + // Restore original XMLHttpRequest window.XMLHttpRequest = originalXMLHttpRequest; }); - it('should создаваться с дефолтными настройками', () => { + it('should be created with default settings', () => { expect(httpClient.defaultHeaders).toBeObject({}); expect(httpClient.customPayload).toBeUndefined(); expect(httpClient.responseBody).toBeUndefined(); }); - it('should выполнять GET запрос с правильными параметрами', () => { + it('should perform GET request with correct parameters', () => { const url = 'https://test.com/api/data'; const options = { headers: { 'X-Custom-Header': 'test' }, @@ -65,15 +65,15 @@ describe('HttpClient', () => { true ); - // Check установку заголовков + // Check header setup expect(xhrMock.setRequestHeader).toHaveBeenCalledWith('X-Custom-Header', 'test'); expect(xhrMock.setRequestHeader).toHaveBeenCalledWith('Content-Type', 'application/json'); - // Check вызов send + // Check send call expect(xhrMock.send).toHaveBeenCalledWith(null); }); - it('should выполнять POST запрос с данными', () => { + it('should perform POST request with data', () => { const url = 'https://test.com/api/data'; const data = { name: 'John', age: 30 }; @@ -95,7 +95,7 @@ describe('HttpClient', () => { expect(xhrMock.send).toHaveBeenCalledWith(JSON.stringify(data)); }); - it('should выполнять DELETE запрос', () => { + it('should perform DELETE request', () => { const url = 'https://test.com/api/data/1'; httpClient.delete(url); @@ -105,7 +105,7 @@ describe('HttpClient', () => { expect(xhrMock.send).toHaveBeenCalledWith(null); }); - it('should добавлять defaultHeaders к запросу', () => { + it('should add defaultHeaders to request', () => { httpClient.defaultHeaders = { 'Authorization': 'Bearer test-token', 'X-Api-Key': 'api-key' @@ -117,7 +117,7 @@ describe('HttpClient', () => { expect(xhrMock.setRequestHeader).toHaveBeenCalledWith('X-Api-Key', 'api-key'); }); - it('should добавлять customPayload к данным запроса', () => { + it('should add customPayload to request data', () => { const data = { name: 'John' }; httpClient.customPayload = ['custom-payload-value']; @@ -129,9 +129,9 @@ describe('HttpClient', () => { })); }); - it('should вызывать onRequest после формирования запроса', () => { + it('should call onRequest after forming request', () => { const onRequestMock = mock((request: HttpRequest) => { - // Изменяем запрос в обработчике + // Modify request in handler request.url = request.url + '/modified'; }); @@ -142,43 +142,43 @@ describe('HttpClient', () => { expect(xhrMock.open).toHaveBeenCalledWith('GET', 'https://test.com/api/data/modified', true); }); - it('should выбрасывать предупреждение при использовании устаревшего beforeEachRequest', () => { - expect(1).toBe(1); // Замените на реальный тест, если нужно + it('should throw warning when using deprecated beforeEachRequest', () => { + expect(1).toBe(1); // Replace with real test if needed }); - it('should обрабатывать успешный ответ с JSON данными', async () => { + it('should handle successful response with JSON data', async () => { const responsePromise = httpClient.get('https://test.com/api/data'); - // Emulate успешный ответ + // Emulate a successful response xhrMock.onreadystatechange(); const response = await responsePromise; expect(response).toBeObject({ success: true, data: 'test' }); expect(httpClient.responseBody).toBeObject({ success: true, data: 'test' }); }); - - it('should вызывать onResponse при успешном ответе', () => { + + it('should call onResponse on successful response', () => { const onResponseMock = mock(); httpClient.onResponse = onResponseMock; const responsePromise = httpClient.get('https://test.com/api/data'); - // Emulate успешный ответ + // Emulate a successful response xhrMock.onreadystatechange(); expect(onResponseMock).toHaveBeenCalledWith(xhrMock); }); - it('should обрабатывать ошибки сети', async () => { + it('should handle network errors', async () => { const responsePromise = httpClient.get('https://test.com/api/data'); - // Emulate ошибку сети + // Emulate a network error xhrMock.status = 0; xhrMock.onreadystatechange(); try { await responsePromise; - fail('Должно было выбросить исключение'); + fail('It should have thrown an exception'); } catch (error) { expect(error).toBeInstanceOf(HttpResponseError); @@ -187,7 +187,7 @@ describe('HttpClient', () => { } }); - it('should обрабатывать HTTP ошибки', async () => { + it('should handle HTTP errors', async () => { const responsePromise = httpClient.get('https://test.com/api/data'); // Emulate HTTP ошибку @@ -197,7 +197,7 @@ describe('HttpClient', () => { try { await responsePromise; - fail('Должно было выбросить исключение'); + fail('It should have thrown an exception'); } catch (error) { expect(error).toBeInstanceOf(HttpResponseError); @@ -205,18 +205,18 @@ describe('HttpClient', () => { expect(error.message).toBe('Resource not found'); } }); - - it('should обрабатывать HTTP ошибку без сообщения', async () => { + + it('should handle HTTP error without message', async () => { const responsePromise = httpClient.get('https://test.com/api/data'); - - // Emulate HTTP ошибку без поля message в ответе + + // Emulate HTTP error without message field in response xhrMock.status = 404; xhrMock.responseText = '{}'; xhrMock.onreadystatechange(); try { await responsePromise; - fail('Должно было выбросить исключение'); + fail('It should have thrown an exception'); } catch (error) { expect(error).toBeInstanceOf(HttpResponseError); @@ -224,9 +224,9 @@ describe('HttpClient', () => { expect(error.message).toBe('No such endpoint: https://test.com/api/data'); } }); - - it('should обрабатывать ответы с разными типами данных', async () => { - // Тестируем text ответ + + it('should handle responses with different data types', async () => { + // Testing text response xhrMock.getResponseHeader.mockReturnValue('text/plain'); xhrMock.responseText = 'Plain text response'; @@ -238,7 +238,7 @@ describe('HttpClient', () => { expect(httpClient.responseBody).toBe('Plain text response'); }); - it('should поддерживать параметры responseType', () => { + it('should support responseType parameters', () => { httpClient.get('https://test.com/api/binary', { responseType: 'arraybuffer' }); @@ -246,7 +246,7 @@ describe('HttpClient', () => { expect(xhrMock.responseType).toBe('arraybuffer'); }); - it('should return экземпляр HttpActionResult', () => { + it('should return HttpActionResult instance', () => { const result = httpClient.get('https://test.com/api/data'); expect(result).toBeInstanceOf(HttpActionResult); @@ -254,7 +254,7 @@ describe('HttpClient', () => { expect(result.getPromise()).toBeDefined(); }); - it('should correctly handle форм-данные', () => { + it('should correctly handle form data', () => { const formData = new FormData(); formData.append('file', new Blob(['test content'], { type: 'text/plain' })); @@ -262,16 +262,16 @@ describe('HttpClient', () => { dataType: 'form-data' }); - // Для form-data не устанавливается Content-Type + // For form-data, Content-Type is not set const contentTypeHeader = Array.from(xhrMock.setRequestHeader.mock.calls) .find(call => call[0] === 'Content-Type'); expect(contentTypeHeader).toBeUndefined(); - // Check что данные отправляются как есть + // Check that data is sent as is expect(xhrMock.send).toHaveBeenCalledWith(formData); }); - it('should correctly формировать URL с query параметрами', () => { + it('should correctly form URL with query parameters', () => { httpClient.get('https://test.com/api/data', { queryParams: { id: 123, diff --git a/easydata.js/packs/core/tests/http_request.test.ts b/easydata.js/packs/core/tests/http_request.test.ts index 38d4be45..0d32527b 100644 --- a/easydata.js/packs/core/tests/http_request.test.ts +++ b/easydata.js/packs/core/tests/http_request.test.ts @@ -19,7 +19,7 @@ describe('HttpRequest', () => { getAllResponseHeaders: mock().mockReturnValue('Content-Type: application/json\nX-Custom-Header: test') }; - // Базовый дескриптор запроса для тестов + // Basic request descriptor for tests requestDescriptor = { method: HttpMethod.GET, url: 'https://test.com/api', @@ -30,7 +30,7 @@ describe('HttpRequest', () => { }; }); - it('should инициализировать запрос с переданными параметрами', () => { + it('should initialize request with passed parameters', () => { const request = new HttpRequest(xhrMock, requestDescriptor); expect(request.method).toBe(HttpMethod.GET); @@ -38,7 +38,7 @@ describe('HttpRequest', () => { expect(request.data).toBeUndefined(); }); - it('should сохранять переданные данные', () => { + it('should store passed data', () => { const data = { test: 'value' }; requestDescriptor.data = data; @@ -47,7 +47,7 @@ describe('HttpRequest', () => { expect(request.data).toBe(data); }); - it('should добавлять заголовок с помощью setHeader', () => { + it('should add header using setHeader', () => { const request = new HttpRequest(xhrMock, requestDescriptor); request.setHeader('Authorization', 'Bearer token123'); @@ -56,7 +56,7 @@ describe('HttpRequest', () => { expect(xhrMock.setRequestHeader).toHaveBeenCalledWith('Authorization', 'Bearer token123'); }); - it('should добавлять параметры запроса с помощью setQueryParam', () => { + it('should add query parameters using setQueryParam', () => { const request = new HttpRequest(xhrMock, requestDescriptor); request.setQueryParam('id', '123'); @@ -65,7 +65,7 @@ describe('HttpRequest', () => { expect(xhrMock.open).toHaveBeenCalledWith('GET', 'https://test.com/api?id=123', true); }); - it('should правильно объединять несколько параметров запроса', () => { + it('should correctly combine multiple query parameters', () => { requestDescriptor.queryParams = { 'id': '123', 'name': 'test' @@ -77,7 +77,7 @@ describe('HttpRequest', () => { expect(xhrMock.open).toHaveBeenCalledWith('GET', 'https://test.com/api?id=123&name=test', true); }); - it('should return XMLHttpRequest через getXMLHttpRequest', () => { + it('should return XMLHttpRequest via getXMLHttpRequest', () => { const request = new HttpRequest(xhrMock, requestDescriptor); const xhr = request.getXMLHttpRequest(); @@ -85,7 +85,7 @@ describe('HttpRequest', () => { expect(xhr).toBe(xhrMock); }); - it('should парсить заголовки ответа через getResponseHeaders', () => { + it('should parse response headers via getResponseHeaders', () => { xhrMock.readyState = xhrMock.HEADERS_RECEIVED; const request = new HttpRequest(xhrMock, requestDescriptor); @@ -97,7 +97,7 @@ describe('HttpRequest', () => { }); }); - it('should return пустой объект заголовков когда readyState не HEADERS_RECEIVED', () => { + it('should return empty headers object when readyState is not HEADERS_RECEIVED', () => { xhrMock.readyState = xhrMock.UNSENT; const request = new HttpRequest(xhrMock, requestDescriptor); @@ -106,7 +106,7 @@ describe('HttpRequest', () => { expect(headers).toBeObject({}); }); - it('should открывать запрос с правильными параметрами', () => { + it('should open request with correct parameters', () => { const request = new HttpRequest(xhrMock, requestDescriptor); request.open(); diff --git a/easydata.js/packs/core/tests/i18n.test.ts b/easydata.js/packs/core/tests/i18n.test.ts index e8881293..b4812aab 100644 --- a/easydata.js/packs/core/tests/i18n.test.ts +++ b/easydata.js/packs/core/tests/i18n.test.ts @@ -5,23 +5,23 @@ import { DataType } from '../src/types/data_type'; describe('i18n module', () => { beforeEach(() => { - // Reset локали перед каждым тестом + // Reset locales before each test i18n.resetLocales(); }); - it('should иметь правильную текущую локаль по умолчанию', () => { + it('should have correct current locale by default', () => { expect(i18n.getCurrentLocale()).toBe('en-US'); }); - it('should return список доступных локалей', () => { - // По умолчанию должна быть только одна локаль + it('should return list of available locales', () => { + // By default should have only one locale const locales = i18n.getLocales(); expect(locales.length).toBe(1); expect(locales[0].locale).toBe('en-US'); expect(locales[0].englishName).toBe('English'); expect(locales[0].displayName).toBe('English'); - // Добавим ещё одну локаль + // Add another locale i18n.addLocale('uk-UA', { englishName: 'Ukrainian', displayName: 'Українська', @@ -31,7 +31,7 @@ describe('i18n module', () => { } }); - // Теперь should be две локали, отсортированные по englishName + // Now should have two locales, sorted by englishName const updatedLocales = i18n.getLocales(); expect(updatedLocales.length).toBe(2); expect(updatedLocales[0].locale).toBe('en-US'); @@ -40,13 +40,13 @@ describe('i18n module', () => { expect(updatedLocales[1].englishName).toBe('Ukrainian'); }); - it('should получать тексты из текущей локали', () => { - // Check стандартных текстов в en-US + it('should get texts from current locale', () => { + // Check standard texts in en-US expect(i18n.getText('ButtonOK')).toBe('OK'); expect(i18n.getText('ButtonCancel')).toBe('Cancel'); expect(i18n.getText('Yes')).toBe('Yes'); - // Добавление новой локали + // Adding new locale i18n.addLocale('de-DE', { englishName: 'German', displayName: 'Deutsch', @@ -58,7 +58,7 @@ describe('i18n module', () => { } }); - // Переключение на новую локаль и проверка текстов + // Switching to new locale and checking texts i18n.setCurrentLocale('de-DE'); expect(i18n.getCurrentLocale()).toBe('de-DE'); expect(i18n.getText('ButtonOK')).toBe('OK'); @@ -67,7 +67,7 @@ describe('i18n module', () => { expect(i18n.getText('No')).toBe('Nein'); }); - it('should работать с вложенными текстовыми ресурсами', () => { + it('should work with nested text resources', () => { i18n.updateLocaleTexts({ Menu: { File: 'File', @@ -86,25 +86,25 @@ describe('i18n module', () => { expect(i18n.getText('Menu', 'Settings', 'Advanced')).toBe('Advanced'); }); - it('should обновлять тексты для текущей локали', () => { - // Обновление текстов текущей локали + it('should update texts for current locale', () => { + // Update current locale texts i18n.updateLocaleTexts({ ButtonOK: 'Okay', ButtonCancel: 'Dismiss', NewText: 'Brand new text' }); - // Check обновленных текстов + // Check updated texts expect(i18n.getText('ButtonOK')).toBe('Okay'); expect(i18n.getText('ButtonCancel')).toBe('Dismiss'); expect(i18n.getText('NewText')).toBe('Brand new text'); - // Check что неизменные тексты остались прежними + // Check that unchanged texts remained the same expect(i18n.getText('Yes')).toBe('Yes'); }); - it('should обновлять тексты по умолчанию для всех локалей', () => { - // Добавим новую локаль + it('should update default texts for all locales', () => { + // Add new locale i18n.addLocale('fr-FR', { englishName: 'French', displayName: 'Français', @@ -114,70 +114,70 @@ describe('i18n module', () => { } }); - // Обновляем тексты по умолчанию + // Update default texts i18n.updateDefaultTexts({ NewGlobalText: 'New text for all locales' }); - // Check английскую локаль + // Check English locale i18n.setCurrentLocale('en-US'); expect(i18n.getText('NewGlobalText')).toBe('New text for all locales'); - // Check французскую локаль + // Check French locale i18n.setCurrentLocale('fr-FR'); expect(i18n.getText('NewGlobalText')).toBe('New text for all locales'); - // Убеждаемся что специфичные для локали тексты остались + // Make sure locale-specific texts remained expect(i18n.getText('ButtonCancel')).toBe('Annuler'); }); - it('should работать с настройками локали', () => { + it('should work with locale settings', () => { const settings = i18n.getLocaleSettings(); - // Check форматов даты и времени + // Check date and time formats expect(settings.shortDateFormat).toBe('MM/dd/yyyy'); expect(settings.longDateFormat).toBe('dd MMM, yyyy'); expect(settings.shortTimeFormat).toBe('HH:mm'); expect(settings.longTimeFormat).toBe('HH:mm:ss'); - // Check названий месяцев + // Check month names expect(settings.shortMonthNames.length).toBe(12); expect(settings.shortMonthNames[0]).toBe('Jan'); expect(settings.longMonthNames[0]).toBe('January'); - // Check названий дней недели + // Check weekday names expect(settings.shortWeekDayNames.length).toBe(7); expect(settings.shortWeekDayNames[0]).toBe('Sun'); expect(settings.longWeekDayNames[0]).toBe('Sunday'); - // Check разделителя десятичных и валюты + // Check decimal separator and currency expect(settings.decimalSeparator).toBe('.'); expect(settings.currency).toBe('USD'); }); - it('should return названия месяцев', () => { + it('should return month names', () => { expect(i18n.getShortMonthName(1)).toBe('Jan'); expect(i18n.getLongMonthName(1)).toBe('January'); expect(i18n.getShortMonthName(12)).toBe('Dec'); expect(i18n.getLongMonthName(12)).toBe('December'); - // Check исключений при неверном номере месяца + // Check exceptions for invalid month number expect(() => i18n.getShortMonthName(0)).toThrow(); expect(() => i18n.getLongMonthName(13)).toThrow(); }); - it('should return названия дней недели', () => { + it('should return weekday names', () => { expect(i18n.getShortWeekDayName(1)).toBe('Sun'); expect(i18n.getLongWeekDayName(1)).toBe('Sunday'); expect(i18n.getShortWeekDayName(7)).toBe('Sat'); expect(i18n.getLongWeekDayName(7)).toBe('Saturday'); - // Check исключений при неверном номере дня + // Check exceptions for invalid day number expect(() => i18n.getShortWeekDayName(0)).toThrow(); expect(() => i18n.getLongWeekDayName(8)).toThrow(); }); - it('should обновлять настройки локали', () => { - // Обновление настроек локали + it('should update locale settings', () => { + // Update locale settings i18n.updateLocaleSettings({ shortDateFormat: 'dd.MM.yyyy', longDateFormat: 'dd MMMM yyyy', @@ -187,20 +187,20 @@ describe('i18n module', () => { const settings = i18n.getLocaleSettings(); - // Check обновленных настроек + // Check updated settings expect(settings.shortDateFormat).toBe('dd.MM.yyyy'); expect(settings.longDateFormat).toBe('dd MMMM yyyy'); expect(settings.decimalSeparator).toBe(','); expect(settings.currency).toBe('EUR'); - // Check, что неизмененные настройки остались прежними + // Check that unchanged settings remained the same expect(settings.shortTimeFormat).toBe('HH:mm'); }); - it('should форматировать дату и время по формату', () => { - const date = new Date(2023, 4, 15, 14, 30, 45); // 15 мая 2023 14:30:45 + it('should format date and time by format', () => { + const date = new Date(2023, 4, 15, 14, 30, 45); // May 15, 2023 14:30:45 - // Форматирование по шаблону + // Formatting by template expect(i18n.dateTimeToStr(date, 'yyyy-MM-dd')).toBe('2023-05-15'); expect(i18n.dateTimeToStr(date, 'dd/MM/yyyy')).toBe('15/05/2023'); expect(i18n.dateTimeToStr(date, 'MMM dd, yyyy')).toBe('May 15, 2023'); @@ -208,14 +208,14 @@ describe('i18n module', () => { expect(i18n.dateTimeToStr(date, 'HH:mm:ss')).toBe('14:30:45'); expect(i18n.dateTimeToStr(date, 'hh:mm tt')).toBe('02:30 PM'); - // Check форматирования с текстом в скобках + // Check formatting with text in brackets expect(i18n.dateTimeToStr(date, 'yyyy-MM-dd [at] HH:mm')).toBe('2023-05-15 at 14:30'); }); - it('should форматировать дату и время с использованием расширенной функции', () => { - const date = new Date(2023, 4, 15, 14, 30, 45); // 15 мая 2023 14:30:45 + it('should format date and time using extended function', () => { + const date = new Date(2023, 4, 15, 14, 30, 45); // May 15, 2023 14:30:45 - // Форматирование с использованием настроек текущей локали + // Formatting using current locale settings i18n.updateLocaleSettings({ shortDateFormat: 'dd.MM.yyyy', longDateFormat: 'dd MMMM, yyyy', @@ -223,58 +223,58 @@ describe('i18n module', () => { longTimeFormat: 'HH:mm:ss' }); - // Использование предопределенных форматов + // Using predefined formats expect(i18n.dateTimeToStrEx(date, DataType.Date, 'd')).toBe('15.05.2023'); expect(i18n.dateTimeToStrEx(date, DataType.Date, 'D')).toBe('15 May, 2023'); expect(i18n.dateTimeToStrEx(date, DataType.DateTime, 'f')).toBe('15.05.2023 14:30'); expect(i18n.dateTimeToStrEx(date, DataType.DateTime, 'F')).toBe('15 May, 2023 14:30:45'); expect(i18n.dateTimeToStrEx(date, DataType.Time)).toBe('14:30'); - // Универсальный формат + // Universal format expect(i18n.dateTimeToStrEx(date, DataType.Date, 'u')).toBe('2023-05-15'); expect(i18n.dateTimeToStrEx(date, DataType.DateTime, 'u')).toBe('2023-05-15 14:30'); }); - it('should форматировать числа', () => { - // Форматирование без указания формата + it('should format numbers', () => { + // Formatting without specifying format i18n.updateLocaleSettings({ decimalSeparator: ',' }); expect(i18n.numberToStr(123.45)).toBe('123,45'); - // С указанием десятичного разделителя + // With decimal separator specified expect(i18n.numberToStr(123.45, null, '.')).toBe('123.45'); - // Форматирование по формату D (decimal) + // Formatting by D format (decimal) expect(i18n.numberToStr(123, 'D3')).toBe('123'); - // Форматирование по формату F (float) + // Formatting by F format (float) expect(i18n.numberToStr(123.456, 'F2')).toBe('123.46'); - // Форматирование по формату C (currency) + // Formatting by C format (currency) i18n.updateLocaleSettings({ currency: 'USD' }); const currencyResult = i18n.numberToStr(1234.56, 'C2'); expect(currencyResult.indexOf('1,234.56')).toBeGreaterThan(-1); expect(currencyResult.indexOf('$')).toBeGreaterThan(-1); - // Форматирование с маской + // Formatting with mask expect(i18n.numberToStr(123456, '### ###')).toBe('123 456'); expect(i18n.numberToStr(123, '#####')).toBe('00123'); - // Форматирование с последовательностью + // Formatting with sequence expect(i18n.numberToStr(1, 'SOne=1|Two=2|Three=3')).toBe('One'); expect(i18n.numberToStr(2, 'SOne=1|Two=2|Three=3')).toBe('Two'); expect(i18n.numberToStr(4, 'SOne=1|Two=2|Three=3')).toBe('4'); }); - it('should форматировать логические значения', () => { - // По умолчанию (без формата) + it('should format boolean values', () => { + // By default (without format) expect(i18n.booleanToStr(true)).toBe('true'); expect(i18n.booleanToStr(false)).toBe('false'); - // С использованием формата S + // Using S format expect(i18n.booleanToStr(true, 'SFalse|True')).toBe('True'); expect(i18n.booleanToStr(false, 'SFalse|True')).toBe('False'); - // С использованием формата S и локализованных значений + // Using S format with localized values i18n.updateLocaleTexts({ Yes: 'Yes', No: 'No' @@ -284,24 +284,24 @@ describe('i18n module', () => { expect(i18n.booleanToStr(false, 'SNo|Yes')).toBe('No'); }); - it('should добавлять и вызывать маппер', () => { + it('should add and call mapper', () => { let mapperCalled = false; let mapperLocaleId: string; let mapperTexts: any; - // Добавление маппера + // Adding mapper i18n.addMapper((info) => { mapperCalled = true; mapperLocaleId = info.localeId; mapperTexts = info.texts; }); - // Вызов функции, которая должна вызвать маппер + // Call function that should invoke mapper i18n.updateLocaleTexts({ NewText: 'Test text' }); - // Check что маппер был вызван с правильными параметрами + // Check that mapper was called with correct parameters expect(mapperCalled).toBe(true); expect(mapperLocaleId).toBe('en-US'); expect(mapperTexts).toBeObject({ NewText: 'Test text' }); diff --git a/easydata.js/packs/core/tests/liquid.test.ts b/easydata.js/packs/core/tests/liquid.test.ts index 72d6362d..0854be15 100644 --- a/easydata.js/packs/core/tests/liquid.test.ts +++ b/easydata.js/packs/core/tests/liquid.test.ts @@ -1,7 +1,7 @@ import { liquid } from '../src/utils/liquid'; describe('liquid', () => { - it('should заменять одну переменную в шаблоне', () => { + it('should replace single variable in template', () => { const template = 'Hello, {{name}}!'; const vars = { name: 'World' }; @@ -10,7 +10,7 @@ describe('liquid', () => { expect(result).toBe('Hello, World!'); }); - it('should заменять несколько разных переменных', () => { + it('should replace multiple different variables', () => { const template = 'Hello, {{name}}! Today is {{day}}.'; const vars = { name: 'John', day: 'Monday' }; @@ -19,7 +19,7 @@ describe('liquid', () => { expect(result).toBe('Hello, John! Today is Monday.'); }); - it('should заменять повторяющиеся переменные', () => { + it('should replace repeating variables', () => { const template = '{{name}} wrote: "Hello, {{name}}!"'; const vars = { name: 'Alice' }; @@ -28,7 +28,7 @@ describe('liquid', () => { expect(result).toBe('Alice wrote: "Hello, Alice!"'); }); - it('should correctly handle числовые значения', () => { + it('should correctly handle numeric values', () => { const template = 'The answer is {{answer}}.'; const vars = { answer: 42 }; @@ -37,17 +37,17 @@ describe('liquid', () => { expect(result).toBe('The answer is 42.'); }); - it('should игнорировать неопределенные переменные', () => { + it('should ignore undefined variables', () => { const template = 'Hello, {{name}}! Your age is {{age}}.'; const vars = { name: 'Bob' }; const result = liquid.renderLiquidTemplate(template, vars); - // Неопределенная переменная остается в виде {{age}} + // Undefined variable remains as {{age}} expect(result).toBe('Hello, Bob! Your age is {{age}}.'); }); - it('should return оригинальный шаблон, если vars равен null', () => { + it('should return original template if vars is null', () => { const template = 'Hello, {{name}}!'; const result = liquid.renderLiquidTemplate(template, null); @@ -55,7 +55,7 @@ describe('liquid', () => { expect(result).toBe('Hello, {{name}}!'); }); - it('should return оригинальный шаблон, если vars равен undefined', () => { + it('should return original template if vars is undefined', () => { const template = 'Hello, {{name}}!'; const result = liquid.renderLiquidTemplate(template, undefined); @@ -63,7 +63,7 @@ describe('liquid', () => { expect(result).toBe('Hello, {{name}}!'); }); - it('should correctly работать с пустым шаблоном', () => { + it('should correctly work with empty template', () => { const template = ''; const vars = { name: 'World' }; @@ -72,7 +72,7 @@ describe('liquid', () => { expect(result).toBe(''); }); - it('should correctly работать с пустым объектом vars', () => { + it('should correctly work with empty vars object', () => { const template = 'Hello, {{name}}!'; const vars = {}; @@ -81,7 +81,7 @@ describe('liquid', () => { expect(result).toBe('Hello, {{name}}!'); }); - it('should correctly заменять на пустые значения', () => { + it('should correctly replace with empty values', () => { const template = 'Name: "{{name}}"'; const vars = { name: '' }; @@ -90,7 +90,7 @@ describe('liquid', () => { expect(result).toBe('Name: ""'); }); - it('should correctly работать с шаблоном без переменных', () => { + it('should correctly work with template without variables', () => { const template = 'Simple text without variables'; const vars = { name: 'World' }; @@ -99,7 +99,7 @@ describe('liquid', () => { expect(result).toBe('Simple text without variables'); }); - it('should correctly заменять переменные с истинным логическим значением', () => { + it('should correctly replace variables with true boolean value', () => { const template = 'Is active: {{isActive}}'; const vars = { isActive: true }; @@ -108,7 +108,7 @@ describe('liquid', () => { expect(result).toBe('Is active: true'); }); - it('should correctly заменять переменные с ложным логическим значением', () => { + it('should correctly replace variables with false boolean value', () => { const template = 'Is deleted: {{isDeleted}}'; const vars = { isDeleted: false }; diff --git a/easydata.js/packs/core/tests/meta_data.test.ts b/easydata.js/packs/core/tests/meta_data.test.ts index e435a165..e51b4a5f 100644 --- a/easydata.js/packs/core/tests/meta_data.test.ts +++ b/easydata.js/packs/core/tests/meta_data.test.ts @@ -15,7 +15,7 @@ describe('MetaData', () => { metaData = new MetaData(); }); - it('should создаваться с правильными значениями по умолчанию', () => { + it('should be created with correct default values', () => { expect(metaData.id).toBe('__none'); expect(metaData.name).toBe('Empty model'); expect(metaData.isEmpty()).toBe(true); @@ -23,7 +23,7 @@ describe('MetaData', () => { expect(metaData.rootEntity).toBeDefined(); }); - it('should создавать и возвращать объекты сущностей и атрибутов', () => { + it('should create and return entity and attribute objects', () => { const entity = metaData.createEntity(); expect(entity).toBeInstanceOf(MetaEntity); @@ -34,7 +34,7 @@ describe('MetaData', () => { expect(editor).toBeInstanceOf(ValueEditor); }); - it('should загружать данные из JSON-строки', () => { + it('should load data from JSON string', () => { const jsonData = JSON.stringify({ id: 'test-model', name: 'Test Model', @@ -53,7 +53,7 @@ describe('MetaData', () => { expect(metaData.version).toBe('1.0.0'); }); - it('should загружать данные из объекта', () => { + it('should load data from object', () => { const data: MetaDataDTO = { id: 'test-model', name: 'Test Model', @@ -72,7 +72,7 @@ describe('MetaData', () => { expect(metaData.version).toBe('1.0.0'); }); - it('should загружать редакторы из данных', () => { + it('should load editors from data', () => { const data: MetaDataDTO = { id: 'test-model', name: 'Test Model', @@ -100,7 +100,7 @@ describe('MetaData', () => { expect(metaData.editors[0].resType).toBe(DataType.String); }); - it('should загружать форматы отображения из данных', () => { + it('should load display formats from data', () => { const data: MetaDataDTO = { id: 'test-model', name: 'Test Model', @@ -131,7 +131,7 @@ describe('MetaData', () => { expect(dateTimeFormats[0].isdef).toBe(true); }); - it('should return формат отображения по умолчанию для типа', () => { + it('should return default display format for type', () => { const data: MetaDataDTO = { id: 'test-model', name: 'Test Model', @@ -157,21 +157,21 @@ describe('MetaData', () => { expect(defaultFormat.format).toBe('dd MMMM yyyy HH:mm'); }); - it('should return пустой массив для несуществующего типа формата', () => { + it('should return empty array for non-existent format type', () => { const formats = metaData.getDisplayFormatsForType(DataType.Bool); expect(formats).toBeArray(); expect(formats).toBeEmpty(); }); - it('should return null для формата по умолчанию несуществующего типа', () => { + it('should return null for non-existent default format type', () => { const defaultFormat = metaData.getDefaultFormat(DataType.Bool); expect(defaultFormat).toBeNull(); }); - it('should обнаруживать пустую модель данных', () => { + it('should detect empty data model', () => { expect(metaData.isEmpty()).toBe(true); - // Добавляем атрибут к корневой сущности + // Add attribute to root entity const attr = metaData.createEntityAttr(metaData.rootEntity); attr.id = 'TestAttribute'; metaData.rootEntity.attributes.push(attr); @@ -179,7 +179,7 @@ describe('MetaData', () => { expect(metaData.isEmpty()).toBe(false); }); - it('should находить редактор по его ID', () => { + it('should find editor by its ID', () => { const editor1 = metaData.createValueEditor(); editor1.id = 'editor1'; @@ -195,23 +195,23 @@ describe('MetaData', () => { expect(notFoundEditor).toBeNull(); }); - it('should находить атрибут по его ID', () => { + it('should find attribute by its ID', () => { // Create attributes const attr1 = metaData.createEntityAttr(metaData.rootEntity); attr1.id = 'attr1'; metaData.rootEntity.attributes.push(attr1); - - // Создаем дочернюю сущность + + // Create child entity const subEntity = metaData.createEntity(metaData.rootEntity); subEntity.name = 'SubEntity'; metaData.rootEntity.subEntities.push(subEntity); - - // Добавляем атрибут к дочерней сущности + + // Add attribute to child entity const attr2 = metaData.createEntityAttr(subEntity); attr2.id = 'attr2'; subEntity.attributes.push(attr2); - - // Check поиск по ID + + // Check search by ID const foundAttr1 = metaData.getAttributeById('attr1'); expect(foundAttr1).toBe(attr1); @@ -222,24 +222,24 @@ describe('MetaData', () => { expect(notFoundAttr).toBeNull(); }); - it('should проверять свойства атрибута', () => { - // Создаем атрибут + it('should check attribute properties', () => { + // Create attribute const attr = metaData.createEntityAttr(metaData.rootEntity); attr.id = 'attr1'; attr.caption = 'Attribute 1'; attr.description = 'Test description'; metaData.rootEntity.attributes.push(attr); - // Check существующее свойство + // Check existing property const hasDescription = metaData.checkAttrProperty('attr1', 'description'); expect(hasDescription).toBe(true); - // Check несуществующее свойство + // Check non-existent property expect(() => { metaData.checkAttrProperty('attr1', 'nonExistentProperty'); }).toThrow('No such property: nonExistentProperty'); - // Check атрибут с lookup + // Check attribute with lookup const lookupAttr = metaData.createEntityAttr(metaData.rootEntity); lookupAttr.id = 'lookupAttr'; lookupAttr.description = 'Lookup description'; @@ -248,16 +248,16 @@ describe('MetaData', () => { const attrWithLookup = metaData.createEntityAttr(metaData.rootEntity); attrWithLookup.id = 'attrWithLookup'; attrWithLookup.lookupAttr = 'lookupAttr'; - attrWithLookup.description = ''; // Пустое описание + attrWithLookup.description = ''; // Empty description metaData.rootEntity.attributes.push(attrWithLookup); - - // Check свойство через lookup + + // Check property via lookup const hasDescriptionViaLookup = metaData.checkAttrProperty('attrWithLookup', 'description'); expect(hasDescriptionViaLookup).toBe(true); }); - it('should очищать модель данных', () => { - // Заполняем модель + it('should clear data model', () => { + // Fill model metaData.id = 'test-model'; metaData.name = 'Test Model'; metaData.version = '1.0.0'; @@ -265,55 +265,55 @@ describe('MetaData', () => { const editor = metaData.createValueEditor(); editor.id = 'editor1'; metaData.editors = [editor]; - - // Очищаем модель + + // Clear model metaData.clear(); - - // Check результат + + // Check result expect(metaData.rootEntity).toBeDefined(); expect(metaData.editors).toBeArray(); expect(metaData.editors).toBeEmpty(); expect(metaData.version).toBe(''); }); - it('should добавлять редакторы значений по умолчанию', () => { + it('should add default value editors', () => { metaData.addDefaultValueEditors(); expect(metaData.editors.length).toBe(3); - - // Check первый редактор + + // Check first editor const stringEditor = metaData.getEditorById('_DTE'); expect(stringEditor).not.toBeNull(); expect(stringEditor.tag).toBe(EditorTag.Edit); expect(stringEditor.resType).toBe(DataType.String); expect(stringEditor.defValue).toBe(''); - - // Check остальные редакторы + + // Check other editors expect(metaData.getEditorById('_DPDE')).not.toBeNull(); expect(metaData.getEditorById('_DPTE')).not.toBeNull(); }); - it('should добавлять или обновлять редактор значений', () => { - // Добавляем новый редактор + it('should add or update value editor', () => { + // Add new editor const newEditor = metaData.addOrUpdateValueEditor('testEditor', EditorTag.Edit, DataType.String); expect(metaData.editors.length).toBe(1); expect(newEditor.id).toBe('testEditor'); expect(newEditor.tag).toBe(EditorTag.Edit); expect(newEditor.resType).toBe(DataType.String); - - // Обновляем существующий редактор + + // Refresh existing editor const updatedEditor = metaData.addOrUpdateValueEditor('testEditor', EditorTag.DateTime, DataType.DateTime); - - expect(metaData.editors.length).toBe(1); // Количество редакторов не изменилось - expect(updatedEditor).toBe(newEditor); // Тот же экземпляр объекта + + expect(metaData.editors.length).toBe(1); // Number of editors did not change + expect(updatedEditor).toBe(newEditor); // Same object instance expect(updatedEditor.tag).toBe(EditorTag.DateTime); expect(updatedEditor.resType).toBe(DataType.DateTime); }); - it('should получать дерево сущностей с фильтрацией', () => { - // Подготовка данных для теста - // Создаем иерархию сущностей + it('should get entities tree with filtering', () => { + // Prepare test data + // Create entity hierarchy const subEntity1 = metaData.createEntity(metaData.rootEntity); subEntity1.name = 'Entity1'; subEntity1.caption = 'TestEntity'; @@ -335,20 +335,20 @@ describe('MetaData', () => { attr2.dataType = DataType.Int32; subEntity2.attributes.push(attr2); - // Get дерево сущностей с фильтром по типу данных + // Get entities tree with filtering const filterFunc = (entity: MetaEntity, attr: MetaEntityAttr) => { return !attr || attr.dataType === DataType.Int32; }; const tree = metaData.getEntitiesTreeWithFilter(filterFunc); - - // Check результат - expect(tree.length).toBe(1); // Должна быть только одна сущность + + // Check result + expect(tree.length).toBe(1); // Should be only one entity expect(tree[0].id).toBe('Entity2'); }); - it('should получать дерево сущностей с опциями', () => { - // Подготовка данных для теста + it('should get entities tree with options', () => { + // Prepare test data const subEntity1 = metaData.createEntity(metaData.rootEntity); subEntity1.name = 'Entity1'; subEntity1.caption = 'Entity 1'; @@ -358,24 +358,24 @@ describe('MetaData', () => { attr1.id = 'Attr1'; attr1.caption = 'Attribute 1'; subEntity1.attributes.push(attr1); - - // Get дерево сущностей с опциями + + // Get the entities tree with options const opts = { includeRootData: true, sortEntities: true, - attrPlacement: 1 // Атрибуты перед сущностями + attrPlacement: 1 // Attributes before entities }; const tree = metaData.getEntitiesTree(opts); - // Check результат + // Check result expect(tree.id).toBe('Root'); - expect(tree.items.length).toBe(1); // Одна дочерняя сущность + expect(tree.items.length).toBe(1); // Should be only one child entity }); - it('should получать путь сущности по атрибуту', () => { - // Подготовка данных для теста - // Создаем иерархию сущностей и атрибутов + it('should get entity path by attribute', () => { + // Prepare test data + // Create entity hierarchy and attributes const subEntity1 = metaData.createEntity(metaData.rootEntity); subEntity1.name = 'Entity1'; subEntity1.caption = 'Entity 1'; @@ -391,15 +391,15 @@ describe('MetaData', () => { attr.caption = 'Deep Attribute'; subEntity2.attributes.push(attr); - // Get путь + // Get path const path = metaData.getFullEntityPathByAttr('DeepAttribute', ' > '); - - // Check результат + + // Check result expect(path).toBe('Entity 1 > Entity 2'); }); - it('should получать текст атрибута', () => { - // Подготовка данных для теста + it('should get attribute text', () => { + // Prepare test data const subEntity = metaData.createEntity(metaData.rootEntity); subEntity.name = 'TestEntity'; subEntity.caption = 'Test Entity'; @@ -409,18 +409,18 @@ describe('MetaData', () => { attr.id = 'TestAttribute'; attr.caption = 'Original Caption'; subEntity.attributes.push(attr); - - // Get текст атрибута без формата + + // Get attribute text without format const simpleText = metaData.getAttributeText(attr, ''); - expect(simpleText).toBe('Тестовый Атрибут'); // Берем из i18n - - // Get текст атрибута с форматом + expect(simpleText).toBe('Test Attribute'); // Take from i18n + + // Get attribute text with format const formattedText = metaData.getAttributeText(attr, '{entity}: {attr}'); - expect(formattedText).toBe('Тестовая Сущность: Тестовый Атрибут'); + expect(formattedText).toBe('Test Entity: Test Attribute'); }); - it('should сканировать сущности и их атрибуты', () => { - // Подготовка данных для теста + it('should scan entities and their attributes', () => { + // Prepare test data const subEntity1 = metaData.createEntity(metaData.rootEntity); subEntity1.name = 'Entity1'; metaData.rootEntity.subEntities.push(subEntity1); @@ -436,8 +436,8 @@ describe('MetaData', () => { const subEntity2 = metaData.createEntity(metaData.rootEntity); subEntity2.name = 'Entity2'; metaData.rootEntity.subEntities.push(subEntity2); - - // Подсчитываем количество атрибутов и сущностей + + // Count attributes and entities let attrCount = 0; let entityCount = 0; @@ -445,14 +445,14 @@ describe('MetaData', () => { () => { attrCount++; }, () => { entityCount++; } ); - - // Check результат: 2 атрибута и 3 сущности (корневая + 2 дочерние) + + // Check result: 2 attributes and 3 entities (root + 2 children) expect(attrCount).toBe(2); expect(entityCount).toBe(3); }); - it('should находить первый атрибут по фильтру', () => { - // Подготовка данных для теста + it('should find the first attribute by filter', () => { + // Prepare test data const entity1 = metaData.createEntity(metaData.rootEntity); entity1.name = 'Entity1'; metaData.rootEntity.subEntities.push(entity1); @@ -466,19 +466,19 @@ describe('MetaData', () => { attr2.id = 'Attr2'; attr2.dataType = DataType.Int32; entity1.attributes.push(attr2); - - // Поиск атрибута типа Int32 + + // Find attribute of type Int32 const intAttr = metaData.getFirstAttributeByFilter( (attr) => attr.dataType === DataType.Int32 ); - - // Check результат + + // Check result expect(intAttr).not.toBeNull(); expect(intAttr.id).toBe('Attr2'); }); - it('should устанавливать данные используя setData', () => { - // Через JSON-строку + it('should set data using setData', () => { + // Through JSON string const jsonData = JSON.stringify({ id: 'test-json', name: 'Test JSON', @@ -488,8 +488,8 @@ describe('MetaData', () => { metaData.setData(jsonData); expect(metaData.id).toBe('test-json'); expect(metaData.name).toBe('Test JSON'); - - // Через объект + + // Creating object const objData = { id: 'test-obj', name: 'Test Object', diff --git a/easydata.js/packs/core/tests/meta_entity.test.ts b/easydata.js/packs/core/tests/meta_entity.test.ts index 86b51487..ca6df022 100644 --- a/easydata.js/packs/core/tests/meta_entity.test.ts +++ b/easydata.js/packs/core/tests/meta_entity.test.ts @@ -17,7 +17,7 @@ describe('MetaEntity', () => { } as unknown as MetaData; }); - it('should initialize with correct values по умолчанию', () => { + it('should initialize with correct default values', () => { const entity = new MetaEntity(); expect(entity.name).toBe(''); @@ -31,14 +31,14 @@ describe('MetaEntity', () => { expect(entity.parent).toBeUndefined(); }); - it('should принимать родительскую сущность в конструкторе', () => { + it('should accept parent entity in constructor', () => { const parent = new MetaEntity(); const child = new MetaEntity(parent); expect(child.parent).toBe(parent); }); - it('should загружать данные из DTO', () => { + it('should load data from DTO', () => { const entity = new MetaEntity(); const dto: MetaEntityDTO = { id: 'test-entity', @@ -60,7 +60,7 @@ describe('MetaEntity', () => { expect(entity.isEditable).toBe(false); }); - it('should загружать дочерние сущности из DTO', () => { + it('should load child entities from DTO', () => { const entity = new MetaEntity(); const dto: MetaEntityDTO = { id: 'parent', @@ -94,7 +94,7 @@ describe('MetaEntity', () => { expect(entity.subEntities[1].parent).toBe(entity); }); - it('should загружать атрибуты из DTO', () => { + it('should load attributes from DTO', () => { const entity = new MetaEntity(); const dto: MetaEntityDTO = { id: 'test-entity', @@ -133,8 +133,8 @@ describe('MetaEntity', () => { expect(entity.attributes[1].isPrimaryKey).toBe(false); }); - it('should сканировать сущность with attributes и подсущностями', () => { - // Настройка сложной иерархии сущностей + it('should scan entity with attributes and sub-entities', () => { + // Setting up complex entity hierarchy const rootEntity = new MetaEntity(); rootEntity.id = 'root'; @@ -150,15 +150,15 @@ describe('MetaEntity', () => { attr2.id = 'attr2'; childEntity.attributes.push(attr2); - // Счетчики для проверки вызовов + // Counters for verification of calls let entityCount = 0; let attrCount = 0; - // Сканирование + // Scanning rootEntity.scan( (attr) => { attrCount++; - // Прерываем сканирование после первого атрибута для теста + // Stop scanning after the first attribute for testing if (attr.id === 'attr1') { return; } @@ -190,7 +190,7 @@ describe('MetaEntity', () => { expect(attrCount).toBe(2); // attr1 и attr2 }); - it('should находить первичные атрибуты', () => { + it('should find primary attributes', () => { const entity = new MetaEntity(); // Create attributes @@ -221,7 +221,7 @@ describe('MetaEntity', () => { expect(firstPrimaryAttr!.id).toBe('id'); }); - it('should return null из getFirstPrimaryAttr если первичных ключей нет', () => { + it('should return null from getFirstPrimaryAttr if there are no primary keys', () => { const entity = new MetaEntity(); const attr = new MetaEntityAttr(entity); @@ -250,7 +250,7 @@ describe('MetaEntityAttr', () => { } as unknown as MetaData; }); - it('should initialize with correct values по умолчанию', () => { + it('should initialize with correct default values', () => { const attr = new MetaEntityAttr(mockEntity); expect(attr.id).toBe(''); @@ -269,7 +269,7 @@ describe('MetaEntityAttr', () => { expect(attr.entity).toBe(mockEntity); }); - it('should загружать данные из DTO', () => { + it('should load data from DTO', () => { const attr = new MetaEntityAttr(mockEntity); const dto: MetaEntityAttrDTO = { id: 'test-attr', @@ -309,10 +309,10 @@ describe('MetaEntityAttr', () => { expect(attr.userData).toBe('{"custom":"data"}'); }); - it('should правильно обрабатывать даты в defaultValue', () => { + it('should correctly handle dates in defaultValue', () => { const attr = new MetaEntityAttr(mockEntity); - // Check Date в defaultValue + // Check Date in defaultValue const dateDto: MetaEntityAttrDTO = { id: 'date-attr', cptn: 'Date Field', @@ -326,11 +326,11 @@ describe('MetaEntityAttr', () => { expect(attr.defaultValue).toBeInstanceOf(Date); expect(attr.defaultValue.getFullYear()).toBe(2022); - expect(attr.defaultValue.getMonth()).toBe(0); // январь = 0 + expect(attr.defaultValue.getMonth()).toBe(0); // January = 0 expect(attr.defaultValue.getDate()).toBe(15); }); - it('should correctly handle lookup атрибуты', () => { + it('should correctly handle lookup attributes', () => { const attr = new MetaEntityAttr(mockEntity); const dto: MetaEntityAttrDTO = { id: 'fk-attr', @@ -352,7 +352,7 @@ describe('MetaEntityAttr', () => { expect(attr.lookupDataAttr).toBe('lookup-data-attr'); }); - it('should устанавливать редактор по умолчанию', () => { + it('should set default editor', () => { const editor = { id: 'test-editor' }; mockMetaData = { @@ -375,10 +375,10 @@ describe('MetaEntityAttr', () => { expect(attr.defaultEditor).toBe(editor); }); - it('should использовать старый формат ivis как синоним для sov', () => { + it('should use old ivis format as synonym for sov', () => { const attr = new MetaEntityAttr(mockEntity); - // Используем старый формат ivis + // Use old ivis format const dtoOld: MetaEntityAttrDTO = { id: 'old-attr', cptn: 'Old Attr', @@ -391,7 +391,7 @@ describe('MetaEntityAttr', () => { attr.loadFromData(mockMetaData, dtoOld); expect(attr.showOnView).toBe(false); - // Используем новый формат sov + // Use new sov format const dtoNew: MetaEntityAttrDTO = { id: 'new-attr', cptn: 'New Attr', diff --git a/easydata.js/packs/core/tests/string_utils.test.ts b/easydata.js/packs/core/tests/string_utils.test.ts index de6229e5..05ded1fe 100644 --- a/easydata.js/packs/core/tests/string_utils.test.ts +++ b/easydata.js/packs/core/tests/string_utils.test.ts @@ -1,14 +1,14 @@ import { repeatString, reverseString, strEndsWith, combinePath } from '../src/utils/string_utils'; describe('String Utils', () => { - it('should правильно повторять строку указанное количество раз', () => { + it('should correctly repeat string specified number of times', () => { expect(repeatString('a', 3)).toBe('aaa'); expect(repeatString('abc', 2)).toBe('abcabc'); expect(repeatString('x', 0)).toBe(''); expect(repeatString('', 5)).toBe(''); }); - it('should правильно переворачивать строку', () => { + it('should correctly reverse string', () => { expect(reverseString('abc')).toBe('cba'); expect(reverseString('hello')).toBe('olleh'); expect(reverseString('')).toBe(''); @@ -16,7 +16,7 @@ describe('String Utils', () => { expect(reverseString('12345')).toBe('54321'); }); - it('should правильно проверять, заканчивается ли строка указанным символом', () => { + it('should correctly check if string ends with specified character', () => { expect(strEndsWith('test.js', '.js')).toBe(true); expect(strEndsWith('hello world', 'world')).toBe(true); expect(strEndsWith('file.txt', '.doc')).toBe(false); @@ -25,12 +25,12 @@ describe('String Utils', () => { expect(strEndsWith('abc', '')).toBe(true); }); - it('should correctly handle случай, когда strEndsWith получает null или undefined', () => { + it('should correctly handle case when strEndsWith receives null or undefined', () => { expect(strEndsWith(null as any, '.js')).toBe(false); expect(strEndsWith(undefined as any, '.txt')).toBe(false); }); - it('should correctly объединять пути', () => { + it('should correctly combine paths', () => { expect(combinePath('path/to', 'file')).toBe('path/to/file'); expect(combinePath('path/to/', 'file')).toBe('path/to/file'); expect(combinePath('', 'file')).toBe('file'); @@ -38,7 +38,7 @@ describe('String Utils', () => { expect(combinePath('path', '')).toBe('path/'); }); - it('should correctly handle различные сценарии объединения путей', () => { + it('should correctly handle various path combination scenarios', () => { expect(combinePath('root', 'path/to/file')).toBe('root/path/to/file'); expect(combinePath('/api', '/data')).toBe('/api/data'); expect(combinePath('http://example.com', 'api')).toBe('http://example.com/api'); diff --git a/easydata.js/packs/core/tests/time_utils.test.ts b/easydata.js/packs/core/tests/time_utils.test.ts index 5e274d89..48738959 100644 --- a/easydata.js/packs/core/tests/time_utils.test.ts +++ b/easydata.js/packs/core/tests/time_utils.test.ts @@ -1,18 +1,18 @@ import { TimeValue, TimeSettings, SpecialDatesResolver, registerSpecialDatesResolver } from '../src/types/time_utils'; describe('Time Utils', () => { - // Mock for даты, чтобы тесты не зависели от текущего времени + // Mock for dates, so tests don't depend on current time let originalDate: DateConstructor; let fixedDate: Date; beforeEach(() => { - // Save оригинальный конструктор Date + // Save original Date constructor originalDate = global.Date; - // Set фиксированную дату для тестов: 15 мая 2023, 10:30:00 + // Set fixed date for tests: May 15, 2023, 10:30:00 fixedDate = new Date(2023, 4, 15, 10, 30, 0); - // Мокаем конструктор Date + // Mock Date constructor global.Date = class extends Date { constructor(...args: any[]) { if (args.length === 0) { @@ -24,11 +24,11 @@ describe('Time Utils', () => { }); afterEach(() => { - // Restore оригинальный Date + // Restore original Date global.Date = originalDate; }); - it('should создавать TimeValue с датой', () => { + it('should create TimeValue with date', () => { const date = new Date(2023, 0, 15); const timeValue = new TimeValue(date); @@ -36,14 +36,14 @@ describe('Time Utils', () => { expect(timeValue['_name']).toBeUndefined(); }); - it('should создавать TimeValue со строковым именем', () => { + it('should create TimeValue with string name', () => { const timeValue = new TimeValue('Today'); expect(timeValue['date']).toBeUndefined(); expect(timeValue['_name']).toBe('Today'); }); - it('should return дату через asTime', () => { + it('should return date via asTime', () => { const date = new Date(2023, 0, 15); const timeValue = new TimeValue(date); @@ -51,27 +51,27 @@ describe('Time Utils', () => { expect(result).toBe(date); }); - it('should return специальную дату через asTime', () => { + it('should return special date via asTime', () => { const timeValue = new TimeValue('Today'); expect(timeValue.asTime()).toBeInstanceOf(Date); }); - it('should иметь getter для name', () => { + it('should have getter for name', () => { const timeValue = new TimeValue('Today'); expect(timeValue.name).toBe('Today'); }); - it('should разрешать специальную дату Today', () => { + it('should resolve special date Today', () => { const resolver = new SpecialDatesResolver(); const result = resolver.Today(); expect(result).toBeInstanceOf(Date); expect(result.getFullYear()).toBe(2023); - expect(result.getMonth()).toBe(4); // май (0-индексированные месяцы) + expect(result.getMonth()).toBe(4); // May (0-indexed months) expect(result.getDate()).toBe(15); }); - it('should разрешать специальную дату Yesterday', () => { + it('should resolve special date Yesterday', () => { const resolver = new SpecialDatesResolver(); const result = resolver.Yesterday(); @@ -81,7 +81,7 @@ describe('Time Utils', () => { expect(result.getDate()).toBe(14); // 15 - 1 = 14 }); - it('should разрешать специальную дату Tomorrow', () => { + it('should resolve special date Tomorrow', () => { const resolver = new SpecialDatesResolver(); const result = resolver.Tomorrow(); @@ -91,7 +91,7 @@ describe('Time Utils', () => { expect(result.getDate()).toBe(16); // 15 + 1 = 16 }); - it('should разрешать специальную дату FirstDayOfMonth', () => { + it('should resolve special date FirstDayOfMonth', () => { const resolver = new SpecialDatesResolver(); const result = resolver.FirstDayOfMonth(); @@ -101,47 +101,47 @@ describe('Time Utils', () => { expect(result.getDate()).toBe(1); }); - it('should разрешать специальную дату LastDayOfMonth', () => { + it('should resolve special date LastDayOfMonth', () => { const resolver = new SpecialDatesResolver(); const result = resolver.LastDayOfMonth(); expect(result).toBeInstanceOf(Date); expect(result.getFullYear()).toBe(2023); expect(result.getMonth()).toBe(4); - expect(result.getDate()).toBe(31); // май имеет 31 день + expect(result.getDate()).toBe(31); // May has 31 days }); - it('should разрешать специальную дату FirstDayOfNextMonth', () => { + it('should resolve special date FirstDayOfNextMonth', () => { const resolver = new SpecialDatesResolver(); const result = resolver.FirstDayOfNextMonth(); expect(result).toBeInstanceOf(Date); expect(result.getFullYear()).toBe(2023); - expect(result.getMonth()).toBe(5); // июнь + expect(result.getMonth()).toBe(5); // June expect(result.getDate()).toBe(1); }); - it('should разрешать специальную дату FirstDayOfPrevMonth', () => { + it('should resolve special date FirstDayOfPrevMonth', () => { const resolver = new SpecialDatesResolver(); const result = resolver.FirstDayOfPrevMonth(); expect(result).toBeInstanceOf(Date); expect(result.getFullYear()).toBe(2023); - expect(result.getMonth()).toBe(3); // апрель + expect(result.getMonth()).toBe(3); // April expect(result.getDate()).toBe(1); }); - it('should разрешать специальную дату FirstDayOfYear', () => { + it('should resolve special date FirstDayOfYear', () => { const resolver = new SpecialDatesResolver(); const result = resolver.FirstDayOfYear(); expect(result).toBeInstanceOf(Date); expect(result.getFullYear()).toBe(2023); - expect(result.getMonth()).toBe(0); // январь + expect(result.getMonth()).toBe(0); // January expect(result.getDate()).toBe(1); }); - it('should разрешать специальную дату FirstDayOfPrevYear', () => { + it('should resolve special date FirstDayOfPrevYear', () => { const resolver = new SpecialDatesResolver(); const result = resolver.FirstDayOfPrevYear(); @@ -151,7 +151,7 @@ describe('Time Utils', () => { expect(result.getDate()).toBe(1); }); - it('should разрешать специальную дату FirstDayOfNextYear', () => { + it('should resolve special date FirstDayOfNextYear', () => { const resolver = new SpecialDatesResolver(); const result = resolver.FirstDayOfNextYear(); diff --git a/easydata.js/packs/core/tests/utils.test.ts b/easydata.js/packs/core/tests/utils.test.ts index 42802ee3..aa80c1fe 100644 --- a/easydata.js/packs/core/tests/utils.test.ts +++ b/easydata.js/packs/core/tests/utils.test.ts @@ -267,7 +267,7 @@ describe('utils', () => { expect(array).toBeArrayEqual(['a', 'c', 'd', 'e', 'b']); }); - it('should удалять элемент из массива через removeArrayItem', () => { + it('should remove element from array via removeArrayItem', () => { const array = ['a', 'b', 'c', 'd']; const removed = utils.removeArrayItem(array, 'b'); diff --git a/easydata.js/packs/core/tests/value_editor.test.ts b/easydata.js/packs/core/tests/value_editor.test.ts index 70700007..2633edc1 100644 --- a/easydata.js/packs/core/tests/value_editor.test.ts +++ b/easydata.js/packs/core/tests/value_editor.test.ts @@ -138,7 +138,7 @@ describe('ValueEditor', () => { expect(result2).toBe(''); }); - it('should загружать данные даже если DTO неполное', () => { + it('should load data even if DTO is incomplete', () => { const editor = new ValueEditor(); const incompleteDTO = { id: 'test-editor' @@ -147,24 +147,24 @@ describe('ValueEditor', () => { editor.loadFromData(incompleteDTO); expect(editor.id).toBe('test-editor'); - // Остальные свойства должны остаться со значениями по умолчанию + // Other properties should remain with default values expect(editor.tag).toBe(EditorTag.Unknown); expect(editor.resType).toBe(DataType.Unknown); expect(editor.defValue).toBe(''); }); - it('should не менять текущие значения если DTO равно null или undefined', () => { + it('should not change current values if DTO is null or undefined', () => { const editor = new ValueEditor(); editor.id = 'existing-id'; editor.tag = EditorTag.Edit; - // Передаем undefined + // Pass undefined editor.loadFromData(undefined as any); expect(editor.id).toBe('existing-id'); expect(editor.tag).toBe(EditorTag.Edit); - // Передаем null + // Pass null editor.loadFromData(null as any); expect(editor.id).toBe('existing-id'); From b0143ba2bad04b7c83d8b663c15f61f28e13bef7 Mon Sep 17 00:00:00 2001 From: korzh Date: Sat, 19 Jul 2025 16:04:35 +0300 Subject: [PATCH 05/11] Update test texts part 3 --- .../packs/crud/tests/data_context.test.ts | 50 +++++++++---------- .../packs/crud/tests/entity_data_view.test.ts | 4 +- .../crud/tests/entity_form_builder.test.ts | 38 +++++++------- easydata.js/packs/crud/tests/utils.test.ts | 32 ++++++------ .../packs/crud/tests/validators.test.ts | 44 ++++++++-------- 5 files changed, 84 insertions(+), 84 deletions(-) diff --git a/easydata.js/packs/crud/tests/data_context.test.ts b/easydata.js/packs/crud/tests/data_context.test.ts index ae4d6b04..2b19d88b 100644 --- a/easydata.js/packs/crud/tests/data_context.test.ts +++ b/easydata.js/packs/crud/tests/data_context.test.ts @@ -102,7 +102,7 @@ describe('DataContext', () => { // Set active entity dataContext.setActiveSource('entity1'); - // Check, что активная сущность установлена + // Check that active entity is set const activeEntity = dataContext.getActiveEntity(); expect(activeEntity).toBeDefined(); expect(activeEntity.id).toBe('entity1'); @@ -110,42 +110,42 @@ describe('DataContext', () => { }); it('should set custom endpoints', () => { - // Set кастомный эндпоинт + // Set custom endpoint dataContext.setEndpoint('GetMetaData', '/custom/api/metadata'); - // Check что эндпоинт установлен + // Check that endpoint is set const endpoint = dataContext.resolveEndpoint('GetMetaData'); expect(endpoint).toBe('/custom/api/metadata'); }); it('should not overwrite existing endpoints when using setEndpointIfNotExist', () => { - // Set эндпоинт + // Set endpoint dataContext.setEndpoint('GetMetaData', '/custom/api/metadata'); - // Пытаемся установить другой эндпоинт через setEnpointIfNotExist + // Try to set another endpoint via setEnpointIfNotExist dataContext.setEnpointIfNotExist('GetMetaData', '/another/endpoint'); - // Check что эндпоинт не изменился + // Check that endpoint hasn't changed const endpoint = dataContext.resolveEndpoint('GetMetaData'); expect(endpoint).toBe('/custom/api/metadata'); }); it('should resolve endpoints with parameters', () => { - // Set эндпоинт с параметрами + // Set endpoint with parameters dataContext.setEndpoint('CustomEndpoint', '/api/custom/{param1}/{param2}'); - // Разрешаем эндпоинт с параметрами + // Resolve endpoint with parameters const endpoint = dataContext.resolveEndpoint('CustomEndpoint', { param1: 'value1', param2: 'value2' }); - // Check что параметры подставлены + // Check that parameters are substituted expect(endpoint).toBe('/api/custom/value1/value2'); }); it('should throw error when required parameter is missing', () => { - // Set эндпоинт с параметрами + // Set endpoint with parameters dataContext.setEndpoint('CustomEndpoint', '/api/custom/{param1}/{param2}'); - // Пытаемся разрешить эндпоинт с отсутствующим параметром + // Try to resolve endpoint with missing parameter expect(() => { dataContext.resolveEndpoint('CustomEndpoint', { param1: 'value1' }); }).toThrow('Parameter [param2] is not defined'); @@ -154,16 +154,16 @@ describe('DataContext', () => { it('should load metadata', () => { const promise = dataContext.loadMetaData(); - // Check что загрузка вызвала правильный метод HTTP клиента + // Check that loading called the correct HTTP client method expect(mockHttpClient.get).toHaveBeenCalled(); expect((mockHttpClient.get as jest.Mock).mock.calls[0][0]).toContain('/api/test/models/test-model'); return promise.then((model) => { - // Check что модель была загружена + // Check that model was loaded expect(model).toBeDefined(); expect(model.id).toBe('test-model'); - // Check что были вызваны методы startProcess и endProcess + // Check that startProcess and endProcess methods were called expect(processStartCount).toBe(1); expect(processEndCount).toBe(1); }); @@ -175,10 +175,10 @@ describe('DataContext', () => { .then(() => { dataContext.setActiveSource('entity1'); - // Создаем фильтр + // Create filter const filter = dataContext.createFilter(); - // Check созданный фильтр + // Check created filter expect(filter).toBeInstanceOf(TextDataFilter); }); }); @@ -203,16 +203,16 @@ describe('DataContext', () => { .then(() => { dataContext.setActiveSource('entity1'); - // Загружаем датасет + // Load dataset return dataContext.fetchDataset(); }) .then((data) => { - // Check результат + // Check result expect(data).toBeDefined(); expect(data.columns.count).toBe(2); expect(data.getCachedCount()).toBe(1); - // Check что dataLoader.loadChunk был вызван + // Check that dataLoader.loadChunk was called expect(dataLoader.loadChunk).toHaveBeenCalled(); }); }); @@ -223,11 +223,11 @@ describe('DataContext', () => { .then(() => { dataContext.setActiveSource('entity1'); - // Get запись + // Get record return dataContext.fetchRecord({ id: 1 }); }) .then(() => { - // Check что был вызван правильный метод HTTP клиента + // Check that correct HTTP client method was called expect(mockHttpClient.get).toHaveBeenCalled(); const lastCall = (mockHttpClient.get).mock.calls.pop(); @@ -235,7 +235,7 @@ describe('DataContext', () => { expect(lastCall[1].queryParams).toBeDefined(); expect(lastCall[1].queryParams.id).toBe(1); - // Check что были вызваны методы startProcess и endProcess + // Check that startProcess and endProcess methods were called expect(processStartCount).toBeGreaterThan(0); expect(processEndCount).toBeGreaterThan(0); }); @@ -250,7 +250,7 @@ describe('DataContext', () => { // Test object for creation const testObj = { name: 'Test Record', value: 123 }; - // Создаем запись + // Create record return dataContext.createRecord(testObj); }) .then(() => { @@ -278,7 +278,7 @@ describe('DataContext', () => { // Test object for update const testObj = { id: 1, name: 'Updated Record', value: 456 }; - // Обновляем запись + // Update record return dataContext.updateRecord(testObj); }) .then(() => { @@ -306,7 +306,7 @@ describe('DataContext', () => { // Test object for deletion const testObj = { id: 1 }; - // Remove запись + // Remove record return dataContext.deleteRecord(testObj); }) .then(() => { diff --git a/easydata.js/packs/crud/tests/entity_data_view.test.ts b/easydata.js/packs/crud/tests/entity_data_view.test.ts index 866a1d8f..1cdbad57 100644 --- a/easydata.js/packs/crud/tests/entity_data_view.test.ts +++ b/easydata.js/packs/crud/tests/entity_data_view.test.ts @@ -186,10 +186,10 @@ describe('EntityDataView', () => { it('should render title and back button', () => { view = new EntityDataView(mockSlot, mockContext, '/basePath', {}); - // Check, что заголовок содержит название сущности + // Check that title contains entity name expect(mockSlot.innerHTML).toContain('

Entities

'); - // Check наличие кнопки возврата + // Check presence of back button const backLink = mockSlot.querySelector('a'); expect(backLink).toBeDefined(); expect(backLink.textContent).toBe('← Back to Entities'); diff --git a/easydata.js/packs/crud/tests/entity_form_builder.test.ts b/easydata.js/packs/crud/tests/entity_form_builder.test.ts index 5ea655be..ecfec438 100644 --- a/easydata.js/packs/crud/tests/entity_form_builder.test.ts +++ b/easydata.js/packs/crud/tests/entity_form_builder.test.ts @@ -15,7 +15,7 @@ describe('EntityEditFormBuilder', () => { let builder: EntityEditFormBuilder; let dataRow: DataRow; - // Helper function для создания атрибута + // Helper function for creating attribute const createAttr = (id: string, caption: string, dataType: DataType, options: any = {}): MetaEntityAttr => { const attr = { id, @@ -162,7 +162,7 @@ describe('EntityEditFormBuilder', () => { builder = new EntityEditFormBuilder(mockDataContext); }); - it('should создаваться с пустыми параметрами и сбрасываться на значения по умолчанию', () => { + it('should be created with empty parameters and reset to default values', () => { expect(builder).toBeDefined(); // Check, что свойство context установлено @@ -177,7 +177,7 @@ describe('EntityEditFormBuilder', () => { expect(formAfterReset).toBeInstanceOf(EntityEditForm); }); - it('should создавать форму с правильными полями', () => { + it('should create form with correct fields', () => { const form = builder.build(); // Check что форма создана @@ -233,7 +233,7 @@ describe('EntityEditFormBuilder', () => { expect(isActiveInput.checked).toBe(true); }); - it('should создавать форму редактирования с правильными параметрами', () => { + it('should create edit form with correct parameters', () => { const params: FormBuildParams = { values: dataRow, isEditForm: true @@ -243,7 +243,7 @@ describe('EntityEditFormBuilder', () => { const form = builder.build(); const formHtml = form.getHtml(); - // Check, что первичные ключи имеют атрибут readonly + // Check that primary keys have readonly attribute const idInput = formHtml.querySelector('[name="Person.id"]') as HTMLInputElement; expect(idInput).toBeDefined(); expect(idInput.getAttribute('readonly')).toBeDefined(); @@ -292,16 +292,16 @@ describe('EntityEditFormBuilder', () => { expect(notesTextarea.tagName.toLowerCase()).toBe('textarea'); }); - it('should создавать select для списков', () => { + it('should create select for lists', () => { const form = builder.build(); const formHtml = form.getHtml(); - // Check select для списков + // Check select for lists const statusSelect = formHtml.querySelector('[name="Person.status"]') as HTMLSelectElement; expect(statusSelect).toBeDefined(); expect(statusSelect.tagName.toLowerCase()).toBe('select'); - // Check опции + // Check options const options = statusSelect.querySelectorAll('option'); expect(options.length).toBe(2); expect(options[0].value).toBe('Active'); @@ -325,21 +325,21 @@ describe('EntityEditFormBuilder', () => { expect(calendarIcon).toBeDefined(); }); - it('should создавать поля для lookup атрибутов', () => { + it('should create fields for lookup attributes', () => { const form = builder.build(); const formHtml = form.getHtml(); - // Check наличие поля для lookup + // Check presence of lookup field const departmentField = formHtml.querySelector('[name="Person.departmentId"]').closest('.kfrm-fields, .kfrm-fields-ie'); expect(departmentField).toBeDefined(); - // Check наличие кнопки для открытия lookup диалога + // Check presence of button for opening lookup dialog const lookupButton = departmentField.querySelector('button'); expect(lookupButton).toBeDefined(); expect(lookupButton.textContent).toBe('...'); }); - it('should добавлять информацию о подсказках для полей с описанием', () => { + it('should add tooltip information for fields with description', () => { const form = builder.build(); const formHtml = form.getHtml(); @@ -352,25 +352,25 @@ describe('EntityEditFormBuilder', () => { expect(helpIcon.getAttribute('title')).toBe('Person full name'); }); - it('should устанавливать обработчик submit и вызывать его при нажатии Enter', () => { - // Создаем мок-функцию для onSubmit + it('should set submit handler and call it when Enter is pressed', () => { + // Create mock function for onSubmit const submitCallback = mock(); - // Set callback через onSubmit + // Set callback via onSubmit builder.onSubmit(submitCallback); - // Строим форму + // Build form const form = builder.build(); const formHtml = form.getHtml(); - // Get поле ввода + // Get input field const nameInput = formHtml.querySelector('[name="Person.name"]') as HTMLInputElement; - // Emulate нажатие клавиши Enter + // Emulate Enter key press const enterKeyEvent = new KeyboardEvent('keypress', { keyCode: 13 }); nameInput.dispatchEvent(enterKeyEvent); - // Check, что callback был вызван + // Check that callback was called expect(submitCallback).toHaveBeenCalled(); }); }); diff --git a/easydata.js/packs/crud/tests/utils.test.ts b/easydata.js/packs/crud/tests/utils.test.ts index 7efacd97..3c49a415 100644 --- a/easydata.js/packs/crud/tests/utils.test.ts +++ b/easydata.js/packs/crud/tests/utils.test.ts @@ -33,61 +33,61 @@ describe('Utils', () => { window.dispatchEvent = originalDispatchEvent; }); - it('should return правильный внутренний формат для Date', () => { + it('should return correct internal format for Date', () => { const format = getInternalDateTimeFormat(DataType.Date); expect(format).toBe('yyyy-MM-dd'); }); - it('should return правильный внутренний формат для Time', () => { + it('should return correct internal format for Time', () => { const format = getInternalDateTimeFormat(DataType.Time); expect(format).toBe('HH:mm'); }); - it('should return правильный внутренний формат для DateTime', () => { + it('should return correct internal format for DateTime', () => { const format = getInternalDateTimeFormat(DataType.DateTime); expect(format).toBe('yyyy-MM-ddTHH:mm'); }); - it('should return формат редактирования из настроек локали для Date', () => { + it('should return edit format from locale settings for Date', () => { const format = getEditDateTimeFormat(DataType.Date); expect(format).toBe('dd.MM.yyyy'); }); - it('should return формат редактирования из настроек локали для Time', () => { + it('should return edit format from locale settings for Time', () => { const format = getEditDateTimeFormat(DataType.Time); expect(format).toBe('HH:mm'); }); - it('should return формат редактирования из настроек локали для DateTime', () => { + it('should return edit format from locale settings for DateTime', () => { const format = getEditDateTimeFormat(DataType.DateTime); expect(format).toBe('dd.MM.yyyy HH:mm'); }); - it('should изменять локацию через pushState и генерировать событие', () => { + it('should change location via pushState and generate event', () => { // Set initial state value const mockState = { test: 'state' }; window.history.state = mockState; document.title = 'Test Title'; - // Call функцию + // Call function setLocation('/new-path'); - // Check, что pushState был вызван с правильными аргументами + // Check that pushState was called with correct arguments expect(window.history.pushState).toHaveBeenCalledWith( mockState, 'Test Title', '/new-path' ); - // Check, что было сгенерировано правильное событие + // Check that correct event was generated expect(window.dispatchEvent).toHaveBeenCalled(); const eventArg = (window.dispatchEvent as jest.Mock).mock.calls[0][0]; expect(eventArg).toBeInstanceOf(Event); expect(eventArg.type).toBe('ed_set_location'); }); - it('should работать с различными форматами путей', () => { - // Относительный путь + it('should work with different path formats', () => { + // Relative path setLocation('relative/path'); expect(window.history.pushState).toHaveBeenCalledWith( expect.anything(), @@ -95,7 +95,7 @@ describe('Utils', () => { 'relative/path' ); - // Путь с параметрами + // Path with parameters setLocation('/path?param=value'); expect(window.history.pushState).toHaveBeenCalledWith( expect.anything(), @@ -103,7 +103,7 @@ describe('Utils', () => { '/path?param=value' ); - // Путь с хэшем + // Path with hash setLocation('/path#section'); expect(window.history.pushState).toHaveBeenCalledWith( expect.anything(), @@ -112,14 +112,14 @@ describe('Utils', () => { ); }); - it('should использовать форматы из i18n для редактирования', () => { + it('should use formats from i18n for editing', () => { // Reconfigure mock for i18n.getLocaleSettings with different formats (i18n.getLocaleSettings as jest.Mock).mockReturnValue({ editDateFormat: 'MM/dd/yyyy', editTimeFormat: 'hh:mm a', }); - // Check обновленные форматы + // Check updated formats expect(getEditDateTimeFormat(DataType.Date)).toBe('MM/dd/yyyy'); expect(getEditDateTimeFormat(DataType.Time)).toBe('hh:mm a'); expect(getEditDateTimeFormat(DataType.DateTime)).toBe('MM/dd/yyyy hh:mm a'); diff --git a/easydata.js/packs/crud/tests/validators.test.ts b/easydata.js/packs/crud/tests/validators.test.ts index 67fe8b62..b23ee789 100644 --- a/easydata.js/packs/crud/tests/validators.test.ts +++ b/easydata.js/packs/crud/tests/validators.test.ts @@ -12,7 +12,7 @@ describe('TypeValidator', () => { beforeEach(() => { validator = new TypeValidator(); - // Мок текстов для i18n + // Mock texts for i18n jest.spyOn(i18n, 'getText').mockImplementation((key: string) => { if (key === 'NumberError') return 'Value must be a number'; if (key === 'IntNumberError') return 'Value must be an integer number'; @@ -66,7 +66,7 @@ describe('TypeValidator', () => { const result1 = validator.validate(mockAttr, '123'); expect(result1.successed).toBe(true); - // Decimal value не should be валидным для Int32 + // Decimal value shouldn't be valid for Int32 const result2 = validator.validate(mockAttr, '123.45'); expect(result2.successed).toBe(false); expect(result2.messages).toBeArray(); @@ -80,7 +80,7 @@ describe('TypeValidator', () => { const result1 = validator.validate(mockAttr, '123'); expect(result1.successed).toBe(true); - // Decimal value should be валидным для Float + // Decimal value should be valid for Float const result2 = validator.validate(mockAttr, '123.45'); expect(result2.successed).toBe(true); @@ -110,7 +110,7 @@ describe('RequiredValidator', () => { beforeEach(() => { validator = new RequiredValidator(); - // Мок текстов для i18n + // Mock texts for i18n jest.spyOn(i18n, 'getText').mockImplementation((key: string) => { if (key === 'RequiredError') return 'This field is required'; return key; @@ -121,12 +121,12 @@ describe('RequiredValidator', () => { jest.restoreAllMocks(); }); - it('should иметь имя "Required"', () => { + it('should have name "Required"', () => { expect(validator.name).toBe('Required'); expect(validator).toBeInstanceOf(Validator); }); - it('should проходить валидацию для nullable атрибутов', () => { + it('should pass validation for nullable attributes', () => { mockAttr = { isNullable: true } as MetaEntityAttr; const result1 = validator.validate(mockAttr, null); @@ -139,10 +139,10 @@ describe('RequiredValidator', () => { expect(result3.successed).toBe(true); }); - it('should проверять обязательные поля', () => { + it('should validate required fields', () => { mockAttr = { isNullable: false } as MetaEntityAttr; - // Не should be валидным для null, undefined или пустой строки + // Shouldn't be valid for null, undefined or empty string const result1 = validator.validate(mockAttr, null); expect(result1.successed).toBe(false); expect(result1.messages).toBeArray(); @@ -177,7 +177,7 @@ describe('DateTimeValidator', () => { beforeEach(() => { validator = new DateTimeValidator(); - // Мок текстов для i18n + // Mock texts for i18n jest.spyOn(i18n, 'getText').mockImplementation((key: string) => { if (key === 'DateTimeError') return 'Invalid date format'; return key; @@ -196,12 +196,12 @@ describe('DateTimeValidator', () => { jest.restoreAllMocks(); }); - it('should иметь имя "DateTime"', () => { + it('should have name "DateTime"', () => { expect(validator.name).toBe('DateTime'); expect(validator).toBeInstanceOf(Validator); }); - it('should успешно проходить валидацию для null или пустой строки', () => { + it('should successfully pass validation for null or empty string', () => { mockAttr = { dataType: DataType.Date } as MetaEntityAttr; const result1 = validator.validate(mockAttr, null); @@ -214,14 +214,14 @@ describe('DateTimeValidator', () => { expect(result3.successed).toBe(true); }); - it('should проверять формат даты', () => { + it('should validate date format', () => { mockAttr = { dataType: DataType.Date } as MetaEntityAttr; - // Корректный формат + // Correct format const result1 = validator.validate(mockAttr, '15.03.2023'); expect(result1.successed).toBe(true); - // Некорректный формат + // Incorrect format const result2 = validator.validate(mockAttr, '2023/03/15'); expect(result2.successed).toBe(false); expect(result2.messages).toBeArray(); @@ -234,14 +234,14 @@ describe('DateTimeValidator', () => { expect(result3.messages[0]).toBe('Invalid date format'); }); - it('should проверять формат времени', () => { + it('should validate time format', () => { mockAttr = { dataType: DataType.Time } as MetaEntityAttr; - // Корректный формат + // Correct format const result1 = validator.validate(mockAttr, '14:30'); expect(result1.successed).toBe(true); - // Некорректный формат + // Incorrect format const result2 = validator.validate(mockAttr, '14.30'); expect(result2.successed).toBe(false); expect(result2.messages).toBeArray(); @@ -254,28 +254,28 @@ describe('DateTimeValidator', () => { expect(result3.messages[0]).toBe('Invalid date format'); }); - it('should проверять формат даты и времени', () => { + it('should validate datetime format', () => { mockAttr = { dataType: DataType.DateTime } as MetaEntityAttr; - // Корректный формат + // Correct format const result1 = validator.validate(mockAttr, '15.03.2023 14:30'); expect(result1.successed).toBe(true); - // Некорректный формат + // Incorrect format const result2 = validator.validate(mockAttr, '2023-03-15 14:30'); expect(result2.successed).toBe(false); expect(result2.messages).toBeArray(); expect(result2.messages[0]).toBe('Invalid date format'); }); - it('should успешно проходить валидацию для нетиповых дат', () => { + it('should successfully pass validation for non-date types', () => { mockAttr = { dataType: DataType.String } as MetaEntityAttr; // Any value should be valid for string const result1 = validator.validate(mockAttr, '15.03.2023'); expect(result1.successed).toBe(true); - const result2 = validator.validate(mockAttr, 'неверная дата'); + const result2 = validator.validate(mockAttr, 'invalid date'); expect(result2.successed).toBe(true); }); }); From 28f2726d57983860536e3f8bab9976e9ab435ac3 Mon Sep 17 00:00:00 2001 From: korzh Date: Sat, 19 Jul 2025 16:36:21 +0300 Subject: [PATCH 06/11] Update comments in tests part 3 --- .../packs/crud/tests/data_context.test.ts | 12 +-- .../packs/crud/tests/entity_data_view.test.ts | 60 ++++++------- .../crud/tests/text_filter_widget.test.ts | 90 +++++++++---------- 3 files changed, 81 insertions(+), 81 deletions(-) diff --git a/easydata.js/packs/crud/tests/data_context.test.ts b/easydata.js/packs/crud/tests/data_context.test.ts index 2b19d88b..70d73c90 100644 --- a/easydata.js/packs/crud/tests/data_context.test.ts +++ b/easydata.js/packs/crud/tests/data_context.test.ts @@ -254,7 +254,7 @@ describe('DataContext', () => { return dataContext.createRecord(testObj); }) .then(() => { - // Check что был вызван правильный метод HTTP клиента + // Check that the correct HTTP client method was called expect(mockHttpClient.post).toHaveBeenCalled(); const lastCall = (mockHttpClient.post).mock.calls.pop(); @@ -263,7 +263,7 @@ describe('DataContext', () => { expect(lastCall[1].name).toBe('Test Record'); expect(lastCall[1].value).toBe(123); - // Check что были вызваны методы startProcess и endProcess + // Check that startProcess and endProcess methods were called expect(processStartCount).toBeGreaterThan(0); expect(processEndCount).toBeGreaterThan(0); }); @@ -282,7 +282,7 @@ describe('DataContext', () => { return dataContext.updateRecord(testObj); }) .then(() => { - // Check что был вызван правильный метод HTTP клиента + // Check that the correct HTTP client method was called expect(mockHttpClient.post).toHaveBeenCalled(); const lastCall = (mockHttpClient.post).mock.calls.pop(); @@ -291,7 +291,7 @@ describe('DataContext', () => { expect(lastCall[1].id).toBe(1); expect(lastCall[1].name).toBe('Updated Record'); - // Check что были вызваны методы startProcess и endProcess + // Check that startProcess and endProcess methods were called expect(processStartCount).toBeGreaterThan(0); expect(processEndCount).toBeGreaterThan(0); }); @@ -310,7 +310,7 @@ describe('DataContext', () => { return dataContext.deleteRecord(testObj); }) .then(() => { - // Check что был вызван правильный метод HTTP клиента + // Check that the correct HTTP client method was called expect(mockHttpClient.post).toHaveBeenCalled(); const lastCall = (mockHttpClient.post).mock.calls.pop(); @@ -318,7 +318,7 @@ describe('DataContext', () => { expect(lastCall[1]).toBeDefined(); expect(lastCall[1].id).toBe(1); - // Check что были вызваны методы startProcess и endProcess + // Check that startProcess and endProcess methods were called expect(processStartCount).toBeGreaterThan(0); expect(processEndCount).toBeGreaterThan(0); }); diff --git a/easydata.js/packs/crud/tests/entity_data_view.test.ts b/easydata.js/packs/crud/tests/entity_data_view.test.ts index 1cdbad57..7be6fb02 100644 --- a/easydata.js/packs/crud/tests/entity_data_view.test.ts +++ b/easydata.js/packs/crud/tests/entity_data_view.test.ts @@ -194,7 +194,7 @@ describe('EntityDataView', () => { expect(backLink).toBeDefined(); expect(backLink.textContent).toBe('← Back to Entities'); - // Check, что обработчик клика установлен + // Check that the click handler is set up const clickEvent = new MouseEvent('click'); const preventDefaultSpy = jest.spyOn(clickEvent, 'preventDefault'); backLink.dispatchEvent(clickEvent); @@ -214,7 +214,7 @@ describe('EntityDataView', () => { it('should call fetchDataset and create grid', () => { view = new EntityDataView(mockSlot, mockContext, '/basePath', {}); - // Check, что fetchDataset был вызван + // Check that fetchDataset was called expect(mockContext.fetchDataset).toHaveBeenCalled(); }); @@ -226,7 +226,7 @@ describe('EntityDataView', () => { jest.runAllTimers(); - // Check создание фильтра + // Check filter creation expect(mockContext.createFilter).toHaveBeenCalled(); }); @@ -244,10 +244,10 @@ describe('EntityDataView', () => { it('should correctly handle add button click', () => { view = new EntityDataView(mockSlot, mockContext, '/basePath', {}); - // Directly call обработчик клика кнопки добавления + // Directly call the add button click handler (view as any).addClickHandler(); - // Check вызов диалога + // Check dialog invocation expect(mockDialogService.open).toHaveBeenCalled(); const openArgs = (mockDialogService.open as jest.Mock).mock.calls[0][0]; expect(openArgs).toBeObject(); @@ -257,13 +257,13 @@ describe('EntityDataView', () => { it('should correctly handle edit button click', () => { view = new EntityDataView(mockSlot, mockContext, '/basePath', {}); - // Directly call обработчик клика кнопки редактирования + // Directly call the edit button click handler (view as any).editClickHandler(new MouseEvent('click'), 0); - // Check вызов getRow + // Check getRow invocation expect(mockDataTable.getRow).toHaveBeenCalledWith(0); - // Check что диалог редактирования открывается + // Check that the edit dialog opens return mockDataTable.getRow(0).then(() => { expect(mockDialogService.open).toHaveBeenCalled(); const openArgs = (mockDialogService.open as jest.Mock).mock.calls[0][0]; @@ -275,13 +275,13 @@ describe('EntityDataView', () => { it('should correctly handle delete button click', () => { view = new EntityDataView(mockSlot, mockContext, '/basePath', {}); - // Directly call обработчик клика кнопки удаления + // Directly call the delete button click handler (view as any).deleteClickHandler(new MouseEvent('click'), 0); - // Check вызов getRow + // Check getRow invocation expect(mockDataTable.getRow).toHaveBeenCalledWith(0); - // Check открытие диалога подтверждения + // Check confirmation dialog opening return mockDataTable.getRow(0).then(() => { expect(mockDialogService.openConfirm).toHaveBeenCalled(); @@ -300,10 +300,10 @@ describe('EntityDataView', () => { // Directly call refreshData return (view as any).refreshData().then(() => { - // Check вызов fetchDataset + // Check fetchDataset invocation expect(mockContext.fetchDataset).toHaveBeenCalled(); - // Check применение фильтра + // Check filter application expect(mockFilterWidget.applyFilter).toHaveBeenCalledWith(false); }); }); @@ -311,14 +311,14 @@ describe('EntityDataView', () => { it('should update grid if filter is not applied', () => { view = new EntityDataView(mockSlot, mockContext, '/basePath', {}); - // Replace filterWidget с флагом что фильтр не был применен + // Replace filterWidget with flag that filter was not applied (view as any).filterWidget = { applyFilter: mock().mockReturnValue(false) }; // Directly call refreshData return (view as any).refreshData().then(() => { - // Check вызов refresh у грида + // Check refresh call on grid expect(mockGrid.refresh).toHaveBeenCalled(); }); }); @@ -326,13 +326,13 @@ describe('EntityDataView', () => { it('should correctly handle errors', () => { view = new EntityDataView(mockSlot, mockContext, '/basePath', {}); - // Создаем ошибку + // Create an error const error = new Error('Test error'); - // Directly call обработчик ошибок + // Directly call the error handler (view as any).processError(error); - // Check открытие диалога с ошибкой + // Check error dialog opening expect(mockDialogService.open).toHaveBeenCalled(); const openArgs = (mockDialogService.open as jest.Mock).mock.calls[0][0]; expect(openArgs).toBeObject(); @@ -340,10 +340,10 @@ describe('EntityDataView', () => { expect(openArgs.body).toBe('Test error'); }); - it('should correctly управлять рендерером ячеек', () => { + it('should correctly manage cell renderer', () => { view = new EntityDataView(mockSlot, mockContext, '/basePath', {}); - // Create column с номером строки + // Create column with row number const column: GridColumn = { isRowNum: true } as GridColumn; @@ -351,13 +351,13 @@ describe('EntityDataView', () => { // Create mock for defaultRenderer const defaultRenderer = mock() as GridCellRenderer; - // Call метод manageCellRenderer + // Call the manageCellRenderer method const renderer = (view as any).manageCellRenderer(column, defaultRenderer); // Check that renderer function is returned expect(typeof renderer).toBe('function'); - // Check, что ширина колонки установлена + // Check that column width is set expect(column.width).toBe(110); // Prepare elements for renderer testing @@ -365,10 +365,10 @@ describe('EntityDataView', () => { const rowEl = document.createElement('tr'); rowEl.setAttribute('data-row-idx', '0'); - // Call рендерер + // Call the renderer renderer('value', column, cell, rowEl); - // Check, что в ячейке появились кнопки Edit и Delete + // Check that Edit and Delete buttons appear in the cell expect(cell.innerHTML).toContain('Edit'); expect(cell.innerHTML).toContain('Delete'); }); @@ -376,29 +376,29 @@ describe('EntityDataView', () => { it('should synchronize grid column visibility with metadata', () => { view = new EntityDataView(mockSlot, mockContext, '/basePath', {}); - // Create column с dataColumn + // Create column with dataColumn const column: GridColumn = { dataColumn: { id: 'Entity.name' } } as GridColumn; - // Set showOnView в false для атрибута + // Set showOnView to false for the attribute const attr = mockMetaData.getAttributeById('Entity.name'); attr.showOnView = false; - // Call метод syncGridColumnHandler + // Call the syncGridColumnHandler method (view as any).syncGridColumnHandler(column); - // Check, что видимость колонки установлена в соответствии с showOnView атрибута + // Check that column visibility is set according to the showOnView attribute expect(column.isVisible).toBe(false); }); - it('should throw error если активная сущность не найдена', () => { + it('should throw error if active entity is not found', () => { // Replace getActiveEntity to return null (mockContext.getActiveEntity as jest.Mock).mockReturnValue(null); - // Check, что конструктор выбрасывает ошибку + // Check that the constructor throws an error expect(() => { new EntityDataView(mockSlot, mockContext, '/basePath', {}); }).toThrow("Can't find active entity for " + window.location.pathname); diff --git a/easydata.js/packs/crud/tests/text_filter_widget.test.ts b/easydata.js/packs/crud/tests/text_filter_widget.test.ts index ff5efac6..ef023c37 100644 --- a/easydata.js/packs/crud/tests/text_filter_widget.test.ts +++ b/easydata.js/packs/crud/tests/text_filter_widget.test.ts @@ -20,15 +20,15 @@ describe('TextFilterWidget', () => { // Initialization before each test beforeEach(() => { - // Create DOM для монтирования виджета + // Create DOM for mounting the widget mockSlot = document.createElement('div'); document.body.appendChild(mockSlot); - // Create mocks for рендереров ячеек + // Create mocks for cell renderers defaultStringRenderer = mock(); defaultNumberRenderer = mock(); - // Mock for хранилища рендереров + // Mock for renderer store mockCellRendererStore = { getDefaultRendererByType: mock((type: CellRendererType) => { if (type === CellRendererType.STRING) return defaultStringRenderer; @@ -57,11 +57,11 @@ describe('TextFilterWidget', () => { return key; }); - // Mock for browserUtils.isIE и isEdge + // Mock for browserUtils.isIE and isEdge jest.spyOn(browserUtils, 'isIE').mockReturnValue(false); jest.spyOn(browserUtils, 'isEdge').mockReturnValue(false); - // Mock for dataUtils.isNumericType и getStringDataTypes + // Mock for dataUtils.isNumericType and getStringDataTypes jest.spyOn(dataUtils, 'isNumericType').mockImplementation((type) => { return type === 'number'; }); @@ -77,36 +77,36 @@ describe('TextFilterWidget', () => { mockSlot.parentNode.removeChild(mockSlot); } - // Сброс всех моков + // Reset all mocks jest.restoreAllMocks(); }); - it('should создаваться with default settings', () => { + it('should be created with default settings', () => { expect(filterWidget).toBeDefined(); - // Check установки рендереров + // Check renderer setup expect(mockCellRendererStore.getDefaultRendererByType).toHaveBeenCalledWith(CellRendererType.STRING); expect(mockCellRendererStore.getDefaultRendererByType).toHaveBeenCalledWith(CellRendererType.NUMBER); expect(mockCellRendererStore.setDefaultRenderer).toHaveBeenCalledTimes(2); }); - it('should правильно рендерить HTML структуру', () => { - // Check наличия поля ввода + it('should correctly render HTML structure', () => { + // Check input field presence const input = mockSlot.querySelector('input'); expect(input).toBeDefined(); expect(input.getAttribute('placeholder')).toBe('Search...'); - // Check наличия кнопки поиска (вне режима instantMode) + // Check search button presence (outside instantMode) const button = mockSlot.querySelector('button'); expect(button).toBeDefined(); expect(button.textContent).toBe('Search'); - // Check наличия иконки очистки + // Check clear icon presence const clearIcon = mockSlot.querySelector('span.icon'); expect(clearIcon).toBeDefined(); }); - it('should рендерить HTML без кнопки поиска в instantMode', () => { + it('should render HTML without search button in instantMode', () => { // Remove предыдущий виджет mockSlot.innerHTML = ''; @@ -115,16 +115,16 @@ describe('TextFilterWidget', () => { instantMode: true }); - // Check наличия поля ввода + // Check input field presence const input = mockSlot.querySelector('input'); expect(input).toBeDefined(); - // Check отсутствия кнопки поиска + // Check absence of search button const button = mockSlot.querySelector('button'); expect(button).toBeNull(); }); - it('should использовать другие классы для IE', () => { + it('should use different classes for IE', () => { // Remove предыдущий виджет mockSlot.innerHTML = ''; @@ -134,11 +134,11 @@ describe('TextFilterWidget', () => { // Создаем новый виджет filterWidget = new TextFilterWidget(mockSlot, mockGrid, mockFilter); - // Check наличия класса для IE + // Check IE class presence expect(mockSlot.classList.contains('kfrm-fields-ie')).toBe(true); }); - it('should устанавливать фокус на поле ввода, если опция focus=true', () => { + it('should set focus on input field if focus=true option is set', () => { // Remove предыдущий виджет mockSlot.innerHTML = ''; @@ -151,11 +151,11 @@ describe('TextFilterWidget', () => { focus: true }); - // Check вызова focus + // Check focus call expect(focusMock).toHaveBeenCalled(); }); - it('should применять фильтр при нажатии Enter', () => { + it('should apply filter when Enter is pressed', () => { // Get поле ввода const input = mockSlot.querySelector('input') as HTMLInputElement; @@ -166,11 +166,11 @@ describe('TextFilterWidget', () => { const enterEvent = new KeyboardEvent('keydown', { keyCode: 13 }); input.dispatchEvent(enterEvent); - // Check вызова apply с правильным значением + // Check apply call with correct value expect(mockFilter.apply).toHaveBeenCalledWith('test'); }); - it('should применять фильтр при нажатии кнопки Search', () => { + it('should apply filter when Search button is pressed', () => { // Get поле ввода и кнопку const input = mockSlot.querySelector('input') as HTMLInputElement; const button = mockSlot.querySelector('button') as HTMLButtonElement; @@ -181,11 +181,11 @@ describe('TextFilterWidget', () => { // Emulate клик по кнопке button.click(); - // Check вызова apply с правильным значением + // Check apply call with correct value expect(mockFilter.apply).toHaveBeenCalledWith('test'); }); - it('should очищать поле ввода при нажатии на иконку очистки', () => { + it('should clear input field when clear icon is clicked', () => { // Get поле ввода и иконку очистки const input = mockSlot.querySelector('input') as HTMLInputElement; const clearIcon = mockSlot.querySelector('span.icon') as HTMLSpanElement; @@ -198,17 +198,17 @@ describe('TextFilterWidget', () => { // Emulate клик по иконке очистки clearIcon.click(); - // Check очистки значения + // Check value clearing expect(input.value).toBe(''); - // Check установки фокуса + // Check focus setting expect(focusMock).toHaveBeenCalled(); - // Check вызова apply с пустым значением + // Check apply call with empty value expect(mockFilter.apply).toHaveBeenCalledWith(''); }); - it('should применять фильтр с задержкой в instantMode при вводе', () => { + it('should apply filter with delay in instantMode on input', () => { // Remove предыдущий виджет mockSlot.innerHTML = ''; @@ -228,20 +228,20 @@ describe('TextFilterWidget', () => { input.value = 'test'; input.dispatchEvent(new KeyboardEvent('keyup')); - // Check, что apply не вызван сразу + // Check that apply is not called immediately expect(mockFilter.apply).not.toHaveBeenCalled(); // Проматываем таймеры jest.advanceTimersByTime(500); - // Check вызова apply с правильным значением + // Check apply call with correct value expect(mockFilter.apply).toHaveBeenCalledWith('test'); - // Restore таймеры + // Restore timers jest.useRealTimers(); }); - it('should очищать предыдущий таймер при новом событии keyup', () => { + it('should clear previous timer on new keyup event', () => { // Remove предыдущий виджет mockSlot.innerHTML = ''; @@ -262,24 +262,24 @@ describe('TextFilterWidget', () => { input.value = 'test'; input.dispatchEvent(new KeyboardEvent('keyup')); - // Второй ввод до истечения таймаута + // Second input before timeout expires input.value = 'test2'; input.dispatchEvent(new KeyboardEvent('keyup')); - // Check вызова clearTimeout + // Check clearTimeout call expect(clearTimeoutSpy).toHaveBeenCalled(); - // Проматываем таймеры + // Advance timers jest.advanceTimersByTime(500); - // Check вызова apply с последним значением + // Check apply call with last value expect(mockFilter.apply).toHaveBeenCalledWith('test2'); - // Restore таймеры + // Restore timers jest.useRealTimers(); }); - it('метод applyFilter should вернуть true если значение фильтра изменилось', () => { + it('applyFilter method should return true if filter value changed', () => { // Get поле ввода const input = mockSlot.querySelector('input') as HTMLInputElement; @@ -293,11 +293,11 @@ describe('TextFilterWidget', () => { const result = filterWidget.applyFilter(true); expect(result).toBe(true); - // Check вызова apply + // Check apply call expect(mockFilter.apply).toHaveBeenCalledWith('test'); }); - it('метод applyFilter should вернуть false если значение фильтра не изменилось', () => { + it('applyFilter method should return false if filter value did not change', () => { // Get поле ввода const input = mockSlot.querySelector('input') as HTMLInputElement; @@ -311,11 +311,11 @@ describe('TextFilterWidget', () => { const result = filterWidget.applyFilter(true); expect(result).toBe(false); - // Check, что apply не вызван + // Check that apply is not called expect(mockFilter.apply).not.toHaveBeenCalled(); }); - it('should правильно выделять текст, совпадающий с фильтром', () => { + it('should correctly highlight text matching the filter', () => { // Mock for getValue, возвращающий искомое слово (mockFilter.getValue as jest.Mock).mockReturnValue('test'); @@ -342,7 +342,7 @@ describe('TextFilterWidget', () => { expect(div.childNodes[2].textContent).toBe(' string'); }); - it('should правильно выделять несколько совпадений', () => { + it('should correctly highlight multiple matches', () => { // Mock for getValue, возвращающий искомое слово (mockFilter.getValue as jest.Mock).mockReturnValue('test'); @@ -372,7 +372,7 @@ describe('TextFilterWidget', () => { expect(secondSpan.textContent).toBe('test'); }); - it('should правильно обрабатывать несколько искомых слов через разделитель ||', () => { + it('should correctly handle multiple search words with || separator', () => { // Mock for getValue, возвращающий несколько слов через разделитель (mockFilter.getValue as jest.Mock).mockReturnValue('apple || banana'); @@ -403,7 +403,7 @@ describe('TextFilterWidget', () => { expect(bananaFound).toBe(true); }); - it('should выделять всю ячейку, если содержимое полностью совпадает с фильтром', () => { + it('should highlight entire cell if content fully matches the filter', () => { // Mock for getValue, возвращающий полное значение ячейки (mockFilter.getValue as jest.Mock).mockReturnValue('exact match'); From 9b2e946eab3fb0a416275d0431ee008cc90c6719 Mon Sep 17 00:00:00 2001 From: korzh Date: Sat, 19 Jul 2025 16:48:03 +0300 Subject: [PATCH 07/11] Fix comments in test files part4 --- .../packs/core/tests/event_emitter.test.ts | 8 +- .../packs/core/tests/http_client.test.ts | 4 +- .../packs/core/tests/http_request.test.ts | 16 +-- .../packs/core/tests/time_utils.test.ts | 36 +++--- easydata.js/packs/core/tests/utils.test.ts | 110 +++++++++--------- .../packs/core/tests/value_editor.test.ts | 14 +-- .../packs/crud/tests/entity_data_view.test.ts | 4 +- 7 files changed, 96 insertions(+), 96 deletions(-) diff --git a/easydata.js/packs/core/tests/event_emitter.test.ts b/easydata.js/packs/core/tests/event_emitter.test.ts index ce04a9b0..b8f7b676 100644 --- a/easydata.js/packs/core/tests/event_emitter.test.ts +++ b/easydata.js/packs/core/tests/event_emitter.test.ts @@ -126,18 +126,18 @@ describe('EventEmitter', () => { eventEmitter.subscribe('testEvent', callback); - // По умолчанию не в тихом режиме + // By default, not in silent mode expect(eventEmitter.isSilent()).toBe(false); - // Входим в тихий режим + // Entering silent mode eventEmitter.enterSilentMode(); expect(eventEmitter.isSilent()).toBe(true); - // В тихом режиме колбэк не should вызываться + // In silent mode, the callback should not be called eventEmitter.fire('testEvent'); expect(callbackCalled).toBe(false); - // Выходим из тихого режима + // Exiting silent mode eventEmitter.exitSilentMode(); expect(eventEmitter.isSilent()).toBe(false); diff --git a/easydata.js/packs/core/tests/http_client.test.ts b/easydata.js/packs/core/tests/http_client.test.ts index 46af7177..39ba7e3d 100644 --- a/easydata.js/packs/core/tests/http_client.test.ts +++ b/easydata.js/packs/core/tests/http_client.test.ts @@ -84,7 +84,7 @@ describe('HttpClient', () => { expect(xhrMock.send).toHaveBeenCalledWith(JSON.stringify(data)); }); - it('should выполнять PUT запрос с данными', () => { + it('should perform PUT request with data', () => { const url = 'https://test.com/api/data/1'; const data = { name: 'Updated Name', age: 31 }; @@ -190,7 +190,7 @@ describe('HttpClient', () => { it('should handle HTTP errors', async () => { const responsePromise = httpClient.get('https://test.com/api/data'); - // Emulate HTTP ошибку + // Emulate HTTP error xhrMock.status = 404; xhrMock.responseText = '{"message":"Resource not found"}'; xhrMock.onreadystatechange(); diff --git a/easydata.js/packs/core/tests/http_request.test.ts b/easydata.js/packs/core/tests/http_request.test.ts index 0d32527b..ecf7b12b 100644 --- a/easydata.js/packs/core/tests/http_request.test.ts +++ b/easydata.js/packs/core/tests/http_request.test.ts @@ -116,7 +116,7 @@ describe('HttpRequest', () => { expect(xhrMock.setRequestHeader).toHaveBeenCalledWith('Content-Type', 'application/json'); }); - it('не should открывать запрос повторно если он уже открыт', () => { + it('should not open the request again if it is already open', () => { xhrMock.readyState = 1; // OPENED const request = new HttpRequest(xhrMock, requestDescriptor); @@ -125,7 +125,7 @@ describe('HttpRequest', () => { expect(xhrMock.open).not.toHaveBeenCalled(); }); - it('should вызывать abort у XMLHttpRequest при вызове abort', () => { + it('should call abort on XMLHttpRequest when abort is called', () => { const request = new HttpRequest(xhrMock, requestDescriptor); request.abort(); @@ -133,33 +133,33 @@ describe('HttpRequest', () => { expect(xhrMock.abort).toHaveBeenCalled(); }); - it('should correctly кодировать URL с параметрами запроса', () => { + it('should correctly encode URL with query parameters', () => { const request = new HttpRequest(xhrMock, requestDescriptor); request.setQueryParam('name', 'John Doe'); request.setQueryParam('tags', 'tag1,tag2'); request.open(); - // URL should быть закодирован правильно + // URL should be encoded correctly expect(xhrMock.open).toHaveBeenCalledWith('GET', 'https://test.com/api?name=John%20Doe&tags=tag1%2Ctag2', true); }); - it('should поддерживать различные HTTP методы', () => { - // Тестируем метод POST + it('should support various HTTP methods', () => { + // Testing POST method requestDescriptor.method = HttpMethod.POST; let request = new HttpRequest(xhrMock, requestDescriptor); request.open(); expect(xhrMock.open).toHaveBeenCalledWith('POST', 'https://test.com/api', true); - // Тестируем метод PUT + // Testing PUT method xhrMock.open.mockClear(); requestDescriptor.method = HttpMethod.PUT; request = new HttpRequest(xhrMock, requestDescriptor); request.open(); expect(xhrMock.open).toHaveBeenCalledWith('PUT', 'https://test.com/api', true); - // Тестируем метод DELETE + // Testing DELETE method xhrMock.open.mockClear(); requestDescriptor.method = HttpMethod.DELETE; request = new HttpRequest(xhrMock, requestDescriptor); diff --git a/easydata.js/packs/core/tests/time_utils.test.ts b/easydata.js/packs/core/tests/time_utils.test.ts index 48738959..90662152 100644 --- a/easydata.js/packs/core/tests/time_utils.test.ts +++ b/easydata.js/packs/core/tests/time_utils.test.ts @@ -147,7 +147,7 @@ describe('Time Utils', () => { expect(result).toBeInstanceOf(Date); expect(result.getFullYear()).toBe(2022); - expect(result.getMonth()).toBe(0); // январь + expect(result.getMonth()).toBe(0); // January expect(result.getDate()).toBe(1); }); @@ -157,41 +157,41 @@ describe('Time Utils', () => { expect(result).toBeInstanceOf(Date); expect(result.getFullYear()).toBe(2024); - expect(result.getMonth()).toBe(0); // январь + expect(result.getMonth()).toBe(0); // January expect(result.getDate()).toBe(1); }); - it('should разрешать специальную дату FirstDayOfWeek для середины недели', () => { - // 15 мая 2023 - понедельник - fixedDate = new Date(2023, 4, 15); // 15 мая 2023 + it('should resolve special date FirstDayOfWeek for midweek', () => { + // 15 May 2023 - Monday + fixedDate = new Date(2023, 4, 15); // 15 May 2023 const resolver = new SpecialDatesResolver(); const result = resolver.FirstDayOfWeek(); expect(result).toBeInstanceOf(Date); expect(result.getFullYear()).toBe(2023); expect(result.getMonth()).toBe(4); - expect(result.getDate()).toBe(15); // понедельник + expect(result.getDate()).toBe(15); // Monday }); - it('should разрешать специальную дату FirstDayOfPrevWeek', () => { + it('should resolve special date FirstDayOfPrevWeek', () => { const resolver = new SpecialDatesResolver(); const result = resolver.FirstDayOfPrevWeek(); expect(result).toBeInstanceOf(Date); - // Конкретная дата зависит от того, какой день недели у fixedDate + // The specific date depends on what day of the week fixedDate is expect(result).toBeInstanceOf(Date); }); - it('should разрешать специальную дату FirstDayOfNextWeek', () => { + it('should resolve special date FirstDayOfNextWeek', () => { const resolver = new SpecialDatesResolver(); const result = resolver.FirstDayOfNextWeek(); expect(result).toBeInstanceOf(Date); - // Конкретная дата зависит от того, какой день недели у fixedDate + // The specific date depends on what day of the week fixedDate is expect(result).toBeInstanceOf(Date); }); - it('should return дату по имени через getDateByName', () => { + it('should return date by name via getDateByName', () => { const resolver = new SpecialDatesResolver(); const result = resolver.getDateByName('Today'); @@ -202,15 +202,15 @@ describe('Time Utils', () => { expect(result.getDate()).toBe(15); }); - it('should return undefined для неизвестного имени даты', () => { + it('should return undefined for unknown date name', () => { const resolver = new SpecialDatesResolver(); const result = resolver.getDateByName('NonExistentDate'); expect(result).toBeUndefined(); }); - it('should регистрировать новый резолвер через registerSpecialDatesResolver', () => { - // Create custom резолвер + it('should register new resolver via registerSpecialDatesResolver', () => { + // Create custom resolver class CustomResolver extends SpecialDatesResolver { public Custom(): Date { return new Date(2000, 0, 1); @@ -218,11 +218,11 @@ describe('Time Utils', () => { } const customResolver = new CustomResolver(); - - // Регистрируем его + + // Register it registerSpecialDatesResolver(customResolver); - - // Check, что теперь используется новый резолвер + + // Check that the new resolver is now used const timeValue = new TimeValue('Custom'); const result = timeValue.asTime(); diff --git a/easydata.js/packs/core/tests/utils.test.ts b/easydata.js/packs/core/tests/utils.test.ts index aa80c1fe..5aee3934 100644 --- a/easydata.js/packs/core/tests/utils.test.ts +++ b/easydata.js/packs/core/tests/utils.test.ts @@ -4,8 +4,8 @@ import { utils } from '../src/utils/utils'; import { DataType } from '../src/types/data_type'; describe('utils', () => { - // Тесты для функций с типами данных - it('should вернуть все типы данных через getAllDataTypes', () => { + // Tests for functions with data types + it('should return all data types through getAllDataTypes', () => { const dataTypes = utils.getAllDataTypes(); expect(dataTypes).toContain(DataType.String); expect(dataTypes).toContain(DataType.Int32); @@ -15,7 +15,7 @@ describe('utils', () => { expect(typeof dataTypes[0]).toBe('number'); }); - it('should вернуть все типы для дат через getDateDataTypes', () => { + it('should return all date types through getDateDataTypes', () => { const dateTypes = utils.getDateDataTypes(); expect(dateTypes.length).toBe(3); expect(dateTypes).toContain(DataType.Date); @@ -23,7 +23,7 @@ describe('utils', () => { expect(dateTypes).toContain(DataType.Time); }); - it('should вернуть все строковые типы через getStringDataTypes', () => { + it('should return all string types through getStringDataTypes', () => { const stringTypes = utils.getStringDataTypes(); expect(stringTypes.length).toBe(3); expect(stringTypes).toContain(DataType.String); @@ -31,14 +31,14 @@ describe('utils', () => { expect(stringTypes).toContain(DataType.FixedChar); }); - it('should вернуть все числовые типы через getNumericDataTypes', () => { + it('should return all numeric types through getNumericDataTypes', () => { const numericTypes = utils.getNumericDataTypes(); expect(numericTypes).toContain(DataType.Int32); expect(numericTypes).toContain(DataType.Float); expect(numericTypes).toContain(DataType.Currency); }); - it('should проверять, является ли тип числовым через isNumericType', () => { + it('should check if the type is numeric through isNumericType', () => { expect(utils.isNumericType(DataType.Int32)).toBe(true); expect(utils.isNumericType(DataType.Float)).toBe(true); expect(utils.isNumericType(DataType.Currency)).toBe(true); @@ -46,7 +46,7 @@ describe('utils', () => { expect(utils.isNumericType(DataType.Date)).toBe(false); }); - it('should проверять, является ли тип целочисленным через isIntType', () => { + it('should check if the type is integer through isIntType', () => { expect(utils.isIntType(DataType.Int32)).toBe(true); expect(utils.isIntType(DataType.Int64)).toBe(true); expect(utils.isIntType(DataType.Byte)).toBe(true); @@ -54,7 +54,7 @@ describe('utils', () => { expect(utils.isIntType(DataType.String)).toBe(false); }); - it('should проверять, совместимы ли типы данных через areCompatibleDataTypes', () => { + it('should check if data types are compatible through areCompatibleDataTypes', () => { expect(utils.areCompatibleDataTypes(DataType.Int32, DataType.Int32)).toBe(true); expect(utils.areCompatibleDataTypes(DataType.Date, DataType.DateTime)).toBe(true); expect(utils.areCompatibleDataTypes(DataType.DateTime, DataType.Date)).toBe(true); @@ -63,22 +63,22 @@ describe('utils', () => { expect(utils.areCompatibleDataTypes(DataType.Int32, DataType.Unknown)).toBe(true); }); - // Тесты для объектных функций - it('should объединять объекты через assign', () => { + // Tests for object functions + it('should merge objects through assign', () => { const target = { a: 1, b: 2 }; const source1 = { b: 3, c: 4 }; const source2 = { d: 5 }; const result = utils.assign(target, source1, source2); - expect(result).toBe(target); // Check, что assign возвращает target + expect(result).toBe(target); // Check that assign returns target expect(result.a).toBe(1); - expect(result.b).toBe(3); // Значение из source1 перезаписало значение из target + expect(result.b).toBe(3); // Value from source1 overwrote value from target expect(result.c).toBe(4); expect(result.d).toBe(5); }); - it('should обрабатывать пустые объекты и null в assign', () => { + it('should handle empty objects and null in assign', () => { const target = null; const source = { a: 1 }; @@ -87,7 +87,7 @@ describe('utils', () => { expect(result).toBeObject({ a: 1 }); }); - it('should делать глубокое копирование объектов через assignDeep', () => { + it('should perform deep copying of objects through assignDeep', () => { const target = { a: 1, nested: { x: 10, y: 20 } @@ -103,27 +103,27 @@ describe('utils', () => { expect(result.a).toBe(1); expect(result.b).toBe(2); expect(result.nested.x).toBe(10); - expect(result.nested.y).toBe(30); // Значение из source перезаписало значение из target + expect(result.nested.y).toBe(30); // Value from source overwrote value from target expect(result.nested.z).toBe(40); - // Check, что объекты действительно скопированы, а не ссылаются на тот же объект + // Check that objects are actually copied, not referencing the same object source.nested.y = 100; expect(result.nested.y).toBe(30); }); - it('should обрабатывать циклические ссылки в assignDeep', () => { + it('should handle cyclic references in assignDeep', () => { const target = {}; const source = { a: 1 }; - source.self = source; // Циклическая ссылка + source.self = source; // Cyclic reference - // Не должно вызывать бесконечную рекурсию + // Should not cause infinite recursion const result = utils.assignDeep(target, source); expect(result.a).toBe(1); - expect(result.self).toBe(result); // Цикл сохранен, но ссылается на новый объект + expect(result.self).toBe(result); // Cycle is preserved but refers to the new object }); - it('should копировать массивы в assignDeep', () => { + it('should copy arrays in assignDeep', () => { const target = { arr: [1, 2] }; const source = { arr: [3, 4, 5] }; @@ -131,19 +131,19 @@ describe('utils', () => { expect(result.arr).toBeArrayEqual([3, 4, 5]); - // Check, что массивы действительно скопированы + // Check that arrays are actually copied source.arr.push(6); - expect(result.arr).toBeArrayEqual([3, 4, 5]); // Не изменился после изменения source + expect(result.arr).toBeArrayEqual([3, 4, 5]); // Did not change after modifying source }); - it('should return значение по умолчанию через getIfDefined', () => { + it('should return default value through getIfDefined', () => { expect(utils.getIfDefined(undefined, 'default')).toBe('default'); expect(utils.getIfDefined(null, 'default')).toBe(null); expect(utils.getIfDefined(0, 'default')).toBe(0); expect(utils.getIfDefined('value', 'default')).toBe('value'); }); - it('should проверять определенность и не-null значения через IsDefinedAndNotNull', () => { + it('should check definiteness and non-null value through IsDefinedAndNotNull', () => { expect(utils.IsDefinedAndNotNull(undefined)).toBe(false); expect(utils.IsDefinedAndNotNull(null)).toBe(false); expect(utils.IsDefinedAndNotNull(0)).toBe(true); @@ -151,7 +151,7 @@ describe('utils', () => { expect(utils.IsDefinedAndNotNull(false)).toBe(true); }); - it('should проверять, является ли значение объектом через isObject', () => { + it('should check if value is an object through isObject', () => { expect(utils.isObject({})).toBe(true); expect(utils.isObject([])).toBe(true); expect(utils.isObject(function() {})).toBe(true); @@ -161,7 +161,7 @@ describe('utils', () => { expect(utils.isObject('string')).toBe(false); }); - it('should проверять, установлено ли свойство через isPropSet', () => { + it('should check if property is set through isPropSet', () => { const obj = { prop1: 'value1', PROP2: 'value2', @@ -170,14 +170,14 @@ describe('utils', () => { }; expect(utils.isPropSet(obj, 'prop1')).toBe('value1'); - expect(utils.isPropSet(obj, 'prop2')).toBe('value2'); // Check регистронезависимости + expect(utils.isPropSet(obj, 'prop2')).toBe('value2'); // Check case insensitivity expect(utils.isPropSet(obj, 'prop3')).toBe(null); expect(utils.isPropSet(obj, 'prop4')).toBe(undefined); expect(utils.isPropSet(obj, 'nonExistent')).toBe(undefined); }); - // Тесты для функций с массивами - it('should копировать элементы одного массива в другой через copyArrayTo', () => { + // Tests for functions with arrays + it('should copy elements from one array to another through copyArrayTo', () => { const source = [1, 2, 3, 4]; const target = [0, 0, 0, 0, 0]; @@ -186,7 +186,7 @@ describe('utils', () => { expect(target).toBeArrayEqual([1, 2, 3, 4, 0]); }); - it('should создавать новый массив из коллекции через createArrayFrom', () => { + it('should create a new array from collection through createArrayFrom', () => { const collection = { 0: 'a', 1: 'b', @@ -203,10 +203,10 @@ describe('utils', () => { expect(Array.isArray(result)).toBe(true); expect(result).toBeArrayEqual(['a', 'b', 'c']); - expect(result === collection).toBe(false); // Новый массив, не исходная коллекция + expect(result === collection).toBe(false); // New array, not the original collection }); - it('should находить элемент по id через findItemById', () => { + it('should find item by id through findItemById', () => { const array = [ { id: 1, value: 'one' }, { id: 2, value: 'two' }, @@ -222,7 +222,7 @@ describe('utils', () => { expect(notFound).toBeNull(); }); - it('should находить индекс элемента по id через findItemIndexById', () => { + it('should find item index by id through findItemIndexById', () => { const array = [ { id: 1, value: 'one' }, { id: 2, value: 'two' }, @@ -236,14 +236,14 @@ describe('utils', () => { expect(notFoundIndex).toBe(-1); }); - it('should находить индекс элемента в массиве через indexOfArrayItem', () => { + it('should find index of item in array through indexOfArrayItem', () => { const array = ['a', 'b', 'c', 'd']; expect(utils.indexOfArrayItem(array, 'b')).toBe(1); expect(utils.indexOfArrayItem(array, 'e')).toBe(-1); }); - it('should перемещать элемент в массиве через moveArrayItem', () => { + it('should move item in array through moveArrayItem', () => { const array = ['a', 'b', 'c', 'd', 'e']; utils.moveArrayItem(array, 1, 3); @@ -251,7 +251,7 @@ describe('utils', () => { expect(array).toBeArrayEqual(['a', 'c', 'd', 'b', 'e']); }); - it('should throw error при перемещении несуществующего элемента', () => { + it('should throw error when moving non-existent item', () => { const array = ['a', 'b', 'c']; expect(() => { @@ -259,10 +259,10 @@ describe('utils', () => { }).toThrow('Index out of bounds: 5'); }); - it('should correctly handle перемещение за пределы массива', () => { + it('should correctly handle moving out of array bounds', () => { const array = ['a', 'b', 'c', 'd', 'e']; - utils.moveArrayItem(array, 1, 10); // Индекс 10 вне массива, should быть скорректирован + utils.moveArrayItem(array, 1, 10); // Index 10 is out of array, should be adjusted expect(array).toBeArrayEqual(['a', 'c', 'd', 'e', 'b']); }); @@ -280,7 +280,7 @@ describe('utils', () => { expect(array).toBeArrayEqual(['a', 'c', 'd']); }); - it('should вставлять элемент в массив через insertArrayItem', () => { + it('should insert element in array through insertArrayItem', () => { const array = ['a', 'c', 'd']; utils.insertArrayItem(array, 1, 'b'); @@ -288,7 +288,7 @@ describe('utils', () => { expect(array).toBeArrayEqual(['a', 'b', 'c', 'd']); }); - it('should заполнять массив значениями через fillArray', () => { + it('should fill array with values through fillArray', () => { const array = ['a', 'b', 'c', 'd', 'e']; utils.fillArray(array, 'x', 1, 4); @@ -296,7 +296,7 @@ describe('utils', () => { expect(array).toBeArrayEqual(['a', 'x', 'x', 'x', 'e']); }); - it('should обрабатывать отрицательные индексы в fillArray', () => { + it('should handle negative indices in fillArray', () => { const array = ['a', 'b', 'c', 'd', 'e']; utils.fillArray(array, 'x', -3, -1); @@ -304,7 +304,7 @@ describe('utils', () => { expect(array).toBeArrayEqual(['a', 'b', 'x', 'x', 'e']); }); - it('should заполнять до конца массива при отсутствии end в fillArray', () => { + it('should fill to the end of the array when no end is provided in fillArray', () => { const array = ['a', 'b', 'c', 'd', 'e']; utils.fillArray(array, 'x', 3); @@ -312,8 +312,8 @@ describe('utils', () => { expect(array).toBeArrayEqual(['a', 'b', 'c', 'x', 'x']); }); - // Тесты для функций с проверками - it('should проверять, является ли значение числовым через isNumeric', () => { + // Tests for functions with checks + it('should check if value is numeric through isNumeric', () => { expect(utils.isNumeric(42)).toBe(true); expect(utils.isNumeric('42')).toBe(true); expect(utils.isNumeric(-1.5)).toBe(true); @@ -326,8 +326,8 @@ describe('utils', () => { expect(utils.isNumeric(undefined)).toBe(false); }); - // Тесты для функций генерации ID - it('should генерировать уникальные ID через generateId', () => { + // Tests for ID generation functions + it('should generate unique IDs through generateId', () => { const id1 = utils.generateId('test'); const id2 = utils.generateId('test'); @@ -336,18 +336,18 @@ describe('utils', () => { expect(id2.startsWith('test-')).toBe(true); }); - it('should использовать префикс easy при вызове generateId без аргументов', () => { + it('should use easy prefix when calling generateId with no arguments', () => { const id = utils.generateId(null); expect(id.startsWith('easy-')).toBe(true); }); - it('should сокращать длинные префиксы в generateId', () => { + it('should shorten long prefixes in generateId', () => { const id = utils.generateId('veryLongPrefix'); expect(id.startsWith('vryL-')).toBe(true); }); - // Тесты для функций с датами - it('should конвертировать строку в дату через strToDateTime', () => { + // Tests for date functions + it('should convert string to date through strToDateTime', () => { const dateStr = '2023-05-15-14-30-45'; const format = 'yyyy-MM-dd-HH-mm-ss'; @@ -362,7 +362,7 @@ describe('utils', () => { expect(result.getSeconds()).toBe(45); }); - it('should обрабатывать различные форматы разделителей в strToDateTime', () => { + it('should handle different delimiter formats in strToDateTime', () => { const dateStr = '2023/05/15 14:30:45'; const format = 'yyyy/MM/dd HH:mm:ss'; @@ -377,13 +377,13 @@ describe('utils', () => { expect(result.getSeconds()).toBe(45); }); - it('should throw error при некорректной дате в strToDateTime', () => { + it('should throw error on incorrect date in strToDateTime', () => { expect(() => { utils.strToDateTime('2023-13-32', 'yyyy-MM-dd'); }).toThrow(); }); - it('should конвертировать строку во время через strToTime', () => { + it('should convert string to time through strToTime', () => { const timeStr = '14:30:45'; const result = utils.strToTime(timeStr); @@ -394,7 +394,7 @@ describe('utils', () => { expect(result.getSeconds()).toBe(0); // Bug in implementation, should be 45 }); - it('should throw error при некорректном времени в strToTime', () => { + it('should throw error on incorrect time in strToTime', () => { expect(() => { utils.strToTime('25:70'); }).toThrow(); diff --git a/easydata.js/packs/core/tests/value_editor.test.ts b/easydata.js/packs/core/tests/value_editor.test.ts index 2633edc1..875853b6 100644 --- a/easydata.js/packs/core/tests/value_editor.test.ts +++ b/easydata.js/packs/core/tests/value_editor.test.ts @@ -60,7 +60,7 @@ describe('ValueEditor', () => { expect(editor.id).toBe('test-editor'); expect(editor.tag).toBe(EditorTag.MultipleChoice); - // subType should перезаписать rtype + // subType should override rtype expect(editor.resType).toBe(DataType.Int32); expect(editor.defValue).toBe('default'); expect(editor.name).toBe('Test Editor'); @@ -72,7 +72,7 @@ describe('ValueEditor', () => { ]); }); - it('should return пустую строку для getValueText когда нет values', () => { + it('should return an empty string for getValueText when there are no values', () => { const editor = new ValueEditor(); editor.id = 'test-editor'; @@ -80,7 +80,7 @@ describe('ValueEditor', () => { expect(result).toBe(''); }); - it('should return текст для строкового значения через getValueText', () => { + it('should return text for a string value through getValueText', () => { const editor = new ValueEditor(); editor.values = [ { id: '1', text: 'Option 1' }, @@ -92,7 +92,7 @@ describe('ValueEditor', () => { expect(result).toBe('Option 2'); }); - it('should return пустую строку для несуществующего значения через getValueText', () => { + it('should return an empty string for a non-existent value through getValueText', () => { const editor = new ValueEditor(); editor.values = [ { id: '1', text: 'Option 1' }, @@ -103,7 +103,7 @@ describe('ValueEditor', () => { expect(result).toBe(''); }); - it('should объединять тексты для массива значений через getValueText', () => { + it('should concatenate texts for an array of values through getValueText', () => { const editor = new ValueEditor(); editor.values = [ { id: '1', text: 'Option 1' }, @@ -115,7 +115,7 @@ describe('ValueEditor', () => { expect(result).toBe('Option 1,Option 3'); }); - it('should return только найденные тексты для массива значений через getValueText', () => { + it('should return only found texts for an array of values through getValueText', () => { const editor = new ValueEditor(); editor.values = [ { id: '1', text: 'Option 1' }, @@ -127,7 +127,7 @@ describe('ValueEditor', () => { expect(result).toBe('Option 1,Option 3'); }); - it('should обрабатывать случай когда в values пустой массив', () => { + it('should handle the case when values is an empty array', () => { const editor = new ValueEditor(); editor.values = []; diff --git a/easydata.js/packs/crud/tests/entity_data_view.test.ts b/easydata.js/packs/crud/tests/entity_data_view.test.ts index 1cdbad57..b12f4583 100644 --- a/easydata.js/packs/crud/tests/entity_data_view.test.ts +++ b/easydata.js/packs/crud/tests/entity_data_view.test.ts @@ -194,7 +194,7 @@ describe('EntityDataView', () => { expect(backLink).toBeDefined(); expect(backLink.textContent).toBe('← Back to Entities'); - // Check, что обработчик клика установлен + // Check that the click handler is set const clickEvent = new MouseEvent('click'); const preventDefaultSpy = jest.spyOn(clickEvent, 'preventDefault'); backLink.dispatchEvent(clickEvent); @@ -206,7 +206,7 @@ describe('EntityDataView', () => { it('should not render back button if showBackToEntities=false', () => { view = new EntityDataView(mockSlot, mockContext, '/basePath', { showBackToEntities: false }); - // Check отсутствие кнопки возврата + // Check absence of the back button const backLink = mockSlot.querySelector('a'); expect(backLink).toBeNull(); }); From c3367e95520d53c24392b3ad18ce7b3ecd422d2d Mon Sep 17 00:00:00 2001 From: korzh Date: Sat, 19 Jul 2025 16:49:21 +0300 Subject: [PATCH 08/11] Update test files in crud package --- .../crud/tests/text_filter_widget.test.ts | 56 +++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/easydata.js/packs/crud/tests/text_filter_widget.test.ts b/easydata.js/packs/crud/tests/text_filter_widget.test.ts index ef023c37..58912bdf 100644 --- a/easydata.js/packs/crud/tests/text_filter_widget.test.ts +++ b/easydata.js/packs/crud/tests/text_filter_widget.test.ts @@ -178,7 +178,7 @@ describe('TextFilterWidget', () => { // Set значение input.value = 'test'; - // Emulate клик по кнопке + // Emulate button click button.click(); // Check apply call with correct value @@ -186,16 +186,16 @@ describe('TextFilterWidget', () => { }); it('should clear input field when clear icon is clicked', () => { - // Get поле ввода и иконку очистки + // Get input field and clear icon const input = mockSlot.querySelector('input') as HTMLInputElement; const clearIcon = mockSlot.querySelector('span.icon') as HTMLSpanElement; - // Set значение и фокус мок + // Set value and focus mock input.value = 'test'; const focusMock = mock(); input.focus = focusMock; - // Emulate клик по иконке очистки + // Emulate clear icon click clearIcon.click(); // Check value clearing @@ -209,13 +209,13 @@ describe('TextFilterWidget', () => { }); it('should apply filter with delay in instantMode on input', () => { - // Remove предыдущий виджет + // Remove previous widget mockSlot.innerHTML = ''; // Mock for setTimeout jest.useFakeTimers(); - // Создаем новый виджет с instantMode и маленьким таймаутом + // Create new widget with instantMode and small timeout filterWidget = new TextFilterWidget(mockSlot, mockGrid, mockFilter, { instantMode: true, instantTimeout: 500 @@ -280,16 +280,16 @@ describe('TextFilterWidget', () => { }); it('applyFilter method should return true if filter value changed', () => { - // Get поле ввода + // Get input field const input = mockSlot.querySelector('input') as HTMLInputElement; - // Set значение + // Set value input.value = 'test'; - // Mock for getValue, возвращающий другое значение + // Mock for getValue returning different value (mockFilter.getValue as jest.Mock).mockReturnValue('old'); - // Call метод и проверяем результат + // Call method and check result const result = filterWidget.applyFilter(true); expect(result).toBe(true); @@ -304,10 +304,10 @@ describe('TextFilterWidget', () => { // Set значение input.value = 'test'; - // Mock for getValue, возвращающий то же самое значение + // Mock for getValue returning the same value (mockFilter.getValue as jest.Mock).mockReturnValue('test'); - // Call метод и проверяем результат + // Call method and check result const result = filterWidget.applyFilter(true); expect(result).toBe(false); @@ -319,13 +319,13 @@ describe('TextFilterWidget', () => { // Mock for getValue, возвращающий искомое слово (mockFilter.getValue as jest.Mock).mockReturnValue('test'); - // Call highlightText напрямую через приватный метод + // Call highlightText directly through private method const result = (filterWidget as any).highlightText('This is a test string'); - // Check типа результата + // Check result type expect(result instanceof HTMLElement).toBe(true); - // Check содержимого + // Check content const div = result as HTMLElement; // Should have 3 child elements: text, span with highlight, text @@ -358,25 +358,25 @@ describe('TextFilterWidget', () => { // Should have 5 child elements: span, text, span, text expect(div.childNodes.length).toBe(3); - // Check первого выделения + // Check first highlight const firstSpan = div.childNodes[0] as HTMLSpanElement; expect(firstSpan.tagName).toBe('SPAN'); expect(firstSpan.textContent).toBe('test'); - // Check текста между выделениями + // Check text between highlights expect(div.childNodes[1].textContent).toBe(' another '); - // Check второго выделения + // Check second highlight const secondSpan = div.childNodes[2] as HTMLSpanElement; expect(secondSpan.tagName).toBe('SPAN'); expect(secondSpan.textContent).toBe('test'); }); it('should correctly handle multiple search words with || separator', () => { - // Mock for getValue, возвращающий несколько слов через разделитель + // Mock for getValue returning multiple words with separator (mockFilter.getValue as jest.Mock).mockReturnValue('apple || banana'); - // Call highlightText напрямую через приватный метод + // Call highlightText directly through private method const result = (filterWidget as any).highlightText('I have an apple and a banana'); // Check типа результата @@ -404,10 +404,10 @@ describe('TextFilterWidget', () => { }); it('should highlight entire cell if content fully matches the filter', () => { - // Mock for getValue, возвращающий полное значение ячейки + // Mock for getValue returning the full cell value (mockFilter.getValue as jest.Mock).mockReturnValue('exact match'); - // Call highlightText напрямую через приватный метод + // Call highlightText directly through private method const result = (filterWidget as any).highlightText('exact match'); // Should return one span highlighting all content @@ -417,10 +417,10 @@ describe('TextFilterWidget', () => { }); it('should return original text if no matches', () => { - // Mock for getValue, возвращающий слово, которого нет в тексте + // Mock for getValue returning word that is not in text (mockFilter.getValue as jest.Mock).mockReturnValue('missing'); - // Call highlightText напрямую через приватный метод + // Call highlightText directly through private method const result = (filterWidget as any).highlightText('This is a test string'); // Should return original string @@ -457,12 +457,12 @@ describe('TextFilterWidget', () => { // Call пользовательский рендерер customStringRenderer('This is a test', column, cellElement, rowElement); - // Check, что ячейка содержит выделенный текст + // Check that cell contains highlighted text expect(cellElement.innerHTML).not.toBe(''); expect(cellElement.querySelector('span[style*="yellow"]')).toBeDefined(); }); - it('should использовать пользовательский рендерер для числовых ячеек', () => { + it('should use custom renderer for numeric cells', () => { // Create mocks for параметров рендерера const column: GridColumn = { dataColumn: { id: 'value' }, @@ -481,10 +481,10 @@ describe('TextFilterWidget', () => { // Mock for toLocaleString Number.prototype.toLocaleString = function() { return this.toString(); }; - // Call пользовательский рендерер + // Call custom renderer customNumberRenderer(42, column, cellElement, rowElement); - // Check, что ячейка содержит выделенный текст + // Check that cell contains highlighted text expect(cellElement.innerHTML).not.toBe(''); expect(cellElement.querySelector('span[style*="yellow"]')).toBeDefined(); }); From e9dc8368e60acf8819299d15863394fe5dbb26af Mon Sep 17 00:00:00 2001 From: korzh Date: Sat, 19 Jul 2025 19:46:02 +0300 Subject: [PATCH 09/11] Finish fixing comments in core tests --- .../packs/core/tests/meta_entity.test.ts | 10 ++-- .../crud/tests/entity_form_builder.test.ts | 18 +++--- .../packs/crud/tests/root_data_view.test.ts | 60 +++++++++---------- .../packs/crud/tests/text_data_filter.test.ts | 46 +++++++------- 4 files changed, 67 insertions(+), 67 deletions(-) diff --git a/easydata.js/packs/core/tests/meta_entity.test.ts b/easydata.js/packs/core/tests/meta_entity.test.ts index ca6df022..34400190 100644 --- a/easydata.js/packs/core/tests/meta_entity.test.ts +++ b/easydata.js/packs/core/tests/meta_entity.test.ts @@ -168,11 +168,11 @@ describe('MetaEntity', () => { } ); - // Должен посетить только корневую сущность и первый атрибут + // Should visit only the root entity and the first attribute expect(entityCount).toBe(1); expect(attrCount).toBe(1); - // Повторное сканирование полностью + // Full re-scan entityCount = 0; attrCount = 0; @@ -185,9 +185,9 @@ describe('MetaEntity', () => { } ); - // Должен посетить все сущности и атрибуты - expect(entityCount).toBe(2); // root и child - expect(attrCount).toBe(2); // attr1 и attr2 + // Should visit all entities and attributes + expect(entityCount).toBe(2); // root and child + expect(attrCount).toBe(2); // attr1 and attr2 }); it('should find primary attributes', () => { diff --git a/easydata.js/packs/crud/tests/entity_form_builder.test.ts b/easydata.js/packs/crud/tests/entity_form_builder.test.ts index ecfec438..58bc9400 100644 --- a/easydata.js/packs/crud/tests/entity_form_builder.test.ts +++ b/easydata.js/packs/crud/tests/entity_form_builder.test.ts @@ -41,7 +41,7 @@ describe('EntityEditFormBuilder', () => { }; beforeEach(() => { - // Create attributes для нашей сущности + // Create attributes for our entity const attributes = [ createAttr('Person.id', 'ID', DataType.Int32, { isPrimaryKey: true, @@ -93,7 +93,7 @@ describe('EntityEditFormBuilder', () => { attributes } as MetaEntity; - // Создаем Department сущность для lookup + // Create Department entity for lookup const departmentEntity = { id: 'Department', name: 'Department', @@ -105,17 +105,17 @@ describe('EntityEditFormBuilder', () => { getFirstPrimaryAttr: () => departmentEntity.attributes[0] } as MetaEntity; - // Создаем метаданные с нашими сущностями + // Create metadata with our entities mockMetaData = { getRootEntity: () => ({ subEntities: [mockEntity, departmentEntity] }), getAttributeById: (id: string) => { - // Поиск в атрибутах Person + // Search in Person attributes const personAttr = mockEntity.attributes.find(attr => attr.id === id); if (personAttr) return personAttr; - // Поиск в атрибутах Department + // Search in Department attributes const deptAttr = departmentEntity.attributes.find(attr => attr.id === id); if (deptAttr) return deptAttr; @@ -123,7 +123,7 @@ describe('EntityEditFormBuilder', () => { } } as MetaData; - // Создаем объект для представления данных строки + // Create object for representing row data const rowData = { 'Person.id': 1, 'Person.name': 'John Doe', @@ -135,12 +135,12 @@ describe('EntityEditFormBuilder', () => { 'Person.departmentId': 2 }; - // Создаем макет для DataRow + // Create mock for DataRow dataRow = { getValue: (id: string) => rowData[id] } as DataRow; - // Create mock for контекста данных + // Create mock for data context mockDataContext = { getMetaData: () => mockMetaData, getActiveEntity: () => mockEntity, @@ -165,7 +165,7 @@ describe('EntityEditFormBuilder', () => { it('should be created with empty parameters and reset to default values', () => { expect(builder).toBeDefined(); - // Check, что свойство context установлено + // Check that context property is set expect((builder as any).context).toBe(mockDataContext); // Check метод reset diff --git a/easydata.js/packs/crud/tests/root_data_view.test.ts b/easydata.js/packs/crud/tests/root_data_view.test.ts index 65401f34..fe15e7b7 100644 --- a/easydata.js/packs/crud/tests/root_data_view.test.ts +++ b/easydata.js/packs/crud/tests/root_data_view.test.ts @@ -4,14 +4,14 @@ import { RootDataView } from '../src/views/root_data_view'; import * as utils from '../src/utils/utils'; describe('RootDataView', () => { - // Mocks for DOM и объектов + // Mocks for DOM and objects let mockSlot: HTMLElement; let mockContext: DataContext; let mockMetaData: MetaData; let mockRootEntity: MetaEntity; let view: RootDataView; - // Тестовые сущности + // Test entities const mockEntities = [ { id: 'entity1', @@ -41,18 +41,18 @@ describe('RootDataView', () => { mockSlot = document.createElement('div'); document.body.appendChild(mockSlot); - // Mock for корневой сущности + // Mock for root entity mockRootEntity = { subEntities: mockEntities } as unknown as MetaEntity; - // Mock for метаданных + // Mock for metadata mockMetaData = { getRootEntity: mock().mockReturnValue(mockRootEntity), isEmpty: mock().mockReturnValue(false) } as unknown as MetaData; - // Mock for контекста данных + // Mock for data context mockContext = { getMetaData: mock().mockReturnValue(mockMetaData) } as unknown as DataContext; @@ -65,7 +65,7 @@ describe('RootDataView', () => { return key; }); - // Mock for функции setLocation + // Mock for setLocation function jest.spyOn(utils, 'setLocation').mockImplementation(() => {}); }); @@ -75,52 +75,52 @@ describe('RootDataView', () => { mockSlot.parentNode.removeChild(mockSlot); } - // Reset моки + // Reset mocks jest.restoreAllMocks(); }); - it('should создаваться с правильными настройками по умолчанию', () => { + it('should be created with correct default settings', () => { view = new RootDataView(mockSlot, mockContext, '/basePath'); // Check that context and metadata are set expect((view as any).context).toBe(mockContext); expect((view as any).metaData).toBe(mockMetaData); - // Check опции по умолчанию + // Check default options const options = (view as any).options; expect(options).toBeObject(); expect(options.usePluralNames).toBe(true); }); - it('should применять пользовательские настройки', () => { + it('should apply custom settings', () => { view = new RootDataView(mockSlot, mockContext, '/basePath', { usePluralNames: false }); - // Check пользовательские опции + // Check custom options const options = (view as any).options; expect(options).toBeObject(); expect(options.usePluralNames).toBe(false); }); - it('should рендерить заголовок', () => { + it('should render title', () => { view = new RootDataView(mockSlot, mockContext, '/basePath'); - // Check, что заголовок содержит текст + // Check that title contains text const header = mockSlot.querySelector('h1'); expect(header).toBeDefined(); expect(header.textContent).toBe('Entities'); }); - it('should рендерить список сущностей', () => { + it('should render entity list', () => { view = new RootDataView(mockSlot, mockContext, '/basePath'); - // Check, что есть описание меню + // Check that menu description exists const menuDescription = mockSlot.querySelector('.ed-menu-description'); expect(menuDescription).toBeDefined(); expect(menuDescription.textContent).toBe('Select an entity from the list below'); - // Check, что есть список сущностей + // Check that entity list exists const entityMenu = mockSlot.querySelector('.ed-entity-menu'); expect(entityMenu).toBeDefined(); @@ -129,73 +129,73 @@ describe('RootDataView', () => { expect(entityItems.length).toBe(3); }); - it('should использовать множественные имена сущностей, когда usePluralNames=true', () => { + it('should use plural entity names when usePluralNames=true', () => { view = new RootDataView(mockSlot, mockContext, '/basePath', { usePluralNames: true }); - // Check, что используются множественные имена + // Check that plural names are used const entityItems = mockSlot.querySelectorAll('.ed-entity-item-caption'); expect(entityItems[0].textContent).toBe('Customers'); expect(entityItems[1].textContent).toBe('Products'); expect(entityItems[2].textContent).toBe('Orders'); }); - it('should использовать обычные имена сущностей, когда usePluralNames=false', () => { + it('should use regular entity names when usePluralNames=false', () => { view = new RootDataView(mockSlot, mockContext, '/basePath', { usePluralNames: false }); - // Check, что используются обычные имена + // Check that regular names are used const entityItems = mockSlot.querySelectorAll('.ed-entity-item-caption'); expect(entityItems[0].textContent).toBe('Customer'); expect(entityItems[1].textContent).toBe('Product'); expect(entityItems[2].textContent).toBe('Order'); }); - it('should отображать описания сущностей', () => { + it('should display entity descriptions', () => { view = new RootDataView(mockSlot, mockContext, '/basePath'); - // Check, что описания отображаются + // Check that descriptions are displayed const descriptionItems = mockSlot.querySelectorAll('.ed-entity-item-descr'); - // Должно быть 2 описания (у entity2 нет описания) + // Should be 2 descriptions (entity2 has no description) expect(descriptionItems.length).toBe(2); expect(descriptionItems[0].textContent).toBe('Customer entity description'); expect(descriptionItems[1].textContent).toBe('Order entity description'); }); - it('should обрабатывать клик по сущности', () => { + it('should handle entity click', () => { view = new RootDataView(mockSlot, mockContext, '/basePath'); // Get first list element const firstEntityItem = mockSlot.querySelector('.ed-entity-item'); - // Emulate клик + // Emulate click firstEntityItem.click(); - // Check, что setLocation был вызван с правильными параметрами + // Check that setLocation was called with correct parameters expect(utils.setLocation).toHaveBeenCalledWith('/basePath/entity1'); }); - it('should отображать сообщение, если модель пуста', () => { + it('should display message if model is empty', () => { // Change mock so metadata is empty (mockMetaData.isEmpty as jest.Mock).mockReturnValue(true); (mockRootEntity.subEntities as any) = []; view = new RootDataView(mockSlot, mockContext, '/basePath'); - // Check, что отображается сообщение о пустой модели + // Check that empty model message is displayed const menuDescription = mockSlot.querySelector('.ed-menu-description'); expect(menuDescription).toBeDefined(); expect(menuDescription.textContent).toBe('The model is empty'); - // Check, что список сущностей пуст + // Check that entity list is empty const entityItems = mockSlot.querySelectorAll('.ed-entity-item'); expect(entityItems.length).toBe(0); }); - it('should correctly декодировать ID сущностей при навигации', () => { + it('should correctly decode entity IDs during navigation', () => { // Create entity с ID, требующим кодирования const encodedEntity = { id: 'entity%20with%20space', diff --git a/easydata.js/packs/crud/tests/text_data_filter.test.ts b/easydata.js/packs/crud/tests/text_data_filter.test.ts index 37733b91..b6eb3c26 100644 --- a/easydata.js/packs/crud/tests/text_data_filter.test.ts +++ b/easydata.js/packs/crud/tests/text_data_filter.test.ts @@ -40,7 +40,7 @@ describe('TextDataFilter', () => { [5, 'Watermelon', 'Summer favorite', 5.99] ]; - // Создаем исходную таблицу данных + // Create source data table sourceTable = new EasyDataTable({ columns: columns, rows: tableData, @@ -50,7 +50,7 @@ describe('TextDataFilter', () => { // Create mock for DataLoader mockLoader = { loadChunk: (chunkInfo: ChunkInfo): Promise<{ table: EasyDataTable, total: number }> => { - // Emulate фильтрацию на сервере + // Emulate server-side filtering const filterValue = chunkInfo['filters']?.[0]?.value?.toLowerCase(); const filteredData = filterValue ? tableData.filter(row => { @@ -77,17 +77,17 @@ describe('TextDataFilter', () => { filter = new TextDataFilter(mockLoader, sourceTable, 'products'); }); - it('should быть экземпляром класса DataFilter', () => { + it('should be an instance of DataFilter class', () => { expect(filter).toBeInstanceOf(DataFilter); expect(filter).toBeObject(); }); - it('should return пустую строку для getValue() после создания', () => { + it('should return empty string for getValue() after creation', () => { const value = filter.getValue(); expect(value).toBe(''); }); - it('should correctly устанавливать и возвращать значение фильтра', () => { + it('should correctly set and return filter value', () => { return filter.apply('apple') .then(() => { const value = filter.getValue(); @@ -95,14 +95,14 @@ describe('TextDataFilter', () => { }); }); - it('should return исходную таблицу при пустом значении фильтра', () => { + it('should return original table with empty filter value', () => { return filter.apply('') .then(result => { expect(result).toBe(sourceTable); }); }); - it('should return исходную таблицу после очистки фильтра', () => { + it('should return original table after clearing filter', () => { return filter.apply('apple') .then(() => filter.clear()) .then(result => { @@ -111,7 +111,7 @@ describe('TextDataFilter', () => { }); }); - it('should фильтровать данные в памяти при полностью загруженной таблице', () => { + it('should filter data in memory when table is fully loaded', () => { return filter.apply('apple') .then(filteredTable => { expect(filteredTable).not.toBe(sourceTable); @@ -121,49 +121,49 @@ describe('TextDataFilter', () => { expect(rows).toBeArray(); expect(rows.length).toBe(2); - // Check первую строку (Apple) + // Check first row (Apple) expect(rows[0].getValue('name')).toBe('Apple'); - // Check вторую строку (Pineapple) + // Check second row (Pineapple) expect(rows[1].getValue('name')).toBe('Pineapple'); }); }); - it('should использовать серверную фильтрацию когда таблица не полностью загружена', () => { - // Создаем частично загруженную таблицу и новый фильтр + it('should use server-side filtering when table is not fully loaded', () => { + // Create partially loaded table and new filter const partialTable = new EasyDataTable({ columns: columns, loader: mockLoader }); - // Добавляем только часть данных, имитируя неполную загрузку + // Add only part of data, simulating incomplete loading partialTable.addRow(tableData[0]); partialTable.addRow(tableData[1]); - partialTable.setTotal(tableData.length); // Общее количество записей больше, чем закешировано + partialTable.setTotal(tableData.length); // Total record count is greater than cached const serverFilter = new TextDataFilter(mockLoader, partialTable, 'products'); - // Шпионим за методом loadChunk у mockLoader + // Spy on loadChunk method of mockLoader const loadChunkSpy = jest.spyOn(mockLoader, 'loadChunk'); return serverFilter.apply('orange') .then(filteredTable => { - // Check, что был вызван метод loadChunk + // Check that loadChunk method was called expect(loadChunkSpy).toHaveBeenCalled(); - // Check, что фильтр был передан в запрос + // Check that filter was passed in request const callArgs = loadChunkSpy.mock.calls[0][0]; expect(callArgs).toBeObject(); expect(callArgs.filters).toBeArray(); expect(callArgs.filters[0].value).toBe('orange'); - // Check результаты фильтрации + // Check filtering results expect(filteredTable.getCachedCount()).toBe(1); expect(filteredTable.getCachedRows()[0].getValue('name')).toBe('Orange'); }); }); - it('should поддерживать множественные поисковые слова через разделитель ||', () => { + it('should support multiple search words through || separator', () => { return filter.apply('apple || melon') .then(filteredTable => { const rows = filteredTable.getCachedRows(); @@ -177,7 +177,7 @@ describe('TextDataFilter', () => { }); }); - it('should фильтровать по нескольким колонкам', () => { + it('should filter by multiple columns', () => { return filter.apply('fruit') .then(filteredTable => { const rows = filteredTable.getCachedRows(); @@ -191,7 +191,7 @@ describe('TextDataFilter', () => { }); }); - it('should фильтровать без учета регистра', () => { + it('should filter case-insensitively', () => { return filter.apply('APPLE') .then(filteredTable => { const rows = filteredTable.getCachedRows(); @@ -204,7 +204,7 @@ describe('TextDataFilter', () => { }); }); - it('should return пустую таблицу если нет соответствий', () => { + it('should return empty table if no matches found', () => { return filter.apply('nonexistent') .then(filteredTable => { expect(filteredTable.getCachedCount()).toBe(0); @@ -212,7 +212,7 @@ describe('TextDataFilter', () => { }); }); - it('should фильтровать по числовым значениям', () => { + it('should filter by numeric values', () => { return filter.apply('1.99') .then(filteredTable => { const rows = filteredTable.getCachedRows(); From 3cef2467e72d20056c9175a83bb1a6471a480854 Mon Sep 17 00:00:00 2001 From: korzh Date: Sat, 19 Jul 2025 20:31:20 +0300 Subject: [PATCH 10/11] Update comments in tests part 6 --- .../crud/tests/entity_form_builder.test.ts | 28 +++---- .../packs/crud/tests/root_data_view.test.ts | 62 +++++++-------- .../packs/crud/tests/text_data_filter.test.ts | 2 +- .../crud/tests/text_filter_widget.test.ts | 78 +++++++++---------- 4 files changed, 85 insertions(+), 85 deletions(-) diff --git a/easydata.js/packs/crud/tests/entity_form_builder.test.ts b/easydata.js/packs/crud/tests/entity_form_builder.test.ts index ecfec438..e6079402 100644 --- a/easydata.js/packs/crud/tests/entity_form_builder.test.ts +++ b/easydata.js/packs/crud/tests/entity_form_builder.test.ts @@ -183,40 +183,40 @@ describe('EntityEditFormBuilder', () => { // Check что форма создана expect(form).toBeInstanceOf(EntityEditForm); - // Get HTML формы + // Get form HTML const formHtml = form.getHtml(); expect(formHtml).toBeDefined(); - // Check наличие блока для ошибок + // Check presence of error block const errorsBlock = formHtml.querySelector('.errors-block'); expect(errorsBlock).toBeDefined(); - // Check наличие полей формы + // Check presence of form fields const inputs = formHtml.querySelectorAll('input'); expect(inputs.length).toBeGreaterThan(0); - // Check что каждый атрибут создал соответствующее поле + // Check that each attribute created corresponding field for (const attr of mockEntity.attributes) { - // Пропустить атрибуты, которые не должны отображаться в форме создания + // Skip attributes that should not be displayed in create form if (!attr.showOnCreate) continue; - // Найти поле по имени + // Find field by name const field = formHtml.querySelector(`[name="${attr.id}"]`); expect(field).toBeDefined(); - // Проверить label для поля + // Check label for field const label = formHtml.querySelector(`label[for="${attr.id}"]`); expect(label).toBeDefined(); expect(label.textContent).toContain(attr.caption); - // Проверить обязательные поля + // Check required fields if (!attr.isNullable) { expect(label.innerHTML).toContain('*'); } } }); - it('should устанавливать значения из dataRow при создании формы', () => { + it('should set values from dataRow when creating form', () => { const params: FormBuildParams = { values: dataRow }; @@ -225,7 +225,7 @@ describe('EntityEditFormBuilder', () => { const form = builder.build(); const formHtml = form.getHtml(); - // Check заполнение полей значениями из dataRow + // Check field population with values from dataRow const nameInput = formHtml.querySelector('[name="Person.name"]') as HTMLInputElement; expect(nameInput.value).toBe('John Doe'); @@ -272,21 +272,21 @@ describe('EntityEditFormBuilder', () => { const nameInput = formHtml.querySelector('[name="Person.name"]') as HTMLInputElement; expect(nameInput.type).toBe('text'); - // Check поля checkbox + // Check checkbox field const isActiveInput = formHtml.querySelector('[name="Person.isActive"]') as HTMLInputElement; expect(isActiveInput.type).toBe('checkbox'); - // Check поля file + // Check file field const photoInput = formHtml.querySelector('[name="Person.photo"]') as HTMLInputElement; expect(photoInput.type).toBe('file'); expect(photoInput.getAttribute('accept')).toBe('image/*'); }); - it('should создавать текстовую область для многострочных редакторов', () => { + it('should create text area for multiline editors', () => { const form = builder.build(); const formHtml = form.getHtml(); - // Check textarea для многострочных полей + // Check textarea for multiline fields const notesTextarea = formHtml.querySelector('[name="Person.notes"]') as HTMLTextAreaElement; expect(notesTextarea).toBeDefined(); expect(notesTextarea.tagName.toLowerCase()).toBe('textarea'); diff --git a/easydata.js/packs/crud/tests/root_data_view.test.ts b/easydata.js/packs/crud/tests/root_data_view.test.ts index 65401f34..a5b65ffa 100644 --- a/easydata.js/packs/crud/tests/root_data_view.test.ts +++ b/easydata.js/packs/crud/tests/root_data_view.test.ts @@ -41,18 +41,18 @@ describe('RootDataView', () => { mockSlot = document.createElement('div'); document.body.appendChild(mockSlot); - // Mock for корневой сущности + // Mock for root entity mockRootEntity = { subEntities: mockEntities } as unknown as MetaEntity; - // Mock for метаданных + // Mock for metadata mockMetaData = { getRootEntity: mock().mockReturnValue(mockRootEntity), isEmpty: mock().mockReturnValue(false) } as unknown as MetaData; - // Mock for контекста данных + // Mock for data context mockContext = { getMetaData: mock().mockReturnValue(mockMetaData) } as unknown as DataContext; @@ -65,7 +65,7 @@ describe('RootDataView', () => { return key; }); - // Mock for функции setLocation + // Mock for setLocation function jest.spyOn(utils, 'setLocation').mockImplementation(() => {}); }); @@ -75,52 +75,52 @@ describe('RootDataView', () => { mockSlot.parentNode.removeChild(mockSlot); } - // Reset моки + // Reset mocks jest.restoreAllMocks(); }); - it('should создаваться с правильными настройками по умолчанию', () => { + it('should be created with correct default settings', () => { view = new RootDataView(mockSlot, mockContext, '/basePath'); // Check that context and metadata are set expect((view as any).context).toBe(mockContext); expect((view as any).metaData).toBe(mockMetaData); - // Check опции по умолчанию + // Check default options const options = (view as any).options; expect(options).toBeObject(); expect(options.usePluralNames).toBe(true); }); - it('should применять пользовательские настройки', () => { + it('should apply custom settings', () => { view = new RootDataView(mockSlot, mockContext, '/basePath', { usePluralNames: false }); - // Check пользовательские опции + // Check custom options const options = (view as any).options; expect(options).toBeObject(); expect(options.usePluralNames).toBe(false); }); - it('should рендерить заголовок', () => { + it('should render title', () => { view = new RootDataView(mockSlot, mockContext, '/basePath'); - // Check, что заголовок содержит текст + // Check that header contains text const header = mockSlot.querySelector('h1'); expect(header).toBeDefined(); expect(header.textContent).toBe('Entities'); }); - it('should рендерить список сущностей', () => { + it('should render list of entities', () => { view = new RootDataView(mockSlot, mockContext, '/basePath'); - // Check, что есть описание меню + // Check that menu description exists const menuDescription = mockSlot.querySelector('.ed-menu-description'); expect(menuDescription).toBeDefined(); expect(menuDescription.textContent).toBe('Select an entity from the list below'); - // Check, что есть список сущностей + // Check that entity list exists const entityMenu = mockSlot.querySelector('.ed-entity-menu'); expect(entityMenu).toBeDefined(); @@ -129,56 +129,56 @@ describe('RootDataView', () => { expect(entityItems.length).toBe(3); }); - it('should использовать множественные имена сущностей, когда usePluralNames=true', () => { + it('should use plural entity names when usePluralNames=true', () => { view = new RootDataView(mockSlot, mockContext, '/basePath', { usePluralNames: true }); - // Check, что используются множественные имена + // Check that plural names are used const entityItems = mockSlot.querySelectorAll('.ed-entity-item-caption'); expect(entityItems[0].textContent).toBe('Customers'); expect(entityItems[1].textContent).toBe('Products'); expect(entityItems[2].textContent).toBe('Orders'); }); - it('should использовать обычные имена сущностей, когда usePluralNames=false', () => { + it('should use regular entity names when usePluralNames=false', () => { view = new RootDataView(mockSlot, mockContext, '/basePath', { usePluralNames: false }); - // Check, что используются обычные имена + // Check that regular names are used const entityItems = mockSlot.querySelectorAll('.ed-entity-item-caption'); expect(entityItems[0].textContent).toBe('Customer'); expect(entityItems[1].textContent).toBe('Product'); expect(entityItems[2].textContent).toBe('Order'); }); - it('should отображать описания сущностей', () => { + it('should display entity descriptions', () => { view = new RootDataView(mockSlot, mockContext, '/basePath'); - // Check, что описания отображаются + // Check that descriptions are displayed const descriptionItems = mockSlot.querySelectorAll('.ed-entity-item-descr'); - // Должно быть 2 описания (у entity2 нет описания) + // Should be 2 descriptions (entity2 has no description) expect(descriptionItems.length).toBe(2); expect(descriptionItems[0].textContent).toBe('Customer entity description'); expect(descriptionItems[1].textContent).toBe('Order entity description'); }); - it('should обрабатывать клик по сущности', () => { + it('should handle entity click', () => { view = new RootDataView(mockSlot, mockContext, '/basePath'); // Get first list element const firstEntityItem = mockSlot.querySelector('.ed-entity-item'); - // Emulate клик + // Emulate click firstEntityItem.click(); - // Check, что setLocation был вызван с правильными параметрами + // Check that setLocation was called with correct parameters expect(utils.setLocation).toHaveBeenCalledWith('/basePath/entity1'); }); - it('should отображать сообщение, если модель пуста', () => { + it('should display message if model is empty', () => { // Change mock so metadata is empty (mockMetaData.isEmpty as jest.Mock).mockReturnValue(true); (mockRootEntity.subEntities as any) = []; @@ -195,8 +195,8 @@ describe('RootDataView', () => { expect(entityItems.length).toBe(0); }); - it('should correctly декодировать ID сущностей при навигации', () => { - // Create entity с ID, требующим кодирования + it('should correctly decode entity IDs during navigation', () => { + // Create entity with ID requiring encoding const encodedEntity = { id: 'entity%20with%20space', name: 'EncodedEntity', @@ -205,18 +205,18 @@ describe('RootDataView', () => { description: null }; - // Добавляем эту сущность в список + // Add this entity to the list (mockRootEntity.subEntities as any) = [encodedEntity]; view = new RootDataView(mockSlot, mockContext, '/basePath'); - // Get элемент списка + // Get list element const entityItem = mockSlot.querySelector('.ed-entity-item'); - // Emulate клик + // Emulate click entityItem.click(); - // Check, что setLocation был вызван с декодированным ID + // Check that setLocation was called with decoded ID expect(utils.setLocation).toHaveBeenCalledWith('/basePath/entity with space'); }); }); diff --git a/easydata.js/packs/crud/tests/text_data_filter.test.ts b/easydata.js/packs/crud/tests/text_data_filter.test.ts index 37733b91..0a8a8ae7 100644 --- a/easydata.js/packs/crud/tests/text_data_filter.test.ts +++ b/easydata.js/packs/crud/tests/text_data_filter.test.ts @@ -212,7 +212,7 @@ describe('TextDataFilter', () => { }); }); - it('should фильтровать по числовым значениям', () => { + it('should filter by numeric values', () => { return filter.apply('1.99') .then(filteredTable => { const rows = filteredTable.getCachedRows(); diff --git a/easydata.js/packs/crud/tests/text_filter_widget.test.ts b/easydata.js/packs/crud/tests/text_filter_widget.test.ts index 58912bdf..10fafe04 100644 --- a/easydata.js/packs/crud/tests/text_filter_widget.test.ts +++ b/easydata.js/packs/crud/tests/text_filter_widget.test.ts @@ -107,10 +107,10 @@ describe('TextFilterWidget', () => { }); it('should render HTML without search button in instantMode', () => { - // Remove предыдущий виджет + // Remove previous widget mockSlot.innerHTML = ''; - // Создаем новый виджет с instantMode + // Create new widget with instantMode filterWidget = new TextFilterWidget(mockSlot, mockGrid, mockFilter, { instantMode: true }); @@ -125,13 +125,13 @@ describe('TextFilterWidget', () => { }); it('should use different classes for IE', () => { - // Remove предыдущий виджет + // Remove previous widget mockSlot.innerHTML = ''; // Change mock for IE (browserUtils.isIE as jest.Mock).mockReturnValue(true); - // Создаем новый виджет + // Create new widget filterWidget = new TextFilterWidget(mockSlot, mockGrid, mockFilter); // Check IE class presence @@ -139,14 +139,14 @@ describe('TextFilterWidget', () => { }); it('should set focus on input field if focus=true option is set', () => { - // Remove предыдущий виджет + // Remove previous widget mockSlot.innerHTML = ''; // Mock for focus const focusMock = mock(); HTMLInputElement.prototype.focus = focusMock; - // Создаем новый виджет с опцией focus=true + // Create new widget with focus=true option filterWidget = new TextFilterWidget(mockSlot, mockGrid, mockFilter, { focus: true }); @@ -156,13 +156,13 @@ describe('TextFilterWidget', () => { }); it('should apply filter when Enter is pressed', () => { - // Get поле ввода + // Get input field const input = mockSlot.querySelector('input') as HTMLInputElement; - // Set значение + // Set value input.value = 'test'; - // Emulate нажатие Enter + // Emulate Enter key press const enterEvent = new KeyboardEvent('keydown', { keyCode: 13 }); input.dispatchEvent(enterEvent); @@ -171,11 +171,11 @@ describe('TextFilterWidget', () => { }); it('should apply filter when Search button is pressed', () => { - // Get поле ввода и кнопку + // Get input field and button const input = mockSlot.querySelector('input') as HTMLInputElement; const button = mockSlot.querySelector('button') as HTMLButtonElement; - // Set значение + // Set value input.value = 'test'; // Emulate button click @@ -221,17 +221,17 @@ describe('TextFilterWidget', () => { instantTimeout: 500 }); - // Get поле ввода + // Get input field const input = mockSlot.querySelector('input') as HTMLInputElement; - // Set значение и эмулируем keyup + // Set value and emulate keyup input.value = 'test'; input.dispatchEvent(new KeyboardEvent('keyup')); // Check that apply is not called immediately expect(mockFilter.apply).not.toHaveBeenCalled(); - // Проматываем таймеры + // Advance timers jest.advanceTimersByTime(500); // Check apply call with correct value @@ -242,23 +242,23 @@ describe('TextFilterWidget', () => { }); it('should clear previous timer on new keyup event', () => { - // Remove предыдущий виджет + // Remove previous widget mockSlot.innerHTML = ''; - // Mock for setTimeout и clearTimeout + // Mock for setTimeout and clearTimeout jest.useFakeTimers(); const clearTimeoutSpy = jest.spyOn(window, 'clearTimeout'); - // Создаем новый виджет с instantMode + // Create new widget with instantMode filterWidget = new TextFilterWidget(mockSlot, mockGrid, mockFilter, { instantMode: true, instantTimeout: 500 }); - // Get поле ввода + // Get input field const input = mockSlot.querySelector('input') as HTMLInputElement; - // Первый ввод + // First input input.value = 'test'; input.dispatchEvent(new KeyboardEvent('keyup')); @@ -298,10 +298,10 @@ describe('TextFilterWidget', () => { }); it('applyFilter method should return false if filter value did not change', () => { - // Get поле ввода + // Get input field const input = mockSlot.querySelector('input') as HTMLInputElement; - // Set значение + // Set value input.value = 'test'; // Mock for getValue returning the same value @@ -316,7 +316,7 @@ describe('TextFilterWidget', () => { }); it('should correctly highlight text matching the filter', () => { - // Mock for getValue, возвращающий искомое слово + // Mock for getValue returning search word (mockFilter.getValue as jest.Mock).mockReturnValue('test'); // Call highlightText directly through private method @@ -331,7 +331,7 @@ describe('TextFilterWidget', () => { // Should have 3 child elements: text, span with highlight, text expect(div.childNodes.length).toBe(3); - // Check правильного выделения + // Check correct highlighting expect(div.childNodes[0].textContent).toBe('This is a '); const highlightSpan = div.childNodes[1] as HTMLSpanElement; @@ -343,16 +343,16 @@ describe('TextFilterWidget', () => { }); it('should correctly highlight multiple matches', () => { - // Mock for getValue, возвращающий искомое слово + // Mock for getValue returning search word (mockFilter.getValue as jest.Mock).mockReturnValue('test'); - // Call highlightText напрямую через приватный метод + // Call highlightText directly through private method const result = (filterWidget as any).highlightText('test another test'); - // Check типа результата + // Check result type expect(result instanceof HTMLElement).toBe(true); - // Check содержимого + // Check content const div = result as HTMLElement; // Should have 5 child elements: span, text, span, text @@ -379,10 +379,10 @@ describe('TextFilterWidget', () => { // Call highlightText directly through private method const result = (filterWidget as any).highlightText('I have an apple and a banana'); - // Check типа результата + // Check result type expect(result instanceof HTMLElement).toBe(true); - // Check содержимого + // Check content const div = result as HTMLElement; // Should contain highlights for both words @@ -428,18 +428,18 @@ describe('TextFilterWidget', () => { }); it('should return original text if filter is empty', () => { - // Mock for getValue, возвращающий пустую строку + // Mock for getValue returning empty string (mockFilter.getValue as jest.Mock).mockReturnValue(''); - // Call highlightText напрямую через приватный метод + // Call highlightText directly through private method const result = (filterWidget as any).highlightText('This is a test string'); // Should return original string expect(result).toBe('This is a test string'); }); - it('should использовать пользовательский рендерер для строковых ячеек', () => { - // Create mocks for параметров рендерера + it('should use custom renderer for string cells', () => { + // Create mocks for renderer parameters const column: GridColumn = { dataColumn: { id: 'name' }, type: 'string' @@ -447,14 +447,14 @@ describe('TextFilterWidget', () => { const cellElement = document.createElement('div'); const rowElement = document.createElement('tr'); - // Get установленный рендерер для строк + // Get custom renderer for strings const stringRendererCall = (mockCellRendererStore.setDefaultRenderer as jest.Mock).mock.calls[0]; const customStringRenderer = stringRendererCall[1]; - // Mock for getValue, чтобы строки выделялись + // Mock for getValue, so that strings are highlighted (mockFilter.getValue as jest.Mock).mockReturnValue('test'); - // Call пользовательский рендерер + // Call custom renderer customStringRenderer('This is a test', column, cellElement, rowElement); // Check that cell contains highlighted text @@ -463,7 +463,7 @@ describe('TextFilterWidget', () => { }); it('should use custom renderer for numeric cells', () => { - // Create mocks for параметров рендерера + // Create mocks for renderer parameters const column: GridColumn = { dataColumn: { id: 'value' }, type: 'number' @@ -471,11 +471,11 @@ describe('TextFilterWidget', () => { const cellElement = document.createElement('div'); const rowElement = document.createElement('tr'); - // Get установленный рендерер для чисел + // Get custom renderer for numbers const numberRendererCall = (mockCellRendererStore.setDefaultRenderer as jest.Mock).mock.calls[1]; const customNumberRenderer = numberRendererCall[1]; - // Mock for getValue, чтобы числа выделялись + // Mock for getValue to make numbers highlighted (mockFilter.getValue as jest.Mock).mockReturnValue('42'); // Mock for toLocaleString From bef02080c49ca29d05a344c91d984ccb939cbcf0 Mon Sep 17 00:00:00 2001 From: korzh Date: Sat, 19 Jul 2025 20:41:01 +0300 Subject: [PATCH 11/11] Fix all texts in tests in CRUD pack --- .../crud/tests/entity_form_builder.test.ts | 22 +++++++++---------- .../packs/crud/tests/text_data_filter.test.ts | 4 ++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/easydata.js/packs/crud/tests/entity_form_builder.test.ts b/easydata.js/packs/crud/tests/entity_form_builder.test.ts index 68830e92..05568f47 100644 --- a/easydata.js/packs/crud/tests/entity_form_builder.test.ts +++ b/easydata.js/packs/crud/tests/entity_form_builder.test.ts @@ -158,7 +158,7 @@ describe('EntityEditFormBuilder', () => { }) } as unknown as DataContext; - // Create instance builder'а + // Create instance of builder builder = new EntityEditFormBuilder(mockDataContext); }); @@ -168,7 +168,7 @@ describe('EntityEditFormBuilder', () => { // Check that context property is set expect((builder as any).context).toBe(mockDataContext); - // Check метод reset + // Check reset method const formBeforeReset = (builder as any).form; builder.reset(); const formAfterReset = (builder as any).form; @@ -180,7 +180,7 @@ describe('EntityEditFormBuilder', () => { it('should create form with correct fields', () => { const form = builder.build(); - // Check что форма создана + // Check that form is created expect(form).toBeInstanceOf(EntityEditForm); // Get form HTML @@ -248,7 +248,7 @@ describe('EntityEditFormBuilder', () => { expect(idInput).toBeDefined(); expect(idInput.getAttribute('readonly')).toBeDefined(); - // Check, что неизменяемые атрибуты имеют readonly + // Check that non-editable attributes have readonly const attrNonEditable = createAttr('Person.createdAt', 'Created At', DataType.DateTime, { isEditable: false, showOnEdit: true @@ -264,11 +264,11 @@ describe('EntityEditFormBuilder', () => { expect(createdAtInput.getAttribute('readonly')).toBeDefined(); }); - it('should создавать текстовые поля правильного типа', () => { + it('should create text fields of correct type', () => { const form = builder.build(); const formHtml = form.getHtml(); - // Check текстовые поля + // Check text fields const nameInput = formHtml.querySelector('[name="Person.name"]') as HTMLInputElement; expect(nameInput.type).toBe('text'); @@ -308,19 +308,19 @@ describe('EntityEditFormBuilder', () => { expect(options[1].value).toBe('Inactive'); }); - it('should создавать специальные поля для дат и времени', () => { + it('should create special fields for dates and times', () => { const form = builder.build(); const formHtml = form.getHtml(); - // Check поля для дат + // Check date fields const birthDateField = formHtml.querySelector('[name="Person.birthDate"]').closest('.kfrm-fields, .kfrm-fields-ie'); expect(birthDateField).toBeDefined(); - // Check наличие кнопки с календарем + // Check presence of calendar button const calendarButton = birthDateField.querySelector('button'); expect(calendarButton).toBeDefined(); - // Check наличие иконки календаря + // Check presence of calendar icon const calendarIcon = calendarButton.querySelector('i.ed-calendar-icon'); expect(calendarIcon).toBeDefined(); }); @@ -343,7 +343,7 @@ describe('EntityEditFormBuilder', () => { const form = builder.build(); const formHtml = form.getHtml(); - // Check наличие подсказки для поля с описанием + // Check presence of tooltip for field with description const nameLabel = formHtml.querySelector(`label[for="Person.name"]`); expect(nameLabel).toBeDefined(); diff --git a/easydata.js/packs/crud/tests/text_data_filter.test.ts b/easydata.js/packs/crud/tests/text_data_filter.test.ts index b6eb3c26..3f1883da 100644 --- a/easydata.js/packs/crud/tests/text_data_filter.test.ts +++ b/easydata.js/packs/crud/tests/text_data_filter.test.ts @@ -115,7 +115,7 @@ describe('TextDataFilter', () => { return filter.apply('apple') .then(filteredTable => { expect(filteredTable).not.toBe(sourceTable); - expect(filteredTable.getCachedCount()).toBe(2); // Apple и Pineapple + expect(filteredTable.getCachedCount()).toBe(2); // Apple and Pineapple const rows = filteredTable.getCachedRows(); expect(rows).toBeArray(); @@ -217,7 +217,7 @@ describe('TextDataFilter', () => { .then(filteredTable => { const rows = filteredTable.getCachedRows(); expect(rows).toBeArray(); - expect(rows.length).toBe(1); // Apple с ценой 1.99 + expect(rows.length).toBe(1); // Apple with price 1.99 expect(rows[0].getValue('name')).toBe('Apple'); expect(rows[0].getValue('price')).toBe(1.99); });