f7
f7 is a spreadsheet formula execution library
git clone https://git.vogt.world/f7.git
Log | Files | README.md | LICENSE.md
← All files
name: src/test/js/testutils/SpreadsheetTestRunner.ts
-rw-r--r--
4876
  1import { assert } from "chai";
  2import { it, describe } from "../testutils/TestUtils";
  3import { F7Exception } from "../../../main/js/errors/F7Exception";
  4import { Executor } from "../../../main/js/execution/Executor";
  5import { Complex } from "../../../main/js/models/common/Types";
  6import { CellObject } from "../../../main/js/spreadsheet/CellObject";
  7import { excelDataTypeFromActualType } from "../../../main/js/spreadsheet/ExcelDataType";
  8import { NamedRange } from "../../../main/js/spreadsheet/NamedRange";
  9import { Sheet } from "../../../main/js/spreadsheet/Sheet";
 10import { Spreadsheet } from "../../../main/js/spreadsheet/Spreadsheet";
 11import { Converters } from "../../../main/js/utils/Converters";
 12import { isNull } from "../../../main/js/common/utils/Types";
 13
 14export class MoreMath {
 15  static maxUInt32 = 4294967296;
 16
 17  // Generate a random UInt32 between 0 and `uint32::max`.
 18  static randomUInt32(): number {
 19    return Math.floor(Math.random() * this.maxUInt32);
 20  }
 21}
 22
 23/**
 24 * Runner class lets us build tests dynamically by adding individual cells,
 25 * without a ton of boiler-plating.
 26 */
 27export class SpreadsheetTestRunner {
 28  private sheets: { [key: string]: Sheet } = {};
 29  private expectedGridValues: { [index: string]: { [index: string]: Complex } } = {};
 30  private expectedEmptyKeys: { [index: string]: Array<string> } = {};
 31  private expectedEmptyKeysButComputed: { [index: string]: Array<string> } = {};
 32  private namedRanges: { [index: string]: NamedRange } = {};
 33
 34  addCell(sheet: string, a1Key: string, value: string): SpreadsheetTestRunner {
 35    if (!(sheet in this.sheets)) {
 36      this.sheets[sheet] = new Sheet({ id: `s${MoreMath.randomUInt32()}`, name: sheet });
 37    }
 38    const parsedType = value.startsWith("=") ? "f" : null;
 39    const parsedValue = value.startsWith("=") ? value.replace("=", "") : value;
 40    const cell: CellObject = {
 41      t: excelDataTypeFromActualType(value),
 42    };
 43    if (parsedType) {
 44      cell.f = parsedValue;
 45    } else {
 46      cell.v = parsedValue;
 47    }
 48    this.sheets[sheet].setCell(a1Key, cell);
 49    return this;
 50  }
 51
 52  addNamedRange(name: string, ref: string): SpreadsheetTestRunner {
 53    this.namedRanges[name] = {
 54      name,
 55      ref,
 56    };
 57    return this;
 58  }
 59
 60  addExpectedValue(gridName: string, a1Key: string, value: Complex): SpreadsheetTestRunner {
 61    if (!(gridName in this.expectedGridValues)) {
 62      this.expectedGridValues[gridName] = {};
 63    }
 64    this.expectedGridValues[gridName][a1Key] = value;
 65    return this;
 66  }
 67
 68  addExpectedEmptyValue(gridName: string, a1Key: string): SpreadsheetTestRunner {
 69    if (!(gridName in this.expectedEmptyKeys)) {
 70      this.expectedEmptyKeys[gridName] = [];
 71    }
 72    this.expectedEmptyKeys[gridName].push(a1Key);
 73    return this;
 74  }
 75
 76  addExpectedEmptyComputedValue(gridName: string, a1Key: string): SpreadsheetTestRunner {
 77    if (!(gridName in this.expectedEmptyKeysButComputed)) {
 78      this.expectedEmptyKeysButComputed[gridName] = [];
 79    }
 80    this.expectedEmptyKeysButComputed[gridName].push(a1Key);
 81    return this;
 82  }
 83
 84  /**
 85   * Run the tests in several steps:
 86   * 1) Build sheet executor and run it.
 87   * 2) For each expected entry, expect result.
 88   * 3) For each empty value, ensure it's empty.
 89   * 4) For each computed empty value, ensure it's empty.
 90   */
 91  public run() {
 92    // 1) Build sheet executor and run it.
 93    const executor = new Executor(new Spreadsheet(this.namedRanges, this.sheets));
 94    executor.execute();
 95
 96    // 2) For each expected entry, expect result.
 97    for (const gridName in this.expectedGridValues) {
 98      // Doing "OR-empty-logic" to get the IDE to stop complaining about it.
 99      for (const a1key in this.expectedGridValues[gridName] || {}) {
100        const cell = executor.getCell(gridName, a1key);
101        const expectedValue = this.expectedGridValues[gridName][a1key];
102        if (expectedValue instanceof F7Exception) {
103          const eName = Converters.castAsF7Exception(cell.v).name;
104          assert.deepEqual(
105            eName,
106            Converters.castAsF7Exception(expectedValue).name,
107            JSON.stringify(eName)
108          );
109        } else {
110          assert.deepEqual(cell.v, expectedValue as Complex);
111        }
112      }
113    }
114
115    // 3) For each empty value, ensure it's empty.
116    for (const sheet in this.expectedEmptyKeys) {
117      for (const a1key of this.expectedEmptyKeys[sheet]) {
118        const foundCell = executor.getCell(sheet, a1key);
119        if (isNull(foundCell)) {
120          assert.isNull(foundCell);
121        } else {
122          assert.fail(`${JSON.stringify(foundCell)} is not empty.`);
123        }
124      }
125    }
126    // 4) For each expected empty computed value, ensure it's empty.
127    for (const sheet in this.expectedEmptyKeys) {
128      for (const a1key of this.expectedEmptyKeys[sheet]) {
129        assert.isNull(executor.getCell(sheet, a1key));
130      }
131    }
132  }
133}