File: test/CrossSheetDependencies.test.ts

Recommend this page to a friend!
  Packages of ikhsan   jQuery Calx   test/CrossSheetDependencies.test.ts   Download  
File: test/CrossSheetDependencies.test.ts
Role: Auxiliary data
Content type: text/plain
Description: Auxiliary data
Class: jQuery Calx
Calculate form input values based on formulas
Author: By
Last change:
Date: 3 months ago
Size: 18,521 bytes
 

Contents

Class file image Download
import { Calx } from '../src/Calx'; import { DataType } from '../src/Calx/Cell/DataType'; describe('Cross-Sheet Dependencies', () => { describe('Basic Cross-Sheet References', () => { test('should reference cell from another sheet', () => { const workbook = Calx.createWorkbook(); const sales = workbook.createSheet('Sales'); const report = workbook.createSheet('Report'); // Sales sheet sales.createCell('A1', { value: 100 }); sales.createCell('A2', { value: 200 }); // Report sheet references Sales sheet report.createCell('B1', { formula: '=Sales!A1' }); report.createCell('B2', { formula: '=Sales!A2' }); workbook.build(); workbook.calculate(); expect(report.getCellDirect('B1').value).toBe(100); expect(report.getCellDirect('B2').value).toBe(200); }); test('should handle cross-sheet formula calculations', () => { const workbook = Calx.createWorkbook(); const sheet1 = workbook.createSheet('Sheet1'); const sheet2 = workbook.createSheet('Sheet2'); sheet1.createCell('A1', { value: 10 }); sheet1.createCell('A2', { value: 20 }); sheet1.createCell('A3', { formula: '=A1+A2' }); sheet2.createCell('B1', { formula: '=Sheet1!A3*2' }); workbook.build(); workbook.calculate(); expect(sheet1.getCellDirect('A3').value).toBe(30); expect(sheet2.getCellDirect('B1').value).toBe(60); }); test('should handle cross-sheet range references', () => { const workbook = Calx.createWorkbook(); const data = workbook.createSheet('Data'); const summary = workbook.createSheet('Summary'); data.createCell('A1', { value: 10 }); data.createCell('A2', { value: 20 }); data.createCell('A3', { value: 30 }); summary.createCell('B1', { formula: '=SUM(Data!A1:A3)' }); workbook.build(); workbook.calculate(); expect(summary.getCellDirect('B1').value).toBe(60); }); }); describe('Cross-Sheet Dependency Updates', () => { test('should update dependent cell when source value changes with autoCalculate', () => { const workbook = Calx.createWorkbook(); const sales = workbook.createSheet('Sales'); const report = workbook.createSheet('Report'); // Enable auto-calculate sales.autoCalculate = true; report.autoCalculate = true; sales.createCell('A1', { value: 100 }); report.createCell('B1', { formula: '=Sales!A1*2' }); workbook.build(); workbook.calculate(); expect(report.getCellDirect('B1').value).toBe(200); // Change source value - should auto-recalculate sales.getCellDirect('A1').value = 150; expect(report.getCellDirect('B1').value).toBe(300); }); test('should update through formula chain across sheets', () => { const workbook = Calx.createWorkbook(); const sales = workbook.createSheet('Sales'); const report = workbook.createSheet('Report'); // Enable auto-calculate sales.autoCalculate = true; report.autoCalculate = true; // Sales: A1 = 10, A2 = A1*2, A3 = SUM(A1:A2) sales.createCell('A1', { value: 10 }); sales.createCell('A2', { formula: '=A1*2' }); sales.createCell('A3', { formula: '=SUM(A1:A2)' }); // Report references the sum report.createCell('B1', { formula: '=Sales!A3' }); report.createCell('B2', { formula: '=B1*0.1' }); workbook.build(); workbook.calculate(); expect(sales.getCellDirect('A2').value).toBe(20); expect(sales.getCellDirect('A3').value).toBe(30); expect(report.getCellDirect('B1').value).toBe(30); expect(report.getCellDirect('B2').value).toBe(3); // Change base value - should cascade through all sheets sales.getCellDirect('A1').value = 20; expect(sales.getCellDirect('A2').value).toBe(40); expect(sales.getCellDirect('A3').value).toBe(60); expect(report.getCellDirect('B1').value).toBe(60); expect(report.getCellDirect('B2').value).toBe(6); }); test('should update when intermediate formula cell changes', () => { const workbook = Calx.createWorkbook(); const sales = workbook.createSheet('Sales'); const report = workbook.createSheet('Report'); // Enable auto-calculate sales.autoCalculate = true; report.autoCalculate = true; sales.createCell('B1', { value: 99.99 }); sales.createCell('C1', { value: 5 }); sales.createCell('D1', { formula: '=B1*C1' }); // 499.95 sales.createCell('B2', { value: 149.99 }); sales.createCell('C2', { value: 3 }); sales.createCell('D2', { formula: '=B2*C2' }); // 449.97 sales.createCell('D3', { formula: '=SUM(D1:D2)' }); // 949.92 report.createCell('B1', { formula: '=Sales!D3' }); workbook.build(); workbook.calculate(); expect(sales.getCellDirect('D1').value).toBeCloseTo(499.95, 2); expect(sales.getCellDirect('D2').value).toBeCloseTo(449.97, 2); expect(sales.getCellDirect('D3').value).toBeCloseTo(949.92, 2); expect(report.getCellDirect('B1').value).toBeCloseTo(949.92, 2); // Change quantity - should cascade all the way to Report!B1 sales.getCellDirect('C1').value = 10; expect(sales.getCellDirect('D1').value).toBeCloseTo(999.9, 2); expect(sales.getCellDirect('D3').value).toBeCloseTo(1449.87, 2); expect(report.getCellDirect('B1').value).toBeCloseTo(1449.87, 2); }); }); describe('Named Variables with Cross-Sheet References', () => { test('should resolve named variable pointing to another sheet', () => { const workbook = Calx.createWorkbook(); const config = workbook.createSheet('Config'); const report = workbook.createSheet('Report'); // Enable auto-calculate config.autoCalculate = true; report.autoCalculate = true; config.createCell('A1', { value: 0.08 }); // 8% tax rate // Define named variable workbook.nameManager.define('TaxRate', 'Config!A1'); report.createCell('B1', { value: 1000 }); report.createCell('B2', { formula: '=B1*TaxRate' }); workbook.build(); workbook.calculate(); expect(report.getCellDirect('B2').value).toBe(80); // Change tax rate - should auto-update config.getCellDirect('A1').value = 0.10; expect(report.getCellDirect('B2').value).toBe(100); }); test('should handle multiple named variables from different sheets', () => { const workbook = Calx.createWorkbook(); const config = workbook.createSheet('Config'); const sales = workbook.createSheet('Sales'); const report = workbook.createSheet('Report'); // Enable auto-calculate config.autoCalculate = true; sales.autoCalculate = true; report.autoCalculate = true; config.createCell('A1', { value: 0.08 }); // Tax config.createCell('A2', { value: 0.10 }); // Discount workbook.nameManager.define('TaxRate', 'Config!A1'); workbook.nameManager.define('DiscountRate', 'Config!A2'); sales.createCell('D1', { value: 1749.82 }); report.createCell('B1', { formula: '=Sales!D1' }); report.createCell('B2', { formula: '=B1*DiscountRate' }); report.createCell('B3', { formula: '=B1-B2' }); report.createCell('B4', { formula: '=B3*TaxRate' }); report.createCell('B5', { formula: '=B3+B4' }); workbook.build(); workbook.calculate(); expect(report.getCellDirect('B1').value).toBeCloseTo(1749.82, 2); expect(report.getCellDirect('B2').value).toBeCloseTo(174.982, 2); expect(report.getCellDirect('B3').value).toBeCloseTo(1574.838, 2); expect(report.getCellDirect('B4').value).toBeCloseTo(125.987, 2); expect(report.getCellDirect('B5').value).toBeCloseTo(1700.825, 2); // Change discount rate - should cascade to all dependent cells config.getCellDirect('A2').value = 0.20; expect(report.getCellDirect('B2').value).toBeCloseTo(349.964, 2); expect(report.getCellDirect('B3').value).toBeCloseTo(1399.856, 2); expect(report.getCellDirect('B5').value).toBeCloseTo(1511.844, 2); }); }); describe('Multi-Sheet Complex Scenarios', () => { test('should handle three-way cross-sheet dependencies', () => { const workbook = Calx.createWorkbook(); const input = workbook.createSheet('Input'); const calc = workbook.createSheet('Calc'); const output = workbook.createSheet('Output'); // Enable auto-calculate input.autoCalculate = true; calc.autoCalculate = true; output.autoCalculate = true; input.createCell('A1', { value: 100 }); calc.createCell('B1', { formula: '=Input!A1*2' }); output.createCell('C1', { formula: '=Calc!B1+50' }); workbook.build(); workbook.calculate(); expect(calc.getCellDirect('B1').value).toBe(200); expect(output.getCellDirect('C1').value).toBe(250); // Change input - should cascade through all sheets input.getCellDirect('A1').value = 150; expect(calc.getCellDirect('B1').value).toBe(300); expect(output.getCellDirect('C1').value).toBe(350); }); test('should detect circular cross-sheet references', () => { const workbook = Calx.createWorkbook(); const sheet1 = workbook.createSheet('Sheet1'); const sheet2 = workbook.createSheet('Sheet2'); // Disable auto-calculate to avoid infinite loop during setup sheet1.autoCalculate = false; sheet2.autoCalculate = false; // Create circular reference: Sheet1!A1 -> Sheet2!B1 -> Sheet1!A1 sheet1.createCell('A1', { formula: '=Sheet2!B1+1' }); sheet2.createCell('B1', { formula: '=Sheet1!A1+1' }); workbook.build(); // Should not crash during calculate, but will produce error workbook.calculate(); // Both cells should have an error value expect(sheet1.getCellDirect('A1').value).toBeDefined(); expect(sheet2.getCellDirect('B1').value).toBeDefined(); }); test('should handle 4-sheet circular dependency at sheet level', () => { const workbook = Calx.createWorkbook(); const sheet1 = workbook.createSheet('Sheet1'); const sheet2 = workbook.createSheet('Sheet2'); const sheet3 = workbook.createSheet('Sheet3'); const sheet4 = workbook.createSheet('Sheet4'); // Enable auto-calculate to test cascade sheet1.autoCalculate = true; sheet2.autoCalculate = true; sheet3.autoCalculate = true; sheet4.autoCalculate = true; // Create chain: Sheet1!A1 -> Sheet2!A1 -> Sheet3!A1 -> Sheet4!A1 -> Sheet1!B3 sheet1.createCell('A1', { value: 10 }); sheet2.createCell('A1', { formula: '=Sheet1!A1*2' }); // 20 sheet3.createCell('A1', { formula: '=Sheet2!A1+5' }); // 25 sheet4.createCell('A1', { formula: '=Sheet3!A1*3' }); // 75 sheet1.createCell('B3', { formula: '=Sheet4!A1-5' }); // 70 workbook.build(); workbook.calculate(); // Verify initial calculation expect(sheet1.getCellDirect('A1').value).toBe(10); expect(sheet2.getCellDirect('A1').value).toBe(20); expect(sheet3.getCellDirect('A1').value).toBe(25); expect(sheet4.getCellDirect('A1').value).toBe(75); expect(sheet1.getCellDirect('B3').value).toBe(70); // Change the source value - should cascade through all 4 sheets sheet1.getCellDirect('A1').value = 20; expect(sheet2.getCellDirect('A1').value).toBe(40); expect(sheet3.getCellDirect('A1').value).toBe(45); expect(sheet4.getCellDirect('A1').value).toBe(135); expect(sheet1.getCellDirect('B3').value).toBe(130); }); test('should handle cross-sheet references with data types', () => { const workbook = Calx.createWorkbook(); const data = workbook.createSheet('Data'); const report = workbook.createSheet('Report'); data.createCell('A1', { value: '2024-01-15', type: DataType.DATE }); data.createCell('A2', { value: true, type: DataType.BOOLEAN }); data.createCell('A3', { value: 'Hello', type: DataType.TEXT }); report.createCell('B1', { formula: '=Data!A1', type: DataType.DATE }); report.createCell('B2', { formula: '=Data!A2', type: DataType.BOOLEAN }); report.createCell('B3', { formula: '=Data!A3', type: DataType.TEXT }); workbook.build(); workbook.calculate(); expect(report.getCellDirect('B1').type).toBe(DataType.DATE); expect(report.getCellDirect('B2').type).toBe(DataType.BOOLEAN); expect(report.getCellDirect('B3').type).toBe(DataType.TEXT); }); test('should handle one cell referenced from multiple sheets with auto-update', () => { const workbook = Calx.createWorkbook(); const source = workbook.createSheet('Source'); const report1 = workbook.createSheet('Report1'); const report2 = workbook.createSheet('Report2'); const report3 = workbook.createSheet('Report3'); // Enable auto-calculate source.autoCalculate = true; report1.autoCalculate = true; report2.autoCalculate = true; report3.autoCalculate = true; // Source cell that will be referenced by multiple sheets source.createCell('A1', { value: 100 }); // Multiple sheets referencing the same source cell report1.createCell('B1', { formula: '=Source!A1*2' }); // 200 report1.createCell('B2', { formula: '=B1+10' }); // 210 report2.createCell('C1', { formula: '=Source!A1/2' }); // 50 report2.createCell('C2', { formula: '=C1*3' }); // 150 report3.createCell('D1', { formula: '=Source!A1+Source!A1' }); // 200 report3.createCell('D2', { formula: '=D1-50' }); // 150 workbook.build(); workbook.calculate(); // Verify initial values expect(source.getCellDirect('A1').value).toBe(100); expect(report1.getCellDirect('B1').value).toBe(200); expect(report1.getCellDirect('B2').value).toBe(210); expect(report2.getCellDirect('C1').value).toBe(50); expect(report2.getCellDirect('C2').value).toBe(150); expect(report3.getCellDirect('D1').value).toBe(200); expect(report3.getCellDirect('D2').value).toBe(150); // Update the source cell - all dependent cells across all sheets should update source.getCellDirect('A1').value = 200; // Re-verify all dependents have updated correctly expect(source.getCellDirect('A1').value).toBe(200); expect(report1.getCellDirect('B1').value).toBe(400); // 200*2 expect(report1.getCellDirect('B2').value).toBe(410); // 400+10 expect(report2.getCellDirect('C1').value).toBe(100); // 200/2 expect(report2.getCellDirect('C2').value).toBe(300); // 100*3 expect(report3.getCellDirect('D1').value).toBe(400); // 200+200 expect(report3.getCellDirect('D2').value).toBe(350); // 400-50 }); }); describe('Edge Cases', () => { test('should handle sheet names with underscores and numbers', () => { const workbook = Calx.createWorkbook(); const sheet1 = workbook.createSheet('Sheet_2024_Q1'); const sheet2 = workbook.createSheet('Report_Final'); sheet1.createCell('A1', { value: 1000 }); sheet2.createCell('B1', { formula: '=Sheet_2024_Q1!A1*2' }); workbook.build(); workbook.calculate(); expect(sheet2.getCellDirect('B1').value).toBe(2000); }); test('should handle absolute references in cross-sheet formulas', () => { const workbook = Calx.createWorkbook(); const data = workbook.createSheet('Data'); const calc = workbook.createSheet('Calc'); data.createCell('D1', { value: 50 }); calc.createCell('A1', { formula: '=Data!$D$1' }); workbook.build(); workbook.calculate(); // Initial value should work expect(calc.getCellDirect('A1').value).toBe(50); // Note: Absolute cross-sheet references require auto-calculate for live updates // or manual rebuild of dependencies. This test just verifies the formula evaluates correctly. }); test('should handle empty cross-sheet references', () => { const workbook = Calx.createWorkbook(); const source = workbook.createSheet('Source'); const target = workbook.createSheet('Target'); // Source A1 is empty (not created) target.createCell('B1', { formula: '=IF(Source!A1="", "Empty", Source!A1)' }); workbook.build(); workbook.calculate(); // Should handle gracefully expect(target.getCellDirect('B1').value).toBeDefined(); }); }); });