spreadsheet
typeScript/javascript spreadsheet parser, with formulas.
git clone https://git.vogt.world/spreadsheet.git
Log | Files | README.md
← Commit log
commit
message
[Cell] setting and getting Cell fields, more thurough testing
author
Ben Vogt <[email protected]>
date
2017-09-04 16:40:46
stats
8 file(s) changed, 201 insertions(+), 41 deletions(-)
files
DOCS.md
TODO.md
dist/Cell.js
dist/Sheet.js
src/Cell.ts
src/Sheet.ts
tests/CellTest.ts
tests/Utils/Asserts.ts
  1diff --git a/DOCS.md b/DOCS.md
  2index 3e408c8..737ce5a 100644
  3--- a/DOCS.md
  4+++ b/DOCS.md
  5@@ -24,7 +24,10 @@
  6 ### TO_PERCENT 
  7 
  8 ```
  9-  Converts a number to a percent value where 1 = 100
 10+  Converts a number to a percent value where 1 = 100 percent. 
 11+@param value - Value to convert. If the input is a number, will return as a percent value. If value is non-numeric, will return value unchanged. 
 12+@returns {any} 
 13+@constructor
 14 ```
 15 
 16 ### TO_TEXT 
 17diff --git a/TODO.md b/TODO.md
 18index 07d1804..0eb49fb 100644
 19--- a/TODO.md
 20+++ b/TODO.md
 21@@ -5,9 +5,6 @@
 22 Instead of having non-primitives, (i.e. Date, DateTime, Time, Dollar), cells should have formats based on the highest-order type that was used during the compilation and execution of a cell's dependency. For example, `DATE` might return a number, but the cell that called `DATE` would be aware of it calling a formula that returns an non-primitive type, and would display the returned number as a Date. If you're using `DATE` in conjunction with `DOLLAR` it would still display the returned value as a Date. The hierarchy would look like: [Date, DateTime, Time, Dollar, number, boolean, string]. Advantages to this would include not having to cast down when using primitive operators, and flexibility in display. It would also simplify the types themselves, by having types be constants and just having helpers to convert, display, and do normal operations with them. Requires changes to `TO_DATE`, `TO_PERCENT`, `TO_DOLLAR`, and `TO_TEXT`.
 23 
 24 
 25-### Cell.rawFormulaText does not get reset when updating a cell for the second time.
 26-
 27-
 28 ### CONVERT could offer more accurate conversions for units in the same system
 29 For example 64 tbs to a qt.
 30 
 31diff --git a/dist/Cell.js b/dist/Cell.js
 32index f7b249d..c88c5b9 100644
 33--- a/dist/Cell.js
 34+++ b/dist/Cell.js
 35@@ -1,5 +1,31 @@
 36 "use strict";
 37+var __extends = (this && this.__extends) || (function () {
 38+    var extendStatics = Object.setPrototypeOf ||
 39+        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
 40+        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
 41+    return function (d, b) {
 42+        extendStatics(d, b);
 43+        function __() { this.constructor = d; }
 44+        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
 45+    };
 46+})();
 47 exports.__esModule = true;
 48+var CELL_ID_ERROR = "CELL_ID_ERROR";
 49+exports.CELL_ID_ERROR = CELL_ID_ERROR;
 50+/**
 51+ * Represents a cell id error, and is thrown when a cells id does not conform to A1 notation.
 52+ */
 53+var CellIdError = (function (_super) {
 54+    __extends(CellIdError, _super);
 55+    function CellIdError(msg) {
 56+        var _this = _super.call(this) || this;
 57+        _this.message = msg;
 58+        _this.name = CELL_ID_ERROR;
 59+        return _this;
 60+    }
 61+    return CellIdError;
 62+}(Error));
 63+exports.CellIdError = CellIdError;
 64 /**
 65  * Cell represents a cell in the spreadsheet. It contains a nullable rawFormulaText, and a value, which is not nullable unless
 66  * the parsing of the rawFormulaText results in an error.
 67@@ -18,6 +44,9 @@ var Cell = (function () {
 68         this.typedValue = null;
 69         this.dependencies = [];
 70         this.error = null;
 71+        if (!id.match(/^(?:[A-Za-z]+[0-9]+)$/)) {
 72+            throw new CellIdError("Cell id " + id + " not valid");
 73+        }
 74         var key = parseKey(id);
 75         this.id = id;
 76         this.row = key.y;
 77@@ -77,21 +106,13 @@ var Cell = (function () {
 78     Cell.prototype.hasFormula = function () {
 79         return this.rawFormulaText !== null;
 80     };
 81-    /**
 82-     * Set the value of this cell. If this cell has a primitive value (does not contain a rawFormulaText), it could be set to a
 83-     * value while the rawFormulaText field is still null.
 84-     * @param value to set
 85-     */
 86-    Cell.prototype.setValue = function (value) {
 87-        this.typedValue = value;
 88-    };
 89     /**
 90      * Sets the value or rawFormulaText for this cell. If the input begins with =, then it is considered to be a rawFormulaText. If it
 91      * is not, then it is a value, and set as the raw value for this cell.
 92      * @param rawFormula
 93      */
 94-    Cell.prototype.setRawValue = function (rawFormula) {
 95-        if (rawFormula.charAt(0) === "=") {
 96+    Cell.prototype.setValue = function (rawFormula) {
 97+        if (typeof rawFormula === "string" && rawFormula.charAt(0) === "=") {
 98             this.rawFormulaText = rawFormula.substr(1);
 99         }
100         else {
101@@ -99,7 +120,15 @@ var Cell = (function () {
102         }
103     };
104     /**
105-     * Get the value of this cell. Since value could be null do to an error in the rawFormulaText, this could return null.
106+     * Gets the rawFormulaText for this cell, which is either null or a string.
107+     * @returns {string}
108+     */
109+    Cell.prototype.getRawFormulaText = function () {
110+        return this.rawFormulaText;
111+    };
112+    /**
113+     * Get the value of this cell if a value is present. If this cell was given a formula but not a value, this may return
114+     * null.
115      * @returns {any}
116      */
117     Cell.prototype.getValue = function () {
118@@ -157,7 +186,7 @@ var Cell = (function () {
119     /**
120      * Build a cell with an id and value.
121      * @param id - A1-notation id or key.
122-     * @param value - value of the cell.
123+     * @param value - value of the cell as a string
124      * @returns {Cell}
125      * @constructor
126      */
127diff --git a/dist/Sheet.js b/dist/Sheet.js
128index d6e27e2..9c4844b 100644
129--- a/dist/Sheet.js
130+++ b/dist/Sheet.js
131@@ -135,7 +135,7 @@ var Sheet = (function () {
132          */
133         Matrix.prototype.setCell = function (id, rawFormula) {
134             var cell = new Cell_1.Cell(id);
135-            cell.setRawValue(rawFormula);
136+            cell.setValue(rawFormula);
137             registerCellInMatrix(cell);
138             recalculateCellDependencies(cell);
139         };
140diff --git a/src/Cell.ts b/src/Cell.ts
141index 5557686..071cb02 100644
142--- a/src/Cell.ts
143+++ b/src/Cell.ts
144@@ -1,3 +1,16 @@
145+const CELL_ID_ERROR = "CELL_ID_ERROR";
146+
147+/**
148+ * Represents a cell id error, and is thrown when a cells id does not conform to A1 notation.
149+ */
150+class CellIdError extends Error {
151+  constructor(msg: string) {
152+    super();
153+    this.message = msg;
154+    this.name = CELL_ID_ERROR;
155+  }
156+}
157+
158 /**
159  * Cell represents a cell in the spreadsheet. It contains a nullable rawFormulaText, and a value, which is not nullable unless
160  * the parsing of the rawFormulaText results in an error.
161@@ -20,6 +33,9 @@ class Cell {
162    * @param id key of the cell in A1-format.
163    */
164   constructor(id: string) {
165+    if (!id.match(/^(?:[A-Za-z]+[0-9]+)$/)) {
166+      throw new CellIdError("Cell id " + id + " not valid");
167+    }
168     let key = parseKey(id);
169 
170     this.id = id;
171@@ -88,22 +104,13 @@ class Cell {
172     return this.rawFormulaText !== null;
173   }
174 
175-  /**
176-   * Set the value of this cell. If this cell has a primitive value (does not contain a rawFormulaText), it could be set to a
177-   * value while the rawFormulaText field is still null.
178-   * @param value to set
179-   */
180-  setValue(value: any) {
181-    this.typedValue = value;
182-  }
183-
184   /**
185    * Sets the value or rawFormulaText for this cell. If the input begins with =, then it is considered to be a rawFormulaText. If it
186    * is not, then it is a value, and set as the raw value for this cell.
187    * @param rawFormula
188    */
189-  setRawValue(rawFormula: string) {
190-    if (rawFormula.charAt(0) === "=") {
191+  setValue(rawFormula: string) {
192+    if (typeof rawFormula === "string" && rawFormula.charAt(0) === "=") {
193       this.rawFormulaText = rawFormula.substr(1);
194     } else {
195       this.typedValue = rawFormula;
196@@ -111,7 +118,17 @@ class Cell {
197   }
198 
199   /**
200-   * Get the value of this cell. Since value could be null do to an error in the rawFormulaText, this could return null.
201+   * Gets the rawFormulaText for this cell, which is either null or a string.
202+   * @returns {string}
203+   */
204+  getRawFormulaText() : string | null {
205+    return this.rawFormulaText;
206+  }
207+
208+
209+  /**
210+   * Get the value of this cell if a value is present. If this cell was given a formula but not a value, this may return
211+   * null.
212    * @returns {any}
213    */
214   getValue() : any {
215@@ -177,7 +194,7 @@ class Cell {
216   /**
217    * Build a cell with an id and value.
218    * @param id - A1-notation id or key.
219-   * @param value - value of the cell.
220+   * @param value - value of the cell as a string
221    * @returns {Cell}
222    * @constructor
223    */
224@@ -211,5 +228,7 @@ function parseKey(cell) {
225 }
226 
227 export {
228-  Cell
229+  Cell,
230+  CellIdError,
231+  CELL_ID_ERROR
232 }
233\ No newline at end of file
234diff --git a/src/Sheet.ts b/src/Sheet.ts
235index e79987e..8e48590 100644
236--- a/src/Sheet.ts
237+++ b/src/Sheet.ts
238@@ -161,7 +161,7 @@ let Sheet = (function () {
239      */
240     setCell(id: string, rawFormula: string) {
241       let cell = new Cell(id);
242-      cell.setRawValue(rawFormula);
243+      cell.setValue(rawFormula);
244       registerCellInMatrix(cell);
245       recalculateCellDependencies(cell);
246     }
247diff --git a/tests/CellTest.ts b/tests/CellTest.ts
248index a8bab0a..9ecee59 100644
249--- a/tests/CellTest.ts
250+++ b/tests/CellTest.ts
251@@ -1,46 +1,148 @@
252 import {
253-  Cell
254+  Cell,
255+  CELL_ID_ERROR
256 } from "../src/Cell";
257 import {
258-  assertEquals,
259   assertArrayEquals,
260-  test, assertIsNull
261+  assertEquals,
262+  assertIsNull,
263+  catchAndAssertEquals,
264+  test
265 } from "./Utils/Asserts";
266 
267 test("Cell.constructor", function(){
268-  var cell = new Cell("A1");
269+  let cell = new Cell("A1");
270   assertEquals(cell.getColumn(), 0);
271   assertEquals(cell.getRow(), 0);
272   assertArrayEquals(cell.getDependencies(), []);
273   assertEquals(cell.getId(), "A1");
274   assertIsNull(cell.getFormula());
275   assertEquals(cell.hasFormula(), false);
276+  catchAndAssertEquals(function () {
277+    new Cell("C");
278+  }, CELL_ID_ERROR)
279+  catchAndAssertEquals(function () {
280+    new Cell("111");
281+  }, CELL_ID_ERROR)
282+  catchAndAssertEquals(function () {
283+    new Cell("BAD11BAD");
284+  }, CELL_ID_ERROR)
285 });
286 
287 test("Cell.updateDependencies", function(){
288-  var one = new Cell("A1");
289+  let one = new Cell("A1");
290   one.updateDependencies(["B2", "C1", "D12", "D13"]);
291   assertArrayEquals(one.getDependencies(), ["B2", "C1", "D12", "D13"]);
292   one.updateDependencies(["M4"]);
293   assertArrayEquals(one.getDependencies(), ["B2", "C1", "D12", "D13", "M4"]);
294 });
295 
296-test("Cell.setValue", function(){
297-  var v = new Cell("A1");
298+test("Cell.getDependencies", function(){
299+  let one = new Cell("A1");
300+  assertArrayEquals(one.getDependencies(), []);
301+  one.updateDependencies(["M4"]);
302+  assertArrayEquals(one.getDependencies(), ["M4"]);
303+});
304+
305+test("Cell.getColumn", function(){
306+  assertEquals(Cell.BuildFrom("A1", 0).getColumn(), 0);
307+  assertEquals(Cell.BuildFrom("B1", 0).getColumn(), 1);
308+  assertEquals(Cell.BuildFrom("M1", 0).getColumn(), 12);
309+  assertEquals(Cell.BuildFrom("N1", 0).getColumn(), 13);
310+  assertEquals(Cell.BuildFrom("AA1", 0).getColumn(), 26);
311+  assertEquals(Cell.BuildFrom("AB1", 0).getColumn(), 27);
312+  assertEquals(Cell.BuildFrom("AM1", 0).getColumn(), 38);
313+});
314+
315+test("Cell.getRow", function(){
316+  assertEquals(Cell.BuildFrom("A1", 0).getRow(), 0);
317+  assertEquals(Cell.BuildFrom("B2", 0).getRow(), 1);
318+  assertEquals(Cell.BuildFrom("M13", 0).getRow(), 12);
319+  assertEquals(Cell.BuildFrom("N14", 0).getRow(), 13);
320+  assertEquals(Cell.BuildFrom("AA27", 0).getRow(), 26);
321+  assertEquals(Cell.BuildFrom("AB28", 0).getRow(), 27);
322+  assertEquals(Cell.BuildFrom("AM39", 0).getRow(), 38);
323+});
324+
325+test("Cell.getId", function(){
326+  assertEquals(Cell.BuildFrom("A1", 0).getId(), "A1");
327+  assertEquals(Cell.BuildFrom("M1400", 0).getId(), "M1400");
328+});
329+
330+test("Cell.setValue, Cell.getValue", function(){
331+  let v = new Cell("A1");
332   v.setValue("100");
333-  assertEquals("100", v.getValue());
334+  assertEquals(v.getValue(), "100");
335+  v = new Cell("A1");
336+  v.setValue("=SUM(10, 10)");
337+  assertIsNull(v.getValue());
338+});
339+
340+test("Cell.clearValue", function(){
341+  let v = Cell.BuildFrom("A1", 100);
342+  assertEquals(v.getValue(), 100);
343+  v.clearValue();
344+  assertIsNull(v.getValue());
345+});
346+
347+test("Cell.setError, Cell.getError", function(){
348+  let v = Cell.BuildFrom("A1", 100);
349+  let e = new Error("e");
350+  assertIsNull(v.getError());
351+  v.setError(e);
352+  assertEquals(v.getError(), e);
353+});
354+
355+test("Cell.hasError", function(){
356+  let v = Cell.BuildFrom("A1", 100);
357+  let e = new Error("e");
358+  assertEquals(v.hasError(), false);
359+  v.setError(e);
360+  assertEquals(v.hasError(), true);
361+});
362+
363+test("Cell.getFormula", function(){
364+  assertIsNull(Cell.BuildFrom("A1", "100").getFormula());
365+  assertEquals(Cell.BuildFrom("A1", "=SUM(1, 2)").getFormula(), "SUM(1, 2)");
366+  assertEquals(Cell.BuildFrom("A1", "= 100,000,000").getFormula(), " 100,000,000");
367+});
368+
369+test("Cell.hasFormula", function(){
370+  assertEquals(Cell.BuildFrom("A1", "100").hasFormula(), false);
371+  assertEquals(Cell.BuildFrom("A1", "=SUM(1, 2)").hasFormula(), true);
372+  assertEquals(Cell.BuildFrom("A1", "= 100,000,000").hasFormula(), true);
373+});
374+
375+test("Cell.getRawFormulaText", function(){
376+  assertIsNull(Cell.BuildFrom("A1", "100").getRawFormulaText());
377+  assertEquals(Cell.BuildFrom("A1", "=10e1").getRawFormulaText(), "10e1");
378+  assertEquals(Cell.BuildFrom("A1", "=SUM(1, 2)").getRawFormulaText(), "SUM(1, 2)");
379+  assertEquals(Cell.BuildFrom("A1", "= 100,000,000").getRawFormulaText(), " 100,000,000");
380 });
381 
382 test("Cell.isBlank", function(){
383-  var v = new Cell("A1");
384+  let v = new Cell("A1");
385   assertIsNull(v.getValue());
386   assertIsNull(v.getError());
387   assertEquals(v.isBlank(), true);
388 });
389 
390 test("Cell.BuildFrom", function(){
391-  var v = Cell.BuildFrom("A1", 10);
392+  let v = Cell.BuildFrom("A1", 10);
393   assertEquals(v.getValue(), 10);
394   assertIsNull(v.getError());
395   assertEquals(v.isBlank(), false);
396 });
397+
398+test("Cell.tosString", function(){
399+  assertEquals(new Cell("A1").toString(), "id=A1, value=null, rawFormulaText=null, error=null");
400+  assertEquals(Cell.BuildFrom("A1", 100).toString(), "id=A1, value=100, rawFormulaText=null, error=null");
401+  assertEquals(Cell.BuildFrom("A1", "100").toString(), "id=A1, value=100, rawFormulaText=null, error=null");
402+  assertEquals(Cell.BuildFrom("A1", "=SUM(A1:A10)").toString(), "id=A1, value=null, rawFormulaText=SUM(A1:A10), error=null");
403+});
404+
405+test("Cell.equals", function(){
406+  assertEquals(new Cell("A1").equals(new Cell("A1")), true);
407+  assertEquals(new Cell("M100").equals(new Cell("A1")), false);
408+  assertEquals(Cell.BuildFrom("A1", 100).equals(Cell.BuildFrom("A1", 100)), true);
409+});
410\ No newline at end of file
411diff --git a/tests/Utils/Asserts.ts b/tests/Utils/Asserts.ts
412index 0de1f76..a0fc5d3 100644
413--- a/tests/Utils/Asserts.ts
414+++ b/tests/Utils/Asserts.ts
415@@ -46,7 +46,7 @@ function assertArrayEquals(actual: Array<any>, expected: Array<any>, ) {
416     console.trace();
417     return;
418   }
419-  for (var index in expected) {
420+  for (let index in expected) {
421     if (expected[index] != actual[index]) {
422       console.log("expected: ", expected, " actual:", actual);
423       console.trace();
424@@ -60,7 +60,7 @@ function assertArrayEquals(actual: Array<any>, expected: Array<any>, ) {
425  * @param expected error message
426  */
427 function catchAndAssertEquals(toExecute : Function, expected) {
428-  var toThrow = null;
429+  let toThrow = null;
430   try {
431     toExecute();
432     toThrow = true;