fix формул
This commit is contained in:
681
src/feature/excelEngine/excelEngine.test.ts
Normal file
681
src/feature/excelEngine/excelEngine.test.ts
Normal file
@@ -0,0 +1,681 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { Workbook, CellUtils } from './excelEngine'
|
||||
|
||||
function createWorkbookWithSheet(name = 'Sheet1') {
|
||||
const wb = new Workbook()
|
||||
const sheet = wb.addSheet(name)
|
||||
return { wb, sheet }
|
||||
}
|
||||
|
||||
describe('Workbook — формулы', () => {
|
||||
// ==================== Базовые значения ====================
|
||||
|
||||
describe('Базовые значения', () => {
|
||||
it('устанавливает и получает число', () => {
|
||||
const { wb, sheet } = createWorkbookWithSheet()
|
||||
sheet.setCell('A1', 42)
|
||||
expect(wb.getValue('Sheet1!A1')).toBe(42)
|
||||
})
|
||||
|
||||
it('устанавливает и получает строку', () => {
|
||||
const { wb, sheet } = createWorkbookWithSheet()
|
||||
sheet.setCell('A1', 'hello')
|
||||
expect(wb.getValue('Sheet1!A1')).toBe('hello')
|
||||
})
|
||||
|
||||
it('пустая ячейка возвращает null', () => {
|
||||
const { wb } = createWorkbookWithSheet()
|
||||
expect(wb.getValue('Sheet1!A1')).toBe(null)
|
||||
})
|
||||
})
|
||||
|
||||
// ==================== Простые формулы ====================
|
||||
|
||||
describe('Простые формулы', () => {
|
||||
it('формула с числом', () => {
|
||||
const { wb, sheet } = createWorkbookWithSheet()
|
||||
sheet.setCell('A1', '=1+2')
|
||||
expect(wb.getValue('Sheet1!A1')).toBe(3)
|
||||
})
|
||||
|
||||
it('формула со ссылкой на ячейку', () => {
|
||||
const { wb, sheet } = createWorkbookWithSheet()
|
||||
sheet.setCell('A1', 10)
|
||||
sheet.setCell('A2', '=A1+5')
|
||||
expect(wb.getValue('Sheet1!A2')).toBe(15)
|
||||
})
|
||||
|
||||
it('формула с несколькими ссылками', () => {
|
||||
const { wb, sheet } = createWorkbookWithSheet()
|
||||
sheet.setCell('A1', 10)
|
||||
sheet.setCell('A2', 20)
|
||||
sheet.setCell('A3', '=A1+A2')
|
||||
expect(wb.getValue('Sheet1!A3')).toBe(30)
|
||||
})
|
||||
})
|
||||
|
||||
// ==================== Функции в формулах ====================
|
||||
|
||||
describe('СУММ в формуле', () => {
|
||||
it('суммирует диапазон', () => {
|
||||
const { wb, sheet } = createWorkbookWithSheet()
|
||||
sheet.setCell('A1', 1)
|
||||
sheet.setCell('A2', 2)
|
||||
sheet.setCell('A3', 3)
|
||||
sheet.setCell('B1', '=СУММ(A1:A3)')
|
||||
expect(wb.getValue('Sheet1!B1')).toBe(6)
|
||||
})
|
||||
})
|
||||
|
||||
describe('СРЗНАЧ в формуле', () => {
|
||||
it('считает среднее диапазона', () => {
|
||||
const { wb, sheet } = createWorkbookWithSheet()
|
||||
sheet.setCell('A1', 2)
|
||||
sheet.setCell('A2', 4)
|
||||
sheet.setCell('A3', 6)
|
||||
sheet.setCell('B1', '=СРЗНАЧ(A1:A3)')
|
||||
expect(wb.getValue('Sheet1!B1')).toBe(4)
|
||||
})
|
||||
})
|
||||
|
||||
describe('МАКС в формуле', () => {
|
||||
it('возвращает максимум двух ячеек', () => {
|
||||
const { wb, sheet } = createWorkbookWithSheet()
|
||||
sheet.setCell('A1', 3)
|
||||
sheet.setCell('A2', 7)
|
||||
sheet.setCell('B1', '=МАКС(A1, A2)')
|
||||
expect(wb.getValue('Sheet1!B1')).toBe(7)
|
||||
})
|
||||
})
|
||||
|
||||
describe('СТЕПЕНЬ в формуле', () => {
|
||||
it('возводит значение ячейки в степень', () => {
|
||||
const { wb, sheet } = createWorkbookWithSheet()
|
||||
sheet.setCell('A1', 2)
|
||||
sheet.setCell('B1', '=СТЕПЕНЬ(A1, 10)')
|
||||
expect(wb.getValue('Sheet1!B1')).toBe(1024)
|
||||
})
|
||||
})
|
||||
|
||||
describe('КОРЕНЬ в формуле', () => {
|
||||
it('извлекает корень из ячейки', () => {
|
||||
const { wb, sheet } = createWorkbookWithSheet()
|
||||
sheet.setCell('A1', 25)
|
||||
sheet.setCell('B1', '=КОРЕНЬ(A1)')
|
||||
expect(wb.getValue('Sheet1!B1')).toBe(5)
|
||||
})
|
||||
})
|
||||
|
||||
describe('ABS в формуле', () => {
|
||||
it('возвращает модуль ячейки', () => {
|
||||
const { wb, sheet } = createWorkbookWithSheet()
|
||||
sheet.setCell('A1', -15)
|
||||
sheet.setCell('B1', '=ABS(A1)')
|
||||
expect(wb.getValue('Sheet1!B1')).toBe(15)
|
||||
})
|
||||
})
|
||||
|
||||
// ==================== БОЛЬШЕ / МЕНЬШЕ ====================
|
||||
|
||||
describe('БОЛЬШЕ в формуле', () => {
|
||||
it('сравнивает два числа', () => {
|
||||
const { wb, sheet } = createWorkbookWithSheet()
|
||||
sheet.setCell('A1', 10)
|
||||
sheet.setCell('A2', 5)
|
||||
sheet.setCell('B1', '=БОЛЬШЕ(A1, A2)')
|
||||
expect(wb.getValue('Sheet1!B1')).toBe(true)
|
||||
})
|
||||
|
||||
it('возвращает false при меньшем значении', () => {
|
||||
const { wb, sheet } = createWorkbookWithSheet()
|
||||
sheet.setCell('A1', 3)
|
||||
sheet.setCell('A2', 5)
|
||||
sheet.setCell('B1', '=БОЛЬШЕ(A1, A2)')
|
||||
expect(wb.getValue('Sheet1!B1')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('МЕНЬШЕ в формуле', () => {
|
||||
it('сравнивает два числа', () => {
|
||||
const { wb, sheet } = createWorkbookWithSheet()
|
||||
sheet.setCell('A1', 3)
|
||||
sheet.setCell('A2', 10)
|
||||
sheet.setCell('B1', '=МЕНЬШЕ(A1, A2)')
|
||||
expect(wb.getValue('Sheet1!B1')).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
// ==================== ЕСЛИ + БОЛЬШЕ/МЕНЬШЕ ====================
|
||||
|
||||
describe('ЕСЛИ + БОЛЬШЕ/МЕНЬШЕ', () => {
|
||||
it('ЕСЛИ(БОЛЬШЕ(...)) возвращает правильную ветку', () => {
|
||||
const { wb, sheet } = createWorkbookWithSheet()
|
||||
sheet.setCell('A1', 100)
|
||||
sheet.setCell('B1', '=ЕСЛИ(БОЛЬШЕ(A1, 50), "Много", "Мало")')
|
||||
expect(wb.getValue('Sheet1!B1')).toBe('Много')
|
||||
})
|
||||
|
||||
it('ЕСЛИ(МЕНЬШЕ(...)) возвращает ветку лжи', () => {
|
||||
const { wb, sheet } = createWorkbookWithSheet()
|
||||
sheet.setCell('A1', 100)
|
||||
sheet.setCell('B1', '=ЕСЛИ(МЕНЬШЕ(A1, 50), "Мало", "Много")')
|
||||
expect(wb.getValue('Sheet1!B1')).toBe('Много')
|
||||
})
|
||||
|
||||
it('ЕСЛИ с И и БОЛЬШЕ', () => {
|
||||
const { wb, sheet } = createWorkbookWithSheet()
|
||||
sheet.setCell('A1', 50)
|
||||
sheet.setCell('B1', '=ЕСЛИ(И(БОЛЬШЕ(A1, 10), МЕНЬШЕ(A1, 100)), "В диапазоне", "Вне")')
|
||||
expect(wb.getValue('Sheet1!B1')).toBe('В диапазоне')
|
||||
})
|
||||
|
||||
it('ЕСЛИ с ИЛИ', () => {
|
||||
const { wb, sheet } = createWorkbookWithSheet()
|
||||
sheet.setCell('A1', 5)
|
||||
sheet.setCell('B1', '=ЕСЛИ(ИЛИ(БОЛЬШЕ(A1, 100), МЕНЬШЕ(A1, 10)), "Крайнее", "Норм")')
|
||||
expect(wb.getValue('Sheet1!B1')).toBe('Крайнее')
|
||||
})
|
||||
|
||||
it('вложенный ЕСЛИ', () => {
|
||||
const { wb, sheet } = createWorkbookWithSheet()
|
||||
sheet.setCell('A1', 50)
|
||||
sheet.setCell('B1', '=ЕСЛИ(БОЛЬШЕ(A1, 100), "Много", ЕСЛИ(МЕНЬШЕ(A1, 10), "Мало", "Норм"))')
|
||||
expect(wb.getValue('Sheet1!B1')).toBe('Норм')
|
||||
})
|
||||
})
|
||||
|
||||
// ==================== ЕПУСТО ====================
|
||||
|
||||
describe('ЕПУСТО в формуле', () => {
|
||||
it('true для пустой ячейки', () => {
|
||||
const { wb, sheet } = createWorkbookWithSheet()
|
||||
sheet.setCell('B1', '=ЕПУСТО(A1)')
|
||||
expect(wb.getValue('Sheet1!B1')).toBe(true)
|
||||
})
|
||||
|
||||
it('false для заполненной ячейки', () => {
|
||||
const { wb, sheet } = createWorkbookWithSheet()
|
||||
sheet.setCell('A1', 42)
|
||||
sheet.setCell('B1', '=ЕПУСТО(A1)')
|
||||
expect(wb.getValue('Sheet1!B1')).toBe(false)
|
||||
})
|
||||
|
||||
it('false для ячейки с нулём', () => {
|
||||
const { wb, sheet } = createWorkbookWithSheet()
|
||||
sheet.setCell('A1', 0)
|
||||
sheet.setCell('B1', '=ЕПУСТО(A1)')
|
||||
expect(wb.getValue('Sheet1!B1')).toBe(false)
|
||||
})
|
||||
|
||||
it('ЕСЛИ(ЕПУСТО(...)) работает', () => {
|
||||
const { wb, sheet } = createWorkbookWithSheet()
|
||||
sheet.setCell('B1', '=ЕСЛИ(ЕПУСТО(A1), "Пусто", "Заполнено")')
|
||||
expect(wb.getValue('Sheet1!B1')).toBe('Пусто')
|
||||
})
|
||||
})
|
||||
|
||||
// ==================== Строковые функции в формулах ====================
|
||||
|
||||
describe('Строковые функции в формулах', () => {
|
||||
it('СЦЕПИТЬ', () => {
|
||||
const { wb, sheet } = createWorkbookWithSheet()
|
||||
sheet.setCell('A1', 'Привет')
|
||||
sheet.setCell('A2', ' мир')
|
||||
sheet.setCell('B1', '=СЦЕПИТЬ(A1, A2)')
|
||||
expect(wb.getValue('Sheet1!B1')).toBe('Привет мир')
|
||||
})
|
||||
|
||||
it('ПРОПИСН', () => {
|
||||
const { wb, sheet } = createWorkbookWithSheet()
|
||||
sheet.setCell('A1', 'hello')
|
||||
sheet.setCell('B1', '=ПРОПИСН(A1)')
|
||||
expect(wb.getValue('Sheet1!B1')).toBe('HELLO')
|
||||
})
|
||||
|
||||
it('СТРОЧН', () => {
|
||||
const { wb, sheet } = createWorkbookWithSheet()
|
||||
sheet.setCell('A1', 'HELLO')
|
||||
sheet.setCell('B1', '=СТРОЧН(A1)')
|
||||
expect(wb.getValue('Sheet1!B1')).toBe('hello')
|
||||
})
|
||||
})
|
||||
|
||||
// ==================== Округление в формулах ====================
|
||||
|
||||
describe('Округление в формулах', () => {
|
||||
it('ОКРУГЛ', () => {
|
||||
const { wb, sheet } = createWorkbookWithSheet()
|
||||
sheet.setCell('A1', 3.456)
|
||||
sheet.setCell('B1', '=ОКРУГЛ(A1, 2)')
|
||||
expect(wb.getValue('Sheet1!B1')).toBe(3.46)
|
||||
})
|
||||
|
||||
it('ЦЕЛОЕ', () => {
|
||||
const { wb, sheet } = createWorkbookWithSheet()
|
||||
sheet.setCell('A1', 3.9)
|
||||
sheet.setCell('B1', '=ЦЕЛОЕ(A1)')
|
||||
expect(wb.getValue('Sheet1!B1')).toBe(3)
|
||||
})
|
||||
})
|
||||
|
||||
// ==================== Межлистовые ссылки ====================
|
||||
|
||||
describe('Межлистовые ссылки', () => {
|
||||
it('ссылка на другой лист', () => {
|
||||
const wb = new Workbook()
|
||||
const sheetL = wb.addSheet('L')
|
||||
const sheetR = wb.addSheet('R')
|
||||
sheetL.setCell('A1', 100)
|
||||
sheetR.setCell('A1', '=L!A1+50')
|
||||
expect(wb.getValue('R!A1')).toBe(150)
|
||||
})
|
||||
})
|
||||
|
||||
// ==================== Зависимости и пересчёт ====================
|
||||
|
||||
describe('Зависимости и пересчёт', () => {
|
||||
it('пересчёт при изменении зависимой ячейки', () => {
|
||||
const { wb, sheet } = createWorkbookWithSheet()
|
||||
sheet.setCell('A1', 10)
|
||||
sheet.setCell('B1', '=A1*2')
|
||||
expect(wb.getValue('Sheet1!B1')).toBe(20)
|
||||
|
||||
sheet.setCell('A1', 20)
|
||||
expect(wb.getValue('Sheet1!B1')).toBe(40)
|
||||
})
|
||||
|
||||
it('цепочка зависимостей', () => {
|
||||
const { wb, sheet } = createWorkbookWithSheet()
|
||||
sheet.setCell('A1', 5)
|
||||
sheet.setCell('A2', '=A1+1')
|
||||
sheet.setCell('A3', '=A2+1')
|
||||
expect(wb.getValue('Sheet1!A3')).toBe(7)
|
||||
|
||||
sheet.setCell('A1', 10)
|
||||
expect(wb.getValue('Sheet1!A3')).toBe(12)
|
||||
})
|
||||
})
|
||||
|
||||
// ==================== Замена функций (регрессия) ====================
|
||||
|
||||
describe('Регрессия: замена функций не ломает кириллические имена', () => {
|
||||
it('ЕСЛИ не должен быть разбит заменой И', () => {
|
||||
const { wb, sheet } = createWorkbookWithSheet()
|
||||
sheet.setCell('A1', 50)
|
||||
sheet.setCell('B1', '=ЕСЛИ(БОЛЬШЕ(A1, 100), "Много", "Мало")')
|
||||
expect(wb.getValue('Sheet1!B1')).toBe('Мало')
|
||||
})
|
||||
|
||||
it('И внутри ЕСЛИ(И(...)) работает корректно', () => {
|
||||
const { wb, sheet } = createWorkbookWithSheet()
|
||||
sheet.setCell('A1', 50)
|
||||
sheet.setCell('B1', '=ЕСЛИ(И(БОЛЬШЕ(A1, 10), МЕНЬШЕ(A1, 100)), "Да", "Нет")')
|
||||
expect(wb.getValue('Sheet1!B1')).toBe('Да')
|
||||
})
|
||||
|
||||
it('НЕ не ломает другие функции', () => {
|
||||
const { wb, sheet } = createWorkbookWithSheet()
|
||||
sheet.setCell('A1', 50)
|
||||
sheet.setCell('B1', '=ЕСЛИ(НЕ(БОЛЬШЕ(A1, 100)), "Не больше", "Больше")')
|
||||
expect(wb.getValue('Sheet1!B1')).toBe('Не больше')
|
||||
})
|
||||
})
|
||||
|
||||
// ==================== Case-insensitive функции ====================
|
||||
|
||||
describe('Функции в нижнем регистре', () => {
|
||||
it('случдр в нижнем регистре', () => {
|
||||
const { wb, sheet } = createWorkbookWithSheet()
|
||||
sheet.setCell('A1', '=случдр(1, 10, 2)')
|
||||
const val = wb.getValue('Sheet1!A1') as number
|
||||
expect(val).toBeGreaterThanOrEqual(1)
|
||||
expect(val).toBeLessThanOrEqual(10)
|
||||
})
|
||||
|
||||
it('округл в нижнем регистре', () => {
|
||||
const { wb, sheet } = createWorkbookWithSheet()
|
||||
sheet.setCell('A1', '=округл(3.456, 2)')
|
||||
expect(wb.getValue('Sheet1!A1')).toBe(3.46)
|
||||
})
|
||||
|
||||
it('сумм в нижнем регистре', () => {
|
||||
const { wb, sheet } = createWorkbookWithSheet()
|
||||
sheet.setCell('A1', 1)
|
||||
sheet.setCell('A2', 2)
|
||||
sheet.setCell('A3', 3)
|
||||
sheet.setCell('B1', '=сумм(A1:A3)')
|
||||
expect(wb.getValue('Sheet1!B1')).toBe(6)
|
||||
})
|
||||
|
||||
it('если в нижнем регистре с больше', () => {
|
||||
const { wb, sheet } = createWorkbookWithSheet()
|
||||
sheet.setCell('A1', 100)
|
||||
sheet.setCell('B1', '=если(больше(A1, 50), "Много", "Мало")')
|
||||
expect(wb.getValue('Sheet1!B1')).toBe('Много')
|
||||
})
|
||||
|
||||
it('смешанный регистр', () => {
|
||||
const { wb, sheet } = createWorkbookWithSheet()
|
||||
sheet.setCell('A1', 5)
|
||||
sheet.setCell('B1', '=Если(Больше(A1, 3), "да", "нет")')
|
||||
expect(wb.getValue('Sheet1!B1')).toBe('да')
|
||||
})
|
||||
|
||||
it('епусто в нижнем регистре — пустая ячейка', () => {
|
||||
const { wb, sheet } = createWorkbookWithSheet()
|
||||
sheet.setCell('B1', '=епусто(A1)')
|
||||
expect(wb.getValue('Sheet1!B1')).toBe(true)
|
||||
})
|
||||
|
||||
it('епусто в нижнем регистре — заполненная ячейка', () => {
|
||||
const { wb, sheet } = createWorkbookWithSheet()
|
||||
sheet.setCell('A1', 42)
|
||||
sheet.setCell('B1', '=епусто(A1)')
|
||||
expect(wb.getValue('Sheet1!B1')).toBe(false)
|
||||
})
|
||||
|
||||
it('если(епусто(...)) в нижнем регистре', () => {
|
||||
const { wb, sheet } = createWorkbookWithSheet()
|
||||
sheet.setCell('B1', '=если(епусто(A1), "Пусто", "Есть")')
|
||||
expect(wb.getValue('Sheet1!B1')).toBe('Пусто')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// ==================== Сценарий протокола ====================
|
||||
|
||||
describe('Сценарий протокола: шаблон → ввод данных → пересчёт → выгрузка', () => {
|
||||
/**
|
||||
* Имитирует реальный flow:
|
||||
* 1. Загружается шаблон с формулами (fromJSON)
|
||||
* 2. Пользователь заполняет форму → значения пишутся в ячейки (setCell)
|
||||
* 3. Движок пересчитывает формулы (recalc)
|
||||
* 4. getAllModifiedCells / toJSON отдают данные для бэкенда
|
||||
*/
|
||||
|
||||
function createProtocolWorkbook() {
|
||||
const wb = new Workbook()
|
||||
wb.addSheet('L') // лист протокола
|
||||
wb.addSheet('R') // лист настроек/расчётов
|
||||
return wb
|
||||
}
|
||||
|
||||
it('загрузка шаблона и пересчёт формул', () => {
|
||||
const wb = createProtocolWorkbook()
|
||||
|
||||
// Шаблон: R!A1 — имя файла, R!A2 — область печати
|
||||
// L!A1 — заголовок, L!B1 — формула суммы
|
||||
wb.fromJSON({
|
||||
L: {
|
||||
A1: 'Протокол испытаний',
|
||||
A2: 10,
|
||||
A3: 20,
|
||||
A4: '=СУММ(A2:A3)',
|
||||
},
|
||||
R: {
|
||||
A1: 'protocol_output.xlsx',
|
||||
A2: 'A1:D20',
|
||||
},
|
||||
})
|
||||
|
||||
expect(wb.getValue('L!A1')).toBe('Протокол испытаний')
|
||||
expect(wb.getValue('L!A4')).toBe(30)
|
||||
expect(wb.getValue('R!A1')).toBe('protocol_output.xlsx')
|
||||
expect(wb.getValue('R!A2')).toBe('A1:D20')
|
||||
})
|
||||
|
||||
it('ввод данных из формы и пересчёт зависимых ячеек', () => {
|
||||
const wb = createProtocolWorkbook()
|
||||
|
||||
wb.fromJSON({
|
||||
L: {
|
||||
A1: '', // ячейка для ввода (пустая в шаблоне)
|
||||
B1: '=ОКРУГЛ(A1 * 1.2, 2)', // формула зависит от A1
|
||||
C1: '=ЕСЛИ(БОЛЬШЕ(B1, 100), "Не соответствует", "Соответствует")',
|
||||
},
|
||||
})
|
||||
|
||||
// Первый recalc после загрузки (как в useMultiSheetEngine.safeRecalc)
|
||||
wb.recalc()
|
||||
|
||||
// Пользователь вводит значение через форму → setCellValue
|
||||
wb.setCell('L!A1', 50)
|
||||
expect(wb.getValue('L!B1')).toBe(60)
|
||||
expect(wb.getValue('L!C1')).toBe('Соответствует')
|
||||
|
||||
// Пользователь меняет значение
|
||||
wb.setCell('L!A1', 100)
|
||||
expect(wb.getValue('L!B1')).toBe(120)
|
||||
expect(wb.getValue('L!C1')).toBe('Не соответствует')
|
||||
})
|
||||
|
||||
it('формулы со ссылками между листами L и R', () => {
|
||||
const wb = createProtocolWorkbook()
|
||||
|
||||
wb.fromJSON({
|
||||
R: {
|
||||
A5: 0.15, // коэффициент из настроек
|
||||
C1: '=случдр(10, 20, 2)', // случайное значение
|
||||
},
|
||||
L: {
|
||||
A1: 100,
|
||||
B1: '=ОКРУГЛ(A1 * R!A5, 2)', // 100 * 0.15 = 15
|
||||
},
|
||||
})
|
||||
|
||||
expect(wb.getValue('L!B1')).toBe(15)
|
||||
|
||||
// recalc чтобы зарегистрировать зависимости
|
||||
wb.recalc()
|
||||
|
||||
// Меняем входное значение
|
||||
wb.setCell('L!A1', 200)
|
||||
expect(wb.getValue('L!B1')).toBe(30)
|
||||
})
|
||||
|
||||
it('ЕСЛИ + БОЛЬШЕ + МЕНЬШЕ в контексте протокола', () => {
|
||||
const wb = createProtocolWorkbook()
|
||||
|
||||
// Типичный сценарий: проверка допусков
|
||||
wb.fromJSON({
|
||||
L: {
|
||||
A1: 'Параметр',
|
||||
A2: 50, // измеренное значение
|
||||
B2: 10, // нижний допуск
|
||||
C2: 100, // верхний допуск
|
||||
D2: '=ЕСЛИ(И(БОЛЬШЕ(A2, B2), МЕНЬШЕ(A2, C2)), "Годен", "Брак")',
|
||||
},
|
||||
})
|
||||
|
||||
expect(wb.getValue('L!D2')).toBe('Годен')
|
||||
|
||||
// Значение вне допуска
|
||||
wb.setCell('L!A2', 5)
|
||||
expect(wb.getValue('L!D2')).toBe('Брак')
|
||||
|
||||
// Значение на верхней границе — БОЛЬШЕ возвращает false при равенстве
|
||||
wb.setCell('L!A2', 100)
|
||||
expect(wb.getValue('L!D2')).toBe('Брак')
|
||||
})
|
||||
|
||||
it('ЕПУСТО в контексте протокола — пропуск пустых строк', () => {
|
||||
const wb = createProtocolWorkbook()
|
||||
|
||||
wb.fromJSON({
|
||||
L: {
|
||||
A1: 42,
|
||||
B1: '=ЕСЛИ(ЕПУСТО(A1), "", ОКРУГЛ(A1 * 1.1, 1))',
|
||||
A2: '', // пустая
|
||||
B2: '=ЕСЛИ(ЕПУСТО(A2), "", ОКРУГЛ(A2 * 1.1, 1))',
|
||||
},
|
||||
})
|
||||
|
||||
expect(wb.getValue('L!B1')).toBe(46.2)
|
||||
expect(wb.getValue('L!B2')).toBe('')
|
||||
})
|
||||
|
||||
it('СТЕПЕНЬ в формулах протокола', () => {
|
||||
const wb = createProtocolWorkbook()
|
||||
|
||||
wb.fromJSON({
|
||||
L: {
|
||||
A1: 3,
|
||||
B1: '=СТЕПЕНЬ(A1, 2)', // 9
|
||||
C1: '=КОРЕНЬ(B1)', // 3
|
||||
},
|
||||
})
|
||||
|
||||
expect(wb.getValue('L!B1')).toBe(9)
|
||||
expect(wb.getValue('L!C1')).toBe(3)
|
||||
})
|
||||
|
||||
it('цепочка пересчёта: A → B → C → D', () => {
|
||||
const wb = createProtocolWorkbook()
|
||||
|
||||
wb.fromJSON({
|
||||
L: {
|
||||
A1: 10,
|
||||
B1: '=A1 * 2', // 20
|
||||
C1: '=B1 + 5', // 25
|
||||
D1: '=ОКРУГЛ(C1 / 3, 2)', // 8.33
|
||||
},
|
||||
})
|
||||
|
||||
expect(wb.getValue('L!D1')).toBe(8.33)
|
||||
|
||||
wb.setCell('L!A1', 100)
|
||||
// 100 → 200 → 205 → 68.33
|
||||
expect(wb.getValue('L!B1')).toBe(200)
|
||||
expect(wb.getValue('L!C1')).toBe(205)
|
||||
expect(wb.getValue('L!D1')).toBe(68.33)
|
||||
})
|
||||
|
||||
it('toJSON возвращает raw-значения (формулы) для бэкенда', () => {
|
||||
const wb = createProtocolWorkbook()
|
||||
|
||||
wb.fromJSON({
|
||||
L: {
|
||||
A1: 10,
|
||||
B1: '=A1 * 2',
|
||||
},
|
||||
})
|
||||
|
||||
// getValue возвращает вычисленное
|
||||
expect(wb.getValue('L!B1')).toBe(20)
|
||||
|
||||
// toJSON возвращает raw (формулу) — это идёт в cells_to_update
|
||||
const json = wb.toJSON()
|
||||
expect(json['L']['A1']).toBe(10)
|
||||
expect(json['L']['B1']).toBe('=A1 * 2')
|
||||
})
|
||||
|
||||
it('getAllModifiedCells: baseline vs текущее состояние', () => {
|
||||
const wb = createProtocolWorkbook()
|
||||
|
||||
// Загружаем шаблон
|
||||
const templateData = {
|
||||
L: {
|
||||
A1: '',
|
||||
B1: '=A1 * 2',
|
||||
},
|
||||
}
|
||||
|
||||
wb.fromJSON(templateData)
|
||||
|
||||
// Сохраняем baseline (как делает setTemplateData в хуке)
|
||||
const baseline = JSON.parse(JSON.stringify(templateData))
|
||||
|
||||
// Пользователь вводит данные
|
||||
wb.setCell('L!A1', 42)
|
||||
|
||||
// Сравниваем текущее с baseline
|
||||
const currentData = wb.toJSON()
|
||||
const modified: Record<string, any> = {}
|
||||
|
||||
for (const cellAddr of Object.keys(currentData['L'] || {})) {
|
||||
const current = String(currentData['L'][cellAddr] ?? '')
|
||||
const original = String(baseline['L']?.[cellAddr] ?? '')
|
||||
if (current !== original) {
|
||||
modified[cellAddr] = currentData['L'][cellAddr]
|
||||
}
|
||||
}
|
||||
|
||||
// A1 изменилась (была '', стала 42)
|
||||
expect(modified['A1']).toBe(42)
|
||||
// B1 не изменилась (формула та же)
|
||||
expect(modified['B1']).toBeUndefined()
|
||||
})
|
||||
|
||||
it('множественные элементы формы → разные ячейки', () => {
|
||||
const wb = createProtocolWorkbook()
|
||||
|
||||
wb.fromJSON({
|
||||
L: {
|
||||
A1: '', // ячейка для ввода
|
||||
B1: '', // ячейка для ввода
|
||||
A2: '', // ячейка для ввода
|
||||
B2: '', // ячейка для ввода
|
||||
// Формулы зависят от ячеек, куда пишутся значения формы
|
||||
C1: '=СЦЕПИТЬ(A1, " ", B1)',
|
||||
C2: '=A2 + B2',
|
||||
},
|
||||
})
|
||||
|
||||
// Первый recalc после загрузки (как в useMultiSheetEngine)
|
||||
wb.recalc()
|
||||
|
||||
// Элемент формы 1 → A1 (текст)
|
||||
wb.setCell('L!A1', 'Иванов')
|
||||
// Элемент формы 2 → B1 (текст)
|
||||
wb.setCell('L!B1', 'И.И.')
|
||||
|
||||
// Элемент формы 3 → A2 (число)
|
||||
wb.setCell('L!A2', 10)
|
||||
// Элемент формы 4 → B2 (число)
|
||||
wb.setCell('L!B2', 20)
|
||||
|
||||
expect(wb.getValue('L!C1')).toBe('Иванов И.И.')
|
||||
expect(wb.getValue('L!C2')).toBe(30)
|
||||
})
|
||||
|
||||
it('формулы в нижнем регистре в шаблоне (как в реальных данных)', () => {
|
||||
const wb = createProtocolWorkbook()
|
||||
|
||||
wb.fromJSON({
|
||||
R: {
|
||||
C7: '=случдр(16, 17.6, 3)',
|
||||
D7: '=если(больше(A1, 100), "Много", "Мало")',
|
||||
},
|
||||
L: {
|
||||
A1: 50,
|
||||
},
|
||||
})
|
||||
|
||||
// случдр должна вернуть число в диапазоне
|
||||
const c7 = wb.getValue('R!C7') as number
|
||||
expect(c7).toBeGreaterThanOrEqual(16)
|
||||
expect(c7).toBeLessThanOrEqual(17.6)
|
||||
|
||||
// если + больше в нижнем регистре
|
||||
expect(wb.getValue('R!D7')).toBe('Мало')
|
||||
})
|
||||
})
|
||||
|
||||
// ==================== CellUtils ====================
|
||||
|
||||
describe('CellUtils', () => {
|
||||
it('indexToColumn', () => {
|
||||
expect(CellUtils.indexToColumn(0)).toBe('A')
|
||||
expect(CellUtils.indexToColumn(25)).toBe('Z')
|
||||
expect(CellUtils.indexToColumn(26)).toBe('AA')
|
||||
})
|
||||
|
||||
it('createCellName с номером колонки', () => {
|
||||
expect(CellUtils.createCellName(0, 1)).toBe('A1')
|
||||
expect(CellUtils.createCellName(2, 5)).toBe('C5')
|
||||
})
|
||||
|
||||
it('createCellName со строкой колонки', () => {
|
||||
expect(CellUtils.createCellName('B', 3)).toBe('B3')
|
||||
})
|
||||
})
|
||||
@@ -123,7 +123,7 @@ export class Cell {
|
||||
// Обрабатываем ЕПУСТО до замены ссылок на значения,
|
||||
// чтобы проверить raw-значение ячейки напрямую
|
||||
code = code.replace(
|
||||
/ЕПУСТО\s*\(\s*(?:([A-Za-z0-9_]+)!)?([A-Z](?:[1-9]|[1-8][0-9]|150))\s*\)/g,
|
||||
/ЕПУСТО\s*\(\s*(?:([A-Za-z0-9_]+)!)?([A-Za-z](?:[1-9]|[1-8][0-9]|150))\s*\)/gi,
|
||||
(_match, sheetName, cellRef) => {
|
||||
const qualifiedRef = sheetName
|
||||
? `${sheetName}!${cellRef}`
|
||||
@@ -204,12 +204,18 @@ export class Cell {
|
||||
// Заменяем функции Excel
|
||||
const functions = this.sheet.workbook.getFunctions()
|
||||
|
||||
for (const [funcName] of Object.entries(functions)) {
|
||||
// Сортируем функции по длине имени (от длинных к коротким),
|
||||
// чтобы ЕСЛИ заменилось раньше И, и не было частичных замен
|
||||
const sortedFuncNames = Object.keys(functions).sort(
|
||||
(a, b) => b.length - a.length
|
||||
)
|
||||
|
||||
for (const funcName of sortedFuncNames) {
|
||||
// Экранируем специальные символы в названии функции для регулярного выражения
|
||||
const escapedFuncName = funcName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
||||
// Для кириллических символов \b может не работать, используем более универсальное решение
|
||||
// Используем юникодные границы: перед функцией не должно быть буквы/цифры (включая кириллицу)
|
||||
const funcRegex = new RegExp(
|
||||
`(?:^|[^\\w])(${escapedFuncName})\\s*\\(`,
|
||||
`(?:^|[^\\wА-Яа-яЁё])(${escapedFuncName})\\s*\\(`,
|
||||
'gi'
|
||||
)
|
||||
const before = code
|
||||
|
||||
354
src/feature/excelEngine/functions.test.ts
Normal file
354
src/feature/excelEngine/functions.test.ts
Normal file
@@ -0,0 +1,354 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { DEFAULT_FUNCTIONS } from './functions'
|
||||
|
||||
describe('DEFAULT_FUNCTIONS', () => {
|
||||
// ==================== Математические ====================
|
||||
|
||||
describe('СУММ', () => {
|
||||
it('суммирует числа', () => {
|
||||
expect(DEFAULT_FUNCTIONS.СУММ(1, 2, 3)).toBe(6)
|
||||
})
|
||||
it('суммирует массив', () => {
|
||||
expect(DEFAULT_FUNCTIONS.СУММ([1, 2, 3])).toBe(6)
|
||||
})
|
||||
it('игнорирует не-числа', () => {
|
||||
expect(DEFAULT_FUNCTIONS.СУММ(1, 'abc', 3)).toBe(4)
|
||||
})
|
||||
it('возвращает 0 без аргументов', () => {
|
||||
expect(DEFAULT_FUNCTIONS.СУММ()).toBe(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('СРЗНАЧ', () => {
|
||||
it('считает среднее', () => {
|
||||
expect(DEFAULT_FUNCTIONS.СРЗНАЧ(2, 4, 6)).toBe(4)
|
||||
})
|
||||
it('игнорирует не-числа', () => {
|
||||
expect(DEFAULT_FUNCTIONS.СРЗНАЧ(2, 'abc', 4)).toBe(3)
|
||||
})
|
||||
it('возвращает 0 если нет чисел', () => {
|
||||
expect(DEFAULT_FUNCTIONS.СРЗНАЧ('a', 'b')).toBe(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('СЧЁТ', () => {
|
||||
it('считает количество чисел', () => {
|
||||
expect(DEFAULT_FUNCTIONS['СЧЁТ'](1, 'a', 2, null, 3)).toBe(3)
|
||||
})
|
||||
it('возвращает 0 без чисел', () => {
|
||||
expect(DEFAULT_FUNCTIONS['СЧЁТ']('a', 'b')).toBe(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('МАКС', () => {
|
||||
it('возвращает максимум', () => {
|
||||
expect(DEFAULT_FUNCTIONS.МАКС(3, 7)).toBe(7)
|
||||
})
|
||||
it('ошибка при не-числах', () => {
|
||||
expect(DEFAULT_FUNCTIONS.МАКС('a' as any, 1)).toBe('#ЗНАЧ!')
|
||||
})
|
||||
it('ошибка при Infinity', () => {
|
||||
expect(DEFAULT_FUNCTIONS.МАКС(Infinity, 1)).toBe('#ЧИСЛО!')
|
||||
})
|
||||
})
|
||||
|
||||
describe('МИН', () => {
|
||||
it('возвращает минимум', () => {
|
||||
expect(DEFAULT_FUNCTIONS.МИН(3, 7, 1)).toBe(1)
|
||||
})
|
||||
it('работает с массивом', () => {
|
||||
expect(DEFAULT_FUNCTIONS.МИН([3, 7, 1])).toBe(1)
|
||||
})
|
||||
it('возвращает 0 без чисел', () => {
|
||||
expect(DEFAULT_FUNCTIONS.МИН('a', 'b')).toBe(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('ABS', () => {
|
||||
it('возвращает модуль', () => {
|
||||
expect(DEFAULT_FUNCTIONS.ABS(-5)).toBe(5)
|
||||
expect(DEFAULT_FUNCTIONS.ABS(5)).toBe(5)
|
||||
expect(DEFAULT_FUNCTIONS.ABS(0)).toBe(0)
|
||||
})
|
||||
it('ошибка при не-числе', () => {
|
||||
expect(DEFAULT_FUNCTIONS.ABS('abc' as any)).toBe('#ЗНАЧ!')
|
||||
})
|
||||
it('ошибка при Infinity', () => {
|
||||
expect(DEFAULT_FUNCTIONS.ABS(Infinity)).toBe('#ЧИСЛО!')
|
||||
})
|
||||
})
|
||||
|
||||
describe('КОРЕНЬ', () => {
|
||||
it('возвращает корень', () => {
|
||||
expect(DEFAULT_FUNCTIONS['КОРЕНЬ'](9)).toBe(3)
|
||||
expect(DEFAULT_FUNCTIONS['КОРЕНЬ'](0)).toBe(0)
|
||||
})
|
||||
it('ошибка при отрицательном числе', () => {
|
||||
expect(DEFAULT_FUNCTIONS['КОРЕНЬ'](-4)).toBe('#ЧИСЛО!')
|
||||
})
|
||||
it('ошибка при не-числе', () => {
|
||||
expect(DEFAULT_FUNCTIONS['КОРЕНЬ']('abc' as any)).toBe('#ЗНАЧ!')
|
||||
})
|
||||
})
|
||||
|
||||
describe('СТЕПЕНЬ', () => {
|
||||
it('возводит в степень', () => {
|
||||
expect(DEFAULT_FUNCTIONS['СТЕПЕНЬ'](2, 3)).toBe(8)
|
||||
expect(DEFAULT_FUNCTIONS['СТЕПЕНЬ'](5, 0)).toBe(1)
|
||||
expect(DEFAULT_FUNCTIONS['СТЕПЕНЬ'](9, 0.5)).toBe(3)
|
||||
})
|
||||
it('ошибка при не-числе', () => {
|
||||
expect(DEFAULT_FUNCTIONS['СТЕПЕНЬ']('a' as any, 2)).toBe('#ЗНАЧ!')
|
||||
})
|
||||
it('ошибка при Infinity', () => {
|
||||
expect(DEFAULT_FUNCTIONS['СТЕПЕНЬ'](Infinity, 2)).toBe('#ЧИСЛО!')
|
||||
})
|
||||
it('ошибка при переполнении', () => {
|
||||
expect(DEFAULT_FUNCTIONS['СТЕПЕНЬ'](10, 1000)).toBe('#ЧИСЛО!')
|
||||
})
|
||||
})
|
||||
|
||||
// ==================== Сравнение ====================
|
||||
|
||||
describe('БОЛЬШЕ', () => {
|
||||
it('возвращает true когда первое больше', () => {
|
||||
expect(DEFAULT_FUNCTIONS['БОЛЬШЕ'](5, 3)).toBe(true)
|
||||
})
|
||||
it('возвращает false когда первое меньше', () => {
|
||||
expect(DEFAULT_FUNCTIONS['БОЛЬШЕ'](3, 5)).toBe(false)
|
||||
})
|
||||
it('возвращает false при равенстве', () => {
|
||||
expect(DEFAULT_FUNCTIONS['БОЛЬШЕ'](5, 5)).toBe(false)
|
||||
})
|
||||
it('ошибка при не-числах', () => {
|
||||
expect(DEFAULT_FUNCTIONS['БОЛЬШЕ']('a' as any, 1)).toBe('#ЗНАЧ!')
|
||||
})
|
||||
it('ошибка при Infinity', () => {
|
||||
expect(DEFAULT_FUNCTIONS['БОЛЬШЕ'](Infinity, 1)).toBe('#ЧИСЛО!')
|
||||
})
|
||||
})
|
||||
|
||||
describe('МЕНЬШЕ', () => {
|
||||
it('возвращает true когда первое меньше', () => {
|
||||
expect(DEFAULT_FUNCTIONS['МЕНЬШЕ'](3, 5)).toBe(true)
|
||||
})
|
||||
it('возвращает false когда первое больше', () => {
|
||||
expect(DEFAULT_FUNCTIONS['МЕНЬШЕ'](5, 3)).toBe(false)
|
||||
})
|
||||
it('возвращает false при равенстве', () => {
|
||||
expect(DEFAULT_FUNCTIONS['МЕНЬШЕ'](5, 5)).toBe(false)
|
||||
})
|
||||
it('ошибка при не-числах', () => {
|
||||
expect(DEFAULT_FUNCTIONS['МЕНЬШЕ']('a' as any, 1)).toBe('#ЗНАЧ!')
|
||||
})
|
||||
})
|
||||
|
||||
describe('ЕПУСТО', () => {
|
||||
it('true для null', () => {
|
||||
expect(DEFAULT_FUNCTIONS['ЕПУСТО'](null)).toBe(true)
|
||||
})
|
||||
it('true для undefined', () => {
|
||||
expect(DEFAULT_FUNCTIONS['ЕПУСТО'](undefined)).toBe(true)
|
||||
})
|
||||
it('true для пустой строки', () => {
|
||||
expect(DEFAULT_FUNCTIONS['ЕПУСТО']('')).toBe(true)
|
||||
})
|
||||
it('false для числа', () => {
|
||||
expect(DEFAULT_FUNCTIONS['ЕПУСТО'](0)).toBe(false)
|
||||
})
|
||||
it('false для строки', () => {
|
||||
expect(DEFAULT_FUNCTIONS['ЕПУСТО']('текст')).toBe(false)
|
||||
})
|
||||
it('false для false', () => {
|
||||
expect(DEFAULT_FUNCTIONS['ЕПУСТО'](false)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
// ==================== Логические ====================
|
||||
|
||||
describe('ЕСЛИ', () => {
|
||||
it('возвращает значение при истине', () => {
|
||||
expect(DEFAULT_FUNCTIONS['ЕСЛИ'](true, 'да', 'нет')).toBe('да')
|
||||
})
|
||||
it('возвращает значение при лжи', () => {
|
||||
expect(DEFAULT_FUNCTIONS['ЕСЛИ'](false, 'да', 'нет')).toBe('нет')
|
||||
})
|
||||
})
|
||||
|
||||
describe('И', () => {
|
||||
it('true когда все true', () => {
|
||||
expect(DEFAULT_FUNCTIONS['И'](true, true, true)).toBe(true)
|
||||
})
|
||||
it('false когда хоть один false', () => {
|
||||
expect(DEFAULT_FUNCTIONS['И'](true, false, true)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('ИЛИ', () => {
|
||||
it('true когда хоть один true', () => {
|
||||
expect(DEFAULT_FUNCTIONS['ИЛИ'](false, true, false)).toBe(true)
|
||||
})
|
||||
it('false когда все false', () => {
|
||||
expect(DEFAULT_FUNCTIONS['ИЛИ'](false, false, false)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('НЕ', () => {
|
||||
it('инвертирует true', () => {
|
||||
expect(DEFAULT_FUNCTIONS['НЕ'](true)).toBe(false)
|
||||
})
|
||||
it('инвертирует false', () => {
|
||||
expect(DEFAULT_FUNCTIONS['НЕ'](false)).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
// ==================== Строковые ====================
|
||||
|
||||
describe('СЦЕПИТЬ', () => {
|
||||
it('объединяет строки', () => {
|
||||
expect(DEFAULT_FUNCTIONS['СЦЕПИТЬ']('Привет', ' ', 'мир')).toBe(
|
||||
'Привет мир'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('ДЛСТР', () => {
|
||||
it('возвращает длину строки', () => {
|
||||
expect(DEFAULT_FUNCTIONS['ДЛСТР']('hello')).toBe(5)
|
||||
})
|
||||
it('возвращает 0 для пустого', () => {
|
||||
expect(DEFAULT_FUNCTIONS['ДЛСТР']('')).toBe(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('ПРОПИСН', () => {
|
||||
it('переводит в верхний регистр', () => {
|
||||
expect(DEFAULT_FUNCTIONS['ПРОПИСН']('hello')).toBe('HELLO')
|
||||
})
|
||||
})
|
||||
|
||||
describe('СТРОЧН', () => {
|
||||
it('переводит в нижний регистр', () => {
|
||||
expect(DEFAULT_FUNCTIONS['СТРОЧН']('HELLO')).toBe('hello')
|
||||
})
|
||||
})
|
||||
|
||||
describe('СЖПРОБЕЛЫ', () => {
|
||||
it('удаляет пробелы по краям', () => {
|
||||
expect(DEFAULT_FUNCTIONS['СЖПРОБЕЛЫ'](' hello ')).toBe('hello')
|
||||
})
|
||||
})
|
||||
|
||||
// ==================== Округление ====================
|
||||
|
||||
describe('ОКРУГЛ', () => {
|
||||
it('округляет до целого', () => {
|
||||
expect(DEFAULT_FUNCTIONS['ОКРУГЛ'](3.7)).toBe(4)
|
||||
expect(DEFAULT_FUNCTIONS['ОКРУГЛ'](3.2)).toBe(3)
|
||||
})
|
||||
it('округляет до разрядов', () => {
|
||||
expect(DEFAULT_FUNCTIONS['ОКРУГЛ'](3.456, 2)).toBe(3.46)
|
||||
})
|
||||
})
|
||||
|
||||
describe('ОКРУГЛВВЕРХ', () => {
|
||||
it('округляет вверх', () => {
|
||||
expect(DEFAULT_FUNCTIONS['ОКРУГЛВВЕРХ'](3.1)).toBe(4)
|
||||
})
|
||||
it('округляет вверх с разрядами', () => {
|
||||
expect(DEFAULT_FUNCTIONS['ОКРУГЛВВЕРХ'](3.411, 1)).toBe(3.5)
|
||||
})
|
||||
})
|
||||
|
||||
describe('ОКРУГЛВНИЗ', () => {
|
||||
it('округляет вниз', () => {
|
||||
expect(DEFAULT_FUNCTIONS['ОКРУГЛВНИЗ'](3.9)).toBe(3)
|
||||
})
|
||||
})
|
||||
|
||||
describe('ОКРВВЕРХ', () => {
|
||||
it('округляет вверх до кратного', () => {
|
||||
expect(DEFAULT_FUNCTIONS['ОКРВВЕРХ'](7, 5)).toBe(10)
|
||||
})
|
||||
it('возвращает 0 при кратном 0', () => {
|
||||
expect(DEFAULT_FUNCTIONS['ОКРВВЕРХ'](7, 0)).toBe(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('ОКРВНИЗ', () => {
|
||||
it('округляет вниз до кратного', () => {
|
||||
expect(DEFAULT_FUNCTIONS['ОКРВНИЗ'](7, 5)).toBe(5)
|
||||
})
|
||||
})
|
||||
|
||||
describe('ЦЕЛОЕ', () => {
|
||||
it('отбрасывает дробную часть вниз', () => {
|
||||
expect(DEFAULT_FUNCTIONS['ЦЕЛОЕ'](3.9)).toBe(3)
|
||||
expect(DEFAULT_FUNCTIONS['ЦЕЛОЕ'](-3.1)).toBe(-4)
|
||||
})
|
||||
})
|
||||
|
||||
describe('ОТБР', () => {
|
||||
it('отбрасывает дробную часть', () => {
|
||||
expect(DEFAULT_FUNCTIONS['ОТБР'](3.9)).toBe(3)
|
||||
expect(DEFAULT_FUNCTIONS['ОТБР'](-3.9)).toBe(-3)
|
||||
})
|
||||
it('с разрядами', () => {
|
||||
expect(DEFAULT_FUNCTIONS['ОТБР'](3.456, 2)).toBe(3.45)
|
||||
})
|
||||
})
|
||||
|
||||
describe('ОКРУГЛТ', () => {
|
||||
it('округляет до кратного', () => {
|
||||
expect(DEFAULT_FUNCTIONS['ОКРУГЛТ'](7, 5)).toBe(5)
|
||||
expect(DEFAULT_FUNCTIONS['ОКРУГЛТ'](8, 5)).toBe(10)
|
||||
})
|
||||
it('возвращает 0 при кратном 0', () => {
|
||||
expect(DEFAULT_FUNCTIONS['ОКРУГЛТ'](7, 0)).toBe(0)
|
||||
})
|
||||
})
|
||||
|
||||
// ==================== Случайные ====================
|
||||
|
||||
describe('СЛУЧ', () => {
|
||||
it('возвращает число от 0 до 1', () => {
|
||||
const result = DEFAULT_FUNCTIONS['СЛУЧ']()
|
||||
expect(result).toBeGreaterThanOrEqual(0)
|
||||
expect(result).toBeLessThan(1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('СЛУЧМЕЖДУ', () => {
|
||||
it('возвращает целое число в диапазоне', () => {
|
||||
for (let i = 0; i < 20; i++) {
|
||||
const result = DEFAULT_FUNCTIONS['СЛУЧМЕЖДУ'](1, 10) as number
|
||||
expect(result).toBeGreaterThanOrEqual(1)
|
||||
expect(result).toBeLessThanOrEqual(10)
|
||||
expect(Number.isInteger(result)).toBe(true)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('СЛУЧДР', () => {
|
||||
it('возвращает число в диапазоне', () => {
|
||||
for (let i = 0; i < 20; i++) {
|
||||
const result = DEFAULT_FUNCTIONS['СЛУЧДР'](1, 10, 2) as number
|
||||
expect(result).toBeGreaterThanOrEqual(1)
|
||||
expect(result).toBeLessThanOrEqual(10)
|
||||
}
|
||||
})
|
||||
it('ошибка при не-числах', () => {
|
||||
expect(DEFAULT_FUNCTIONS['СЛУЧДР']('a' as any, 10, 2)).toBe('#ЗНАЧ!')
|
||||
})
|
||||
it('ошибка при Infinity', () => {
|
||||
expect(DEFAULT_FUNCTIONS['СЛУЧДР'](Infinity, 10, 2)).toBe('#ЧИСЛО!')
|
||||
})
|
||||
it('меняет местами если низ > верх', () => {
|
||||
for (let i = 0; i < 20; i++) {
|
||||
const result = DEFAULT_FUNCTIONS['СЛУЧДР'](10, 1, 0) as number
|
||||
expect(result).toBeGreaterThanOrEqual(1)
|
||||
expect(result).toBeLessThanOrEqual(10)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user