Files
Web_FreeCAD_Bitbybit/tests/spreadsheet.test.ts

59 lines
2.8 KiB
TypeScript

import assert from 'node:assert/strict'
import test from 'node:test'
import { createSpreadsheet } from '../src/facade/spreadsheet'
test('Spreadsheet evaluates unit formulas and aliases deterministically', () => {
const sheet = createSpreadsheet('sheet', 'Parameters')
sheet.setCell('A1', '6 mm', { alias: 'Width' })
sheet.setCell('A2', '=Width * 2', { alias: 'Length' })
sheet.setCell('B1', '=max(A1, 4 mm)')
const evaluation = sheet.evaluate()
assert.equal(evaluation.errors.length, 0)
assert.deepEqual(evaluation.cells.A1.value, { value: 6, dimension: 'length' })
assert.deepEqual(evaluation.cells.A2.value, { value: 12, dimension: 'length' })
assert.deepEqual(evaluation.cells.B1.value, { value: 6, dimension: 'length' })
assert.deepEqual(evaluation.dependencies.map(({ source, target, reference }) => `${source}:${target}:${reference}`).sort(), ['A2:A1:Width', 'B1:A1:A1'])
})
test('Spreadsheet rejects duplicate aliases and reports dependency cycles', () => {
const sheet = createSpreadsheet()
sheet.setCell('A1', 1, { alias: 'Count' })
assert.throws(() => sheet.setCell('A2', 2, { alias: 'Count' }), /already used/)
sheet.setCell('B1', '=C1 + 1')
sheet.setCell('C1', '=B1 + 1')
const evaluation = sheet.evaluate()
assert.equal(evaluation.cycles.length > 0, true)
assert.equal(evaluation.cells.B1.error?.code, 'DEPENDENCY_CYCLE')
assert.equal(evaluation.cells.C1.error?.code, 'DEPENDENCY_CYCLE')
})
test('Spreadsheet preserves binding metadata and exports escaped CSV', () => {
const sheet = createSpreadsheet()
sheet.setCell('$A$1', '6 mm')
sheet.setAlias('A1', 'Width')
sheet.setCell('B1', '=Width * 2')
const binding = sheet.bindProperty('Width', 'pad', 'Length')
assert.deepEqual(binding, { alias: 'Width', objectId: 'pad', propertyName: 'Length' })
assert.deepEqual(sheet.snapshot().bindings, [binding])
assert.match(sheet.exportCsv(), /^6 length,12 length$/)
assert.throws(() => sheet.setCell('1A', 1), /Invalid spreadsheet cell address/)
assert.throws(() => sheet.bindProperty('Missing', 'pad', 'Length'), /does not exist/)
})
test('Spreadsheet preserves styles, merged ranges, named ranges and address shifts', () => {
const sheet = createSpreadsheet()
sheet.setCell('A1', 1)
sheet.setCell('A2', '=A1 + 1')
sheet.setStyle('A1:B2', { background: '#e6f4f1', bold: true, numberFormat: '0.00' })
sheet.mergeCells('B1:A2')
sheet.setNamedRange('Inputs', 'A1:A2')
sheet.insertRows(2)
sheet.insertColumns(2)
const snapshot = sheet.snapshot()
assert.equal(snapshot.cells.find((cell) => cell.address === 'A1')?.style?.bold, true)
assert.deepEqual(snapshot.namedRanges, [{ name: 'Inputs', range: 'A1:A3' }])
assert.deepEqual(snapshot.mergedRanges, ['A1:C3'])
assert.equal(snapshot.cells.find((cell) => cell.address === 'A3')?.input, '=A1 + 1')
assert.throws(() => sheet.setStyle('A1', { color: 'red' }), /invalid color/)
})