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/) })