diff --git a/CHANGELOG.md b/CHANGELOG.md index b6ac86d232..1ede7334bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - GPU.js constructor needs to be provided directly to engine configuration. (#355) +- An input value should be preserved through serialization more precisely. A **Breaking change**: This changes the type of value returned via serialization. (#617) - A deprecated config option vlookupThreshold has been removed. (#620) ### Added diff --git a/src/CellContentParser.ts b/src/CellContentParser.ts index c52cd3d898..bf0f4af96e 100644 --- a/src/CellContentParser.ts +++ b/src/CellContentParser.ts @@ -173,9 +173,7 @@ export class CellContentParser { if (parsedDateNumber !== undefined) { return new CellContent.Number(parsedDateNumber) } else { - return new CellContent.String( - content.startsWith('\'') ? content.slice(1) : content, - ) + return new CellContent.String(content.startsWith('\'') ? content.slice(1) : content ) } } } else { @@ -183,4 +181,3 @@ export class CellContentParser { } } } - diff --git a/src/ClipboardOperations.ts b/src/ClipboardOperations.ts index c70ff59e46..fed79a0c89 100644 --- a/src/ClipboardOperations.ts +++ b/src/ClipboardOperations.ts @@ -5,6 +5,7 @@ import {AbsoluteCellRange} from './AbsoluteCellRange' import {invalidSimpleCellAddress, simpleCellAddress, SimpleCellAddress} from './Cell' +import {RawCellContent} from './CellContentParser' import {Operations} from './Operations' import {DependencyGraph} from './DependencyGraph' import {ValueCellVertexValue} from './DependencyGraph/ValueCellVertex' @@ -30,7 +31,8 @@ export enum ClipboardCellType { export interface ClipboardCellValue { type: ClipboardCellType.VALUE, - value: ValueCellVertexValue, + parsedValue: ValueCellVertexValue, + rawValue: RawCellContent, } export interface ClipboardCellEmpty { diff --git a/src/DependencyGraph/AddressMapping/AddressMapping.ts b/src/DependencyGraph/AddressMapping/AddressMapping.ts index c7db4ecbdb..af79f988cd 100644 --- a/src/DependencyGraph/AddressMapping/AddressMapping.ts +++ b/src/DependencyGraph/AddressMapping/AddressMapping.ts @@ -4,9 +4,10 @@ */ import {SimpleCellAddress} from '../../Cell' +import {RawCellContent} from '../../CellContentParser' import {EmptyValue, InterpreterValue} from '../../interpreter/InterpreterValue' import {ColumnsSpan, RowsSpan} from '../../Span' -import {MatrixVertex} from '../index' +import {EmptyCellVertex, MatrixVertex, ValueCellVertex} from '../index' import {CellVertex} from '../Vertex' import {ChooseAddressMapping} from './ChooseAddressMappingPolicy' import {IAddressMappingStrategy} from './IAddressMappingStrategy' @@ -77,6 +78,17 @@ export class AddressMapping { } } + public getRawValue(address: SimpleCellAddress): RawCellContent { + const vertex = this.getCell(address) + if(vertex instanceof ValueCellVertex) { + return vertex.getValues().rawValue + } else if (vertex instanceof MatrixVertex) { + return vertex.getMatrixCellRawValue(address) + } else { + return null + } + } + /** @inheritDoc */ public setCell(address: SimpleCellAddress, newVertex: CellVertex) { const sheetMapping = this.mapping.get(address.sheet) diff --git a/src/DependencyGraph/DependencyGraph.ts b/src/DependencyGraph/DependencyGraph.ts index 3d58aaf4af..4e7cf78936 100644 --- a/src/DependencyGraph/DependencyGraph.ts +++ b/src/DependencyGraph/DependencyGraph.ts @@ -12,6 +12,7 @@ import { simpleCellAddress, SimpleCellAddress } from '../Cell' +import {RawCellContent} from '../CellContentParser' import {CellDependency} from '../CellDependency' import {Config} from '../Config' import {ErrorMessage} from '../error-message' @@ -38,7 +39,7 @@ import {Graph, TopSortResult} from './Graph' import {MatrixMapping} from './MatrixMapping' import {RangeMapping} from './RangeMapping' import {SheetMapping} from './SheetMapping' -import {ValueCellVertexValue} from './ValueCellVertex' +import {RawAndParsedValue, ValueCellVertexValue} from './ValueCellVertex' import {FunctionRegistry} from '../interpreter/FunctionRegistry' import { EmptyValue, @@ -112,18 +113,18 @@ export class DependencyGraph { this.correctInfiniteRangesDependency(address) } - public setValueToCell(address: SimpleCellAddress, newValue: ValueCellVertexValue) { + public setValueToCell(address: SimpleCellAddress, value: RawAndParsedValue) { const vertex = this.addressMapping.getCell(address) this.ensureThatVertexIsNonMatrixCellVertex(vertex) if (vertex instanceof ValueCellVertex) { - const oldValue = vertex.getCellValue() - if (oldValue !== newValue) { - vertex.setCellValue(newValue) + const oldValue = vertex.getValues() + if (oldValue.rawValue !== value.rawValue) { + vertex.setValues(value) this.graph.markNodeAsSpecialRecentlyChanged(vertex) } } else { - const newVertex = new ValueCellVertex(newValue) + const newVertex = new ValueCellVertex(value.parsedValue, value.rawValue) this.exchangeOrAddGraphNode(vertex, newVertex) this.addressMapping.setCell(address, newVertex) this.graph.markNodeAsSpecialRecentlyChanged(newVertex) @@ -505,8 +506,8 @@ export class DependencyGraph { const adjacentNodes = this.graph.adjacentNodes(matrixVertex) for (const address of matrixRange.addresses(this)) { - const value = this.getCellValue(address) as ExtendedNumber // We wouldn't need that typecast if we would take values from Matrix - const valueVertex = new ValueCellVertex(value) + // We wouldn't need that typecast if we would take values from Matrix + const valueVertex = new ValueCellVertex(this.getCellValue(address) as ExtendedNumber, this.getRawValue(address)) this.addVertex(address, valueVertex) } @@ -605,6 +606,10 @@ export class DependencyGraph { return this.addressMapping.getCellValue(address) } + public getRawValue(address: SimpleCellAddress): RawCellContent { + return this.addressMapping.getRawValue(address) + } + public getScalarValue(address: SimpleCellAddress): InternalScalarValue { const value = this.addressMapping.getCellValue(address) if (value instanceof SimpleRangeValue) { diff --git a/src/DependencyGraph/MatrixVertex.ts b/src/DependencyGraph/MatrixVertex.ts index 7a0090c028..72770faa1e 100644 --- a/src/DependencyGraph/MatrixVertex.ts +++ b/src/DependencyGraph/MatrixVertex.ts @@ -56,6 +56,11 @@ export class MatrixVertex { return this.matrix.get(col, row) } + public getMatrixCellRawValue(address: SimpleCellAddress): number | undefined { + const val = this.getMatrixCellValue(address) + return val instanceof CellError ? undefined : val + } + public setMatrixCellValue(address: SimpleCellAddress, value: number): void { const col = address.col - this.cellAddress.col const row = address.row - this.cellAddress.row diff --git a/src/DependencyGraph/ValueCellVertex.ts b/src/DependencyGraph/ValueCellVertex.ts index d002e05f15..1c855935a0 100644 --- a/src/DependencyGraph/ValueCellVertex.ts +++ b/src/DependencyGraph/ValueCellVertex.ts @@ -4,32 +4,41 @@ */ import {CellError} from '../Cell' -import {ExtendedNumber} from '../interpreter/InterpreterValue' +import {RawCellContent} from '../CellContentParser' +import {ExtendedNumber, RawScalarValue} from '../interpreter/InterpreterValue' export type ValueCellVertexValue = ExtendedNumber | boolean | string | CellError +export interface RawAndParsedValue { + parsedValue: ValueCellVertexValue, + rawValue: RawCellContent, +} + /** * Represents vertex which keeps static cell value */ export class ValueCellVertex { /** Static cell value. */ - private cellValue: ValueCellVertexValue + constructor(private parsedValue: ValueCellVertexValue, private rawValue: RawCellContent) { + } - constructor(cellValue: ValueCellVertexValue) { - this.cellValue = cellValue + public getValues(): RawAndParsedValue { + return {parsedValue: this.parsedValue, rawValue: this.rawValue} + } + + public setValues(values: RawAndParsedValue) { + this.parsedValue = values.parsedValue + this.rawValue = values.rawValue } /** * Returns cell value stored in vertex */ public getCellValue(): ValueCellVertexValue { - return this.cellValue + return this.parsedValue } - /** - * Sets computed cell value stored in this vertex - */ - public setCellValue(cellValue: ValueCellVertexValue) { - this.cellValue = cellValue + public setCellValue(_cellValue: ValueCellVertexValue): never { + throw 'SetCellValue is deprecated for ValueCellVertex' } } diff --git a/src/GraphBuilder.ts b/src/GraphBuilder.ts index acbf1fb8c2..61747f1108 100644 --- a/src/GraphBuilder.ts +++ b/src/GraphBuilder.ts @@ -133,7 +133,7 @@ export class SimpleStrategy implements GraphBuilderStrategy { } else if (parsedCellContent instanceof CellContent.Empty) { /* we don't care about empty cells here */ } else { - const vertex = new ValueCellVertex(parsedCellContent.value) + const vertex = new ValueCellVertex(parsedCellContent.value, cellContent) this.columnIndex.add(getRawValue(parsedCellContent.value), address) this.dependencyGraph.addVertex(address, vertex) } @@ -204,7 +204,7 @@ export class MatrixDetectionStrategy implements GraphBuilderStrategy { } else if (parsedCellContent instanceof CellContent.Number) { matrixHeuristic.add(address) } else { - const vertex = new ValueCellVertex(parsedCellContent.value) + const vertex = new ValueCellVertex(parsedCellContent.value, cellContent) this.columnSearch.add(parsedCellContent.value, address) this.dependencyGraph.addVertex(address, vertex) } @@ -219,7 +219,7 @@ export class MatrixDetectionStrategy implements GraphBuilderStrategy { const elem = notMatrices[i] for (const address of elem.cells.reverse()) { const value = sheets[this.dependencyGraph.getSheetName(address.sheet)][address.row][address.col] - const vertex = new ValueCellVertex(Number(value)) + const vertex = new ValueCellVertex(Number(value), value) this.columnSearch.add(Number(value), address) this.dependencyGraph.addVertex(address, vertex) } @@ -234,7 +234,7 @@ export class MatrixDetectionStrategy implements GraphBuilderStrategy { export function buildMatrixVertex(ast: ProcedureAst, formulaAddress: SimpleCellAddress): MatrixVertex | ValueCellVertex { const size = checkMatrixSize(ast, formulaAddress) if (size instanceof CellError) { - return new ValueCellVertex(size) + return new ValueCellVertex(size, undefined) } return new MatrixVertex(formulaAddress, size.width, size.height, ast) } diff --git a/src/HyperFormula.ts b/src/HyperFormula.ts index bd258cebd3..110c98668a 100644 --- a/src/HyperFormula.ts +++ b/src/HyperFormula.ts @@ -643,7 +643,7 @@ export class HyperFormula implements TypedEmitter { * * @category Cells */ - public getCellSerialized(cellAddress: SimpleCellAddress): NoErrorCellValue { + public getCellSerialized(cellAddress: SimpleCellAddress): RawCellContent { this.ensureEvaluationIsNotSuspended() return this._serialization.getCellSerialized(cellAddress) } @@ -733,7 +733,7 @@ export class HyperFormula implements TypedEmitter { * * @category Sheets */ - public getSheetSerialized(sheetId: number): NoErrorCellValue[][] { + public getSheetSerialized(sheetId: number): RawCellContent[][] { this.ensureEvaluationIsNotSuspended() return this._serialization.getSheetSerialized(sheetId) } @@ -850,7 +850,7 @@ export class HyperFormula implements TypedEmitter { * * @category Sheets */ - public getAllSheetsSerialized(): Record { + public getAllSheetsSerialized(): Record { this.ensureEvaluationIsNotSuspended() return this._serialization.getAllSheetsSerialized() } @@ -2214,7 +2214,7 @@ export class HyperFormula implements TypedEmitter { * * @category Ranges */ - public getRangeSerialized(leftCorner: SimpleCellAddress, width: number, height: number): CellValue[][] { + public getRangeSerialized(leftCorner: SimpleCellAddress, width: number, height: number): RawCellContent[][] { const cellRange = AbsoluteCellRange.spanFrom(leftCorner, width, height) return cellRange.arrayOfAddressesInRange().map( (subarray) => subarray.map( diff --git a/src/Operations.ts b/src/Operations.ts index 24e6c01d21..3eb3be190c 100644 --- a/src/Operations.ts +++ b/src/Operations.ts @@ -5,7 +5,7 @@ import {EmptyValue, getRawValue} from './interpreter/InterpreterValue' import {ClipboardCell, ClipboardCellType} from './ClipboardOperations' -import {invalidSimpleCellAddress, simpleCellAddress, SimpleCellAddress} from './Cell' +import {CellError, invalidSimpleCellAddress, simpleCellAddress, SimpleCellAddress} from './Cell' import {AbsoluteCellRange} from './AbsoluteCellRange' import {absolutizeDependencies, filterDependenciesOutOfScope} from './absolutizeDependencies' import {CellContent, CellContentParser, RawCellContent} from './CellContentParser' @@ -24,7 +24,7 @@ import { SparseStrategy, ValueCellVertex } from './DependencyGraph' -import {ValueCellVertexValue} from './DependencyGraph/ValueCellVertex' +import {RawAndParsedValue, ValueCellVertexValue} from './DependencyGraph/ValueCellVertex' import {AddColumnsTransformer} from './dependencyTransformers/AddColumnsTransformer' import {AddRowsTransformer} from './dependencyTransformers/AddRowsTransformer' import {MoveCellsTransformer} from './dependencyTransformers/MoveCellsTransformer' @@ -444,7 +444,7 @@ export class Operations { public restoreCell(address: SimpleCellAddress, clipboardCell: ClipboardCell) { switch (clipboardCell.type) { case ClipboardCellType.VALUE: { - this.setValueToCell(clipboardCell.value, address) + this.setValueToCell(clipboardCell, address) break } case ClipboardCellType.FORMULA: { @@ -577,9 +577,9 @@ export class Operations { if (vertex === null || vertex instanceof EmptyCellVertex) { return {type: ClipboardCellType.EMPTY} } else if (vertex instanceof ValueCellVertex) { - return {type: ClipboardCellType.VALUE, value: vertex.getCellValue()} + return {type: ClipboardCellType.VALUE, ...vertex.getValues()} } else if (vertex instanceof MatrixVertex) { - return {type: ClipboardCellType.VALUE, value: vertex.getMatrixCellValue(address)} + return {type: ClipboardCellType.VALUE, parsedValue: vertex.getMatrixCellValue(address), rawValue: vertex.getMatrixCellRawValue(address)} } else if (vertex instanceof FormulaCellVertex) { return { type: ClipboardCellType.FORMULA, @@ -659,7 +659,7 @@ export class Operations { } else if (parsedCellContent instanceof CellContent.MatrixFormula) { throw new Error('Cant happen') } else { - this.setValueToCell(parsedCellContent.value, address) + this.setValueToCell({parsedValue: parsedCellContent.value, rawValue: newCellContent}, address) } } else { throw new Error('Illegal operation') @@ -676,11 +676,11 @@ export class Operations { } } - public setValueToCell(value: ValueCellVertexValue, address: SimpleCellAddress) { + public setValueToCell(value: RawAndParsedValue, address: SimpleCellAddress) { const oldValue = this.dependencyGraph.getCellValue(address) this.dependencyGraph.setValueToCell(address, value) - this.columnSearch.change(getRawValue(oldValue), getRawValue(value), address) - this.changes.addChange(value, address) + this.columnSearch.change(getRawValue(oldValue), getRawValue(value.parsedValue), address) + this.changes.addChange(value.parsedValue, address) } public setCellEmpty(address: SimpleCellAddress) { @@ -776,7 +776,7 @@ export class Operations { if (parsedCellContent instanceof CellContent.Empty) { this.setCellEmpty(address) } else { - this.setValueToCell(parsedCellContent.value, address) + this.setValueToCell({parsedValue: parsedCellContent.value, rawValue: expression}, address) } } } @@ -847,7 +847,7 @@ export class Operations { } else if (sourceVertex instanceof EmptyCellVertex) { this.setCellEmpty(expression.address) } else if (sourceVertex instanceof ValueCellVertex) { - this.setValueToCell(sourceVertex.getCellValue(), expression.address) + this.setValueToCell(sourceVertex.getValues(), expression.address) } } return this.dependencyGraph.fetchCellOrCreateEmpty(expression.address) diff --git a/src/Serialization.ts b/src/Serialization.ts index 8e05e99fff..199c0d4b62 100644 --- a/src/Serialization.ts +++ b/src/Serialization.ts @@ -4,17 +4,19 @@ */ import {simpleCellAddress, SimpleCellAddress} from './Cell' +import {RawCellContent} from './CellContentParser' import {CellValue, DetailedCellError, NoErrorCellValue} from './CellValue' import {Config} from './Config' import {DependencyGraph, FormulaCellVertex, MatrixVertex, ParsingErrorVertex} from './DependencyGraph' import {Exporter} from './Exporter' +import {RawScalarValue} from './interpreter/InterpreterValue' import {Maybe} from './Maybe' import {buildLexerConfig, Unparser} from './parser' import {NamedExpressionOptions, NamedExpressions} from './NamedExpressions' export interface SerializedNamedExpression { name: string, - expression: NoErrorCellValue, + expression: RawCellContent, scope: Maybe, options: Maybe, } @@ -44,17 +46,12 @@ export class Serialization { return undefined } - public getCellSerialized(address: SimpleCellAddress): NoErrorCellValue { + public getCellSerialized(address: SimpleCellAddress): RawCellContent { const formula: Maybe = this.getCellFormula(address) if (formula !== undefined) { return formula } else { - const value: CellValue = this.getCellValue(address) - if (value instanceof DetailedCellError) { - return this.config.translationPackage.getErrorTranslation(value.type) - } else { - return value - } + return this.getRawValue(address) } } @@ -62,6 +59,10 @@ export class Serialization { return this.exporter.exportValue(this.dependencyGraph.getScalarValue(address)) } + public getRawValue(address: SimpleCellAddress): RawCellContent { + return this.dependencyGraph.getRawValue(address) + } + public getSheetValues(sheet: number): CellValue[][] { return this.genericSheetGetter(sheet, (arg) => this.getCellValue(arg)) } @@ -110,7 +111,7 @@ export class Serialization { return result } - public getSheetSerialized(sheet: number): NoErrorCellValue[][] { + public getSheetSerialized(sheet: number): RawCellContent[][] { return this.genericSheetGetter(sheet, (arg) => this.getCellSerialized(arg)) } @@ -122,7 +123,7 @@ export class Serialization { return this.genericAllSheetsGetter((arg) => this.getSheetFormulas(arg)) } - public getAllSheetsSerialized(): Record { + public getAllSheetsSerialized(): Record { return this.genericAllSheetsGetter((arg) => this.getSheetSerialized(arg)) } diff --git a/test/address-mapping.spec.ts b/test/address-mapping.spec.ts index 5ce896942a..617fa56f70 100644 --- a/test/address-mapping.spec.ts +++ b/test/address-mapping.spec.ts @@ -11,7 +11,7 @@ import {adr} from './testUtils' const sharedExamples = (builder: (width: number, height: number) => AddressMapping) => { it('simple set', () => { const mapping = builder(1, 1) - const vertex = new ValueCellVertex(42) + const vertex = new ValueCellVertex(42, 42) const address = adr('A1') mapping.setCell(address, vertex) @@ -21,7 +21,7 @@ const sharedExamples = (builder: (width: number, height: number) => AddressMappi it('set and using different reference when get', () => { const mapping = builder(1, 1) - const vertex = new ValueCellVertex(42) + const vertex = new ValueCellVertex(42, 42) mapping.setCell(adr('A1'), vertex) @@ -37,7 +37,7 @@ const sharedExamples = (builder: (width: number, height: number) => AddressMappi it('get when there was already something in that column', () => { const mapping = builder(1, 2) - mapping.setCell(adr('A2'), new ValueCellVertex(42)) + mapping.setCell(adr('A2'), new ValueCellVertex(42, 42)) expect(mapping.getCell(adr('A1'))).toBe(null) }) @@ -50,8 +50,8 @@ const sharedExamples = (builder: (width: number, height: number) => AddressMappi it("set when there's already something in that column", () => { const mapping = builder(1, 2) - const vertex0 = new ValueCellVertex(42) - const vertex1 = new ValueCellVertex(42) + const vertex0 = new ValueCellVertex(42, 42) + const vertex1 = new ValueCellVertex(42, 42) mapping.setCell(adr('A1'), vertex0) mapping.setCell(adr('A2'), vertex1) @@ -62,8 +62,8 @@ const sharedExamples = (builder: (width: number, height: number) => AddressMappi it('set overrides old value', () => { const mapping = builder(1, 1) - const vertex0 = new ValueCellVertex(42) - const vertex1 = new ValueCellVertex(42) + const vertex0 = new ValueCellVertex(42, 42) + const vertex1 = new ValueCellVertex(42, 42) mapping.setCell(adr('A1'), vertex0) mapping.setCell(adr('A1'), vertex1) @@ -86,7 +86,7 @@ const sharedExamples = (builder: (width: number, height: number) => AddressMappi it('has when there was already something in that column', () => { const mapping = builder(1, 2) - mapping.setCell(adr('A2'), new ValueCellVertex(42)) + mapping.setCell(adr('A2'), new ValueCellVertex(42, 42)) expect(mapping.has(adr('A1'))).toBe(false) }) @@ -94,7 +94,7 @@ const sharedExamples = (builder: (width: number, height: number) => AddressMappi it('has when there is a value', () => { const mapping = builder(1, 1) - mapping.setCell(adr('A1'), new ValueCellVertex(42)) + mapping.setCell(adr('A1'), new ValueCellVertex(42, 42)) expect(mapping.has(adr('A1'))).toBe(true) }) @@ -102,37 +102,37 @@ const sharedExamples = (builder: (width: number, height: number) => AddressMappi it('addRows in the beginning of a mapping', () => { const mapping = builder(1, 1) - mapping.setCell(adr('A1'), new ValueCellVertex(42)) + mapping.setCell(adr('A1'), new ValueCellVertex(42, 42)) mapping.addRows(0, 0, 1) expect(mapping.getCell(adr('A1'))).toBe(null) - expect(mapping.fetchCell(adr('A2'))).toEqual(new ValueCellVertex(42)) + expect(mapping.fetchCell(adr('A2'))).toEqual(new ValueCellVertex(42, 42)) expect(mapping.getHeight(0)).toEqual(2) }) it('addRows in the middle of a mapping', () => { const mapping = builder(1, 2) - mapping.setCell(adr('A1'), new ValueCellVertex(42)) - mapping.setCell(adr('A2'), new ValueCellVertex(43)) + mapping.setCell(adr('A1'), new ValueCellVertex(42, 42)) + mapping.setCell(adr('A2'), new ValueCellVertex(43, 43)) mapping.addRows(0, 1, 1) - expect(mapping.fetchCell(adr('A1'))).toEqual(new ValueCellVertex(42)) + expect(mapping.fetchCell(adr('A1'))).toEqual(new ValueCellVertex(42, 42)) expect(mapping.getCell(adr('A2'))).toBe(null) - expect(mapping.fetchCell(adr('A3'))).toEqual(new ValueCellVertex(43)) + expect(mapping.fetchCell(adr('A3'))).toEqual(new ValueCellVertex(43, 43)) expect(mapping.getHeight(0)).toEqual(3) }) it('addRows in the end of a mapping', () => { const mapping = builder(1, 1) - mapping.setCell(adr('A1'), new ValueCellVertex(42)) + mapping.setCell(adr('A1'), new ValueCellVertex(42, 42)) mapping.addRows(0, 1, 1) - expect(mapping.fetchCell(adr('A1'))).toEqual(new ValueCellVertex(42)) + expect(mapping.fetchCell(adr('A1'))).toEqual(new ValueCellVertex(42, 42)) expect(mapping.getCell(adr('A2'))).toBe(null) expect(mapping.getHeight(0)).toEqual(2) }) @@ -140,44 +140,44 @@ const sharedExamples = (builder: (width: number, height: number) => AddressMappi it('addRows more than one row', () => { const mapping = builder(1, 2) - mapping.setCell(adr('A1'), new ValueCellVertex(42)) - mapping.setCell(adr('A2'), new ValueCellVertex(43)) + mapping.setCell(adr('A1'), new ValueCellVertex(42, 42)) + mapping.setCell(adr('A2'), new ValueCellVertex(43, 43)) mapping.addRows(0, 1, 3) - expect(mapping.fetchCell(adr('A1'))).toEqual(new ValueCellVertex(42)) + expect(mapping.fetchCell(adr('A1'))).toEqual(new ValueCellVertex(42, 42)) expect(mapping.getCell(adr('A2'))).toBe(null) expect(mapping.getCell(adr('A3'))).toBe(null) expect(mapping.getCell(adr('A4'))).toBe(null) - expect(mapping.fetchCell(adr('A5'))).toEqual(new ValueCellVertex(43)) + expect(mapping.fetchCell(adr('A5'))).toEqual(new ValueCellVertex(43, 43)) expect(mapping.getHeight(0)).toEqual(5) }) it('addRows when more than one column present', () => { const mapping = builder(2, 2) - mapping.setCell(adr('A1'), new ValueCellVertex(11)) - mapping.setCell(adr('B1'), new ValueCellVertex(12)) - mapping.setCell(adr('A2'), new ValueCellVertex(21)) - mapping.setCell(adr('B2'), new ValueCellVertex(22)) + mapping.setCell(adr('A1'), new ValueCellVertex(11, 11)) + mapping.setCell(adr('B1'), new ValueCellVertex(12, 12)) + mapping.setCell(adr('A2'), new ValueCellVertex(21, 21)) + mapping.setCell(adr('B2'), new ValueCellVertex(22, 22)) mapping.addRows(0, 1, 1) - expect(mapping.fetchCell(adr('A1'))).toEqual(new ValueCellVertex(11)) - expect(mapping.fetchCell(adr('B1'))).toEqual(new ValueCellVertex(12)) + expect(mapping.fetchCell(adr('A1'))).toEqual(new ValueCellVertex(11, 11)) + expect(mapping.fetchCell(adr('B1'))).toEqual(new ValueCellVertex(12, 12)) expect(mapping.getCell(adr('A2'))).toBe(null) expect(mapping.getCell(adr('B2'))).toBe(null) - expect(mapping.fetchCell(adr('A3'))).toEqual(new ValueCellVertex(21)) - expect(mapping.fetchCell(adr('B3'))).toEqual(new ValueCellVertex(22)) + expect(mapping.fetchCell(adr('A3'))).toEqual(new ValueCellVertex(21, 21)) + expect(mapping.fetchCell(adr('B3'))).toEqual(new ValueCellVertex(22, 22)) expect(mapping.getHeight(0)).toEqual(3) }) it('removeRows - one row', () => { const mapping = builder(2, 2) - mapping.setCell(adr('A1'), new ValueCellVertex(11)) // to remove - mapping.setCell(adr('B1'), new ValueCellVertex(12)) // to remove - mapping.setCell(adr('A2'), new ValueCellVertex(21)) - mapping.setCell(adr('B2'), new ValueCellVertex(22)) + mapping.setCell(adr('A1'), new ValueCellVertex(11, 11)) // to remove + mapping.setCell(adr('B1'), new ValueCellVertex(12, 12)) // to remove + mapping.setCell(adr('A2'), new ValueCellVertex(21, 21)) + mapping.setCell(adr('B2'), new ValueCellVertex(22, 22)) expect(mapping.getHeight(0)).toBe(2) mapping.removeRows(new RowsSpan(0, 0, 0)) @@ -188,14 +188,14 @@ const sharedExamples = (builder: (width: number, height: number) => AddressMappi it('removeRows - more than one row', () => { const mapping = builder(2, 4) - mapping.setCell(adr('A1'), new ValueCellVertex(11)) - mapping.setCell(adr('B1'), new ValueCellVertex(12)) - mapping.setCell(adr('A2'), new ValueCellVertex(21)) // to - mapping.setCell(adr('B2'), new ValueCellVertex(22)) // re - mapping.setCell(adr('A3'), new ValueCellVertex(31)) // mo - mapping.setCell(adr('B3'), new ValueCellVertex(32)) // ve - mapping.setCell(adr('A4'), new ValueCellVertex(41)) - mapping.setCell(adr('B4'), new ValueCellVertex(42)) + mapping.setCell(adr('A1'), new ValueCellVertex(11, 11)) + mapping.setCell(adr('B1'), new ValueCellVertex(12, 12)) + mapping.setCell(adr('A2'), new ValueCellVertex(21, 21)) // to + mapping.setCell(adr('B2'), new ValueCellVertex(22, 22)) // re + mapping.setCell(adr('A3'), new ValueCellVertex(31, 31)) // mo + mapping.setCell(adr('B3'), new ValueCellVertex(32, 32)) // ve + mapping.setCell(adr('A4'), new ValueCellVertex(41, 41)) + mapping.setCell(adr('B4'), new ValueCellVertex(42, 42)) expect(mapping.getHeight(0)).toBe(4) mapping.removeRows(new RowsSpan(0, 1, 2)) @@ -206,10 +206,10 @@ const sharedExamples = (builder: (width: number, height: number) => AddressMappi it('removeRows - remove more rows thant mapping size', () => { const mapping = builder(2, 2) - mapping.setCell(adr('A1'), new ValueCellVertex(11)) - mapping.setCell(adr('B1'), new ValueCellVertex(12)) - mapping.setCell(adr('A2'), new ValueCellVertex(21)) - mapping.setCell(adr('B2'), new ValueCellVertex(22)) + mapping.setCell(adr('A1'), new ValueCellVertex(11, 11)) + mapping.setCell(adr('B1'), new ValueCellVertex(12, 12)) + mapping.setCell(adr('A2'), new ValueCellVertex(21, 21)) + mapping.setCell(adr('B2'), new ValueCellVertex(22, 22)) expect(mapping.getHeight(0)).toBe(2) mapping.removeRows(new RowsSpan(0, 0, 5)) @@ -219,10 +219,10 @@ const sharedExamples = (builder: (width: number, height: number) => AddressMappi it('removeRows - remove more cols than size, but still something left', () => { const mapping = builder(2, 2) - mapping.setCell(adr('A1'), new ValueCellVertex(11)) - mapping.setCell(adr('B1'), new ValueCellVertex(12)) - mapping.setCell(adr('A2'), new ValueCellVertex(21)) - mapping.setCell(adr('B2'), new ValueCellVertex(22)) + mapping.setCell(adr('A1'), new ValueCellVertex(11, 11)) + mapping.setCell(adr('B1'), new ValueCellVertex(12, 12)) + mapping.setCell(adr('A2'), new ValueCellVertex(21, 21)) + mapping.setCell(adr('B2'), new ValueCellVertex(22, 22)) mapping.removeRows(new RowsSpan(0, 1, 5)) @@ -233,10 +233,10 @@ const sharedExamples = (builder: (width: number, height: number) => AddressMappi it('removeRows - sometimes nothing is removed', () => { const mapping = builder(2, 2) - mapping.setCell(adr('A1'), new ValueCellVertex(11)) - mapping.setCell(adr('B1'), new ValueCellVertex(12)) - mapping.setCell(adr('A2'), new ValueCellVertex(21)) - mapping.setCell(adr('B2'), new ValueCellVertex(22)) + mapping.setCell(adr('A1'), new ValueCellVertex(11, 11)) + mapping.setCell(adr('B1'), new ValueCellVertex(12, 12)) + mapping.setCell(adr('A2'), new ValueCellVertex(21, 21)) + mapping.setCell(adr('B2'), new ValueCellVertex(22, 22)) mapping.removeRows(new RowsSpan(0, 2, 3)) @@ -247,14 +247,14 @@ const sharedExamples = (builder: (width: number, height: number) => AddressMappi it('removeColumns - more than one col', () => { const mapping = builder(4, 2) - mapping.setCell(adr('A1'), new ValueCellVertex(11)) - mapping.setCell(adr('A2'), new ValueCellVertex(12)) - mapping.setCell(adr('B1'), new ValueCellVertex(21)) // to - mapping.setCell(adr('B2'), new ValueCellVertex(22)) // re - mapping.setCell(adr('C1'), new ValueCellVertex(31)) // mo - mapping.setCell(adr('C2'), new ValueCellVertex(32)) // ve - mapping.setCell(adr('D1'), new ValueCellVertex(41)) - mapping.setCell(adr('D2'), new ValueCellVertex(42)) + mapping.setCell(adr('A1'), new ValueCellVertex(11, 11)) + mapping.setCell(adr('A2'), new ValueCellVertex(12, 12)) + mapping.setCell(adr('B1'), new ValueCellVertex(21, 21)) // to + mapping.setCell(adr('B2'), new ValueCellVertex(22, 22)) // re + mapping.setCell(adr('C1'), new ValueCellVertex(31, 31)) // mo + mapping.setCell(adr('C2'), new ValueCellVertex(32, 32)) // ve + mapping.setCell(adr('D1'), new ValueCellVertex(41, 41)) + mapping.setCell(adr('D2'), new ValueCellVertex(42, 42)) expect(mapping.getWidth(0)).toBe(4) mapping.removeColumns(new ColumnsSpan(0, 1, 2)) @@ -265,10 +265,10 @@ const sharedExamples = (builder: (width: number, height: number) => AddressMappi it('removeColumns - remove more cols thant mapping size', () => { const mapping = builder(2, 2) - mapping.setCell(adr('A1'), new ValueCellVertex(11)) - mapping.setCell(adr('B1'), new ValueCellVertex(12)) - mapping.setCell(adr('A2'), new ValueCellVertex(21)) - mapping.setCell(adr('B2'), new ValueCellVertex(22)) + mapping.setCell(adr('A1'), new ValueCellVertex(11, 11)) + mapping.setCell(adr('B1'), new ValueCellVertex(12, 12)) + mapping.setCell(adr('A2'), new ValueCellVertex(21, 21)) + mapping.setCell(adr('B2'), new ValueCellVertex(22, 22)) expect(mapping.getHeight(0)).toBe(2) mapping.removeColumns(new ColumnsSpan(0, 0, 5)) @@ -278,10 +278,10 @@ const sharedExamples = (builder: (width: number, height: number) => AddressMappi it('removeColumns - remove more cols than size, but still something left', () => { const mapping = builder(2, 2) - mapping.setCell(adr('A1'), new ValueCellVertex(11)) - mapping.setCell(adr('B1'), new ValueCellVertex(12)) - mapping.setCell(adr('A2'), new ValueCellVertex(21)) - mapping.setCell(adr('B2'), new ValueCellVertex(22)) + mapping.setCell(adr('A1'), new ValueCellVertex(11, 11)) + mapping.setCell(adr('B1'), new ValueCellVertex(12, 12)) + mapping.setCell(adr('A2'), new ValueCellVertex(21, 21)) + mapping.setCell(adr('B2'), new ValueCellVertex(22, 22)) mapping.removeColumns(new ColumnsSpan(0, 1, 5)) @@ -292,10 +292,10 @@ const sharedExamples = (builder: (width: number, height: number) => AddressMappi it('removeColumns - sometimes nothing is removed', () => { const mapping = builder(2, 2) - mapping.setCell(adr('A1'), new ValueCellVertex(11)) - mapping.setCell(adr('B1'), new ValueCellVertex(12)) - mapping.setCell(adr('A2'), new ValueCellVertex(21)) - mapping.setCell(adr('B2'), new ValueCellVertex(22)) + mapping.setCell(adr('A1'), new ValueCellVertex(11, 11)) + mapping.setCell(adr('B1'), new ValueCellVertex(12, 12)) + mapping.setCell(adr('A2'), new ValueCellVertex(21, 21)) + mapping.setCell(adr('B2'), new ValueCellVertex(22, 22)) mapping.removeColumns(new ColumnsSpan(0, 2, 3)) @@ -328,7 +328,7 @@ describe('SparseStrategy', () => { const mapping = new AddressMapping(new AlwaysSparse()) mapping.addSheet(0, new SparseStrategy(4, 16)) - mapping.setCell(adr('D16'), new ValueCellVertex(42)) + mapping.setCell(adr('D16'), new ValueCellVertex(42, 42)) expect(mapping.getHeight(0)).toEqual(16) expect(mapping.getWidth(0)).toEqual(4) diff --git a/test/engine.spec.ts b/test/engine.spec.ts index 5feed86b73..8f6cfe6de7 100644 --- a/test/engine.spec.ts +++ b/test/engine.spec.ts @@ -400,7 +400,7 @@ describe('#getCellSerialized', () => { ]) expect(engine.getCellSerialized(adr('A1'))).toEqual(1) - expect(engine.getCellSerialized(adr('B1'))).toEqual(2) + expect(engine.getCellSerialized(adr('B1'))).toEqual('2') expect(engine.getCellSerialized(adr('C1'))).toEqual('foo') expect(engine.getCellSerialized(adr('D1'))).toEqual(true) }) @@ -431,6 +431,7 @@ describe('#getCellSerialized', () => { ['1', '2'], ], {matrixDetection: true, matrixDetectionThreshold: 1}) + //we are losing info about original values for values inside matrices expect(engine.getCellSerialized(adr('A1'))).toEqual(1) expect(engine.getCellSerialized(adr('B1'))).toEqual(2) }) @@ -479,7 +480,7 @@ describe('#getRangeSerialized', () => { const out = engine.getRangeSerialized(adr('A1'), 6, 1) - expectArrayWithSameContent([['=SUM(1, B1)', 2, '#VALUE!', null, '=#DIV/0!', '{=TRANSPOSE(A1:B1)}']], out) + expectArrayWithSameContent([['=SUM(1, B1)', '2', '#VALUE!', null, '=#DIV/0!', '{=TRANSPOSE(A1:B1)}']], out) }) }) diff --git a/test/escaping-support.spec.ts b/test/escaping-support.spec.ts new file mode 100644 index 0000000000..79cc803017 --- /dev/null +++ b/test/escaping-support.spec.ts @@ -0,0 +1,10 @@ +import {HyperFormula} from '../src' +import {adr} from './testUtils' + +describe('escaped formulas', () => { + it('should serialize properly', () => { + const engine = HyperFormula.buildFromArray([['\'=SUM(2,2)']]) + expect(engine.getCellSerialized(adr('A1'))).toEqual('\'=SUM(2,2)') + expect(engine.getCellValue(adr('A1'))).toEqual('=SUM(2,2)') + }) +}) diff --git a/test/graph-vertex.spec.ts b/test/graph-vertex.spec.ts index a613976510..8231f7112e 100644 --- a/test/graph-vertex.spec.ts +++ b/test/graph-vertex.spec.ts @@ -6,8 +6,8 @@ describe('Graph with Vertex', () => { it('#addNode works correctly with Vertex instances', () => { const graph = new Graph(dummyGetDependenciesQuery) - const v1 = new ValueCellVertex("1'") - const v2 = new ValueCellVertex('2') + const v1 = new ValueCellVertex("1'", "1'") + const v2 = new ValueCellVertex('2', '2') graph.addNode(v1) graph.addNode(v1) graph.addNode(v2) diff --git a/test/interpreter/precision.spec.ts b/test/interpreter/precision.spec.ts index df78c07a9b..5131381f6d 100644 --- a/test/interpreter/precision.spec.ts +++ b/test/interpreter/precision.spec.ts @@ -181,22 +181,22 @@ describe('Snap to zero', () => { ['=0+0'+chunk1, '=0+0'+chunk2], ], { smartRounding : true}) - expect(engine.dependencyGraph.getCellValue(adr('A1'))).toBeCloseTo(0.0000000001, 5) - expect(engine.dependencyGraph.getCellValue(adr('B1'))).toEqual(0) - expect(engine.dependencyGraph.getCellValue(adr('A2'))).toBeCloseTo(0.0000000001, 5) - expect(engine.dependencyGraph.getCellValue(adr('B2'))).toEqual(0) - expect(engine.dependencyGraph.getCellValue(adr('A3'))).toBeCloseTo(0.0000000001, 5) - expect(engine.dependencyGraph.getCellValue(adr('B3'))).toEqual(0) - expect(engine.dependencyGraph.getCellValue(adr('A4'))).toBeCloseTo(0.0000000001, 5) - expect(engine.dependencyGraph.getCellValue(adr('B4'))).toEqual(0) - expect(engine.dependencyGraph.getCellValue(adr('A5'))).toBeCloseTo(0.0000000001, 5) - expect(engine.dependencyGraph.getCellValue(adr('B5'))).toBeCloseTo(0.0000000000001, 5) - expect(engine.dependencyGraph.getCellValue(adr('A6'))).toBeCloseTo(0.0000000001, 5) - expect(engine.dependencyGraph.getCellValue(adr('B6'))).toBeCloseTo(0.0000000000001, 5) - expect(engine.dependencyGraph.getCellValue(adr('A7'))).toBeCloseTo(0.0000000001, 5) - expect(engine.dependencyGraph.getCellValue(adr('B7'))).toBeCloseTo(0.0000000000001, 5) - expect(engine.dependencyGraph.getCellValue(adr('A8'))).toBeCloseTo(0.0000000001, 5) - expect(engine.dependencyGraph.getCellValue(adr('B8'))).toBeCloseTo(0.0000000000001, 5) + expect(engine.getCellValue(adr('A1'))).toBeCloseTo(0.0000000001, 5) + expect(engine.getCellValue(adr('B1'))).toEqual(0) + expect(engine.getCellValue(adr('A2'))).toBeCloseTo(0.0000000001, 5) + expect(engine.getCellValue(adr('B2'))).toEqual(0) + expect(engine.getCellValue(adr('A3'))).toBeCloseTo(0.0000000001, 5) + expect(engine.getCellValue(adr('B3'))).toEqual(0) + expect(engine.getCellValue(adr('A4'))).toBeCloseTo(0.0000000001, 5) + expect(engine.getCellValue(adr('B4'))).toEqual(0) + expect(engine.getCellValue(adr('A5'))).toBeCloseTo(0.0000000001, 5) + expect(engine.getCellValue(adr('B5'))).toBeCloseTo(0.0000000000001, 5) + expect(engine.getCellValue(adr('A6'))).toBeCloseTo(0.0000000001, 5) + expect(engine.getCellValue(adr('B6'))).toBeCloseTo(0.0000000000001, 5) + expect(engine.getCellValue(adr('A7'))).toBeCloseTo(0.0000000001, 5) + expect(engine.getCellValue(adr('B7'))).toBeCloseTo(0.0000000000001, 5) + expect(engine.getCellValue(adr('A8'))).toBeCloseTo(0.0000000001, 5) + expect(engine.getCellValue(adr('B8'))).toBeCloseTo(0.0000000000001, 5) }) }) diff --git a/test/serialization.spec.ts b/test/serialization.spec.ts new file mode 100644 index 0000000000..4445b37398 --- /dev/null +++ b/test/serialization.spec.ts @@ -0,0 +1,97 @@ +import {HyperFormula} from '../src' +import {CellValueDetailedType} from '../src/Cell' +import {adr} from './testUtils' + +describe('serialization', () => { + it('should not loose sheet information on serialization', () => { + const engine1 = HyperFormula.buildFromArray([ + [1, '2', 'foo', true, '\'1', '33$', '12/01/15', '1%', '=FOO(', '#DIV/0!', ] + ]) + + expect(engine1.getCellSerialized(adr('A1'))).toEqual(1) + expect(engine1.getCellValueFormat(adr('A1'))).toEqual(undefined) + expect(engine1.getCellValueDetailedType(adr('A1'))).toEqual(CellValueDetailedType.NUMBER_RAW) + + expect(engine1.getCellSerialized(adr('B1'))).toEqual('2') + expect(engine1.getCellValueFormat(adr('B1'))).toEqual(undefined) + expect(engine1.getCellValueDetailedType(adr('B1'))).toEqual(CellValueDetailedType.NUMBER_RAW) + + expect(engine1.getCellSerialized(adr('C1'))).toEqual('foo') + expect(engine1.getCellValueFormat(adr('C1'))).toEqual(undefined) + expect(engine1.getCellValueDetailedType(adr('C1'))).toEqual(CellValueDetailedType.STRING) + + expect(engine1.getCellSerialized(adr('D1'))).toEqual(true) + expect(engine1.getCellValueFormat(adr('D1'))).toEqual(undefined) + expect(engine1.getCellValueDetailedType(adr('D1'))).toEqual(CellValueDetailedType.BOOLEAN) + + expect(engine1.getCellSerialized(adr('E1'))).toEqual('\'1') + expect(engine1.getCellValueFormat(adr('E1'))).toEqual(undefined) + expect(engine1.getCellValueDetailedType(adr('E1'))).toEqual(CellValueDetailedType.STRING) + + expect(engine1.getCellSerialized(adr('F1'))).toEqual('33$') + expect(engine1.getCellValueFormat(adr('F1'))).toEqual('$') + expect(engine1.getCellValueDetailedType(adr('F1'))).toEqual(CellValueDetailedType.NUMBER_CURRENCY) + + expect(engine1.getCellSerialized(adr('G1'))).toEqual('12/01/15') + expect(engine1.getCellValueFormat(adr('G1'))).toEqual(undefined) //this is wrong, see issue 626 + expect(engine1.getCellValueDetailedType(adr('G1'))).toEqual(CellValueDetailedType.NUMBER_DATE) + + expect(engine1.getCellSerialized(adr('H1'))).toEqual('1%') + expect(engine1.getCellValueFormat(adr('H1'))).toEqual(undefined) + expect(engine1.getCellValueDetailedType(adr('H1'))).toEqual(CellValueDetailedType.NUMBER_PERCENT) + + expect(engine1.getCellSerialized(adr('I1'))).toEqual('=FOO(') + expect(engine1.getCellValueFormat(adr('I1'))).toEqual(undefined) + expect(engine1.getCellValueDetailedType(adr('I1'))).toEqual(CellValueDetailedType.ERROR) + + expect(engine1.getCellSerialized(adr('J1'))).toEqual('#DIV/0!') + expect(engine1.getCellValueFormat(adr('J1'))).toEqual(undefined) + expect(engine1.getCellValueDetailedType(adr('J1'))).toEqual(CellValueDetailedType.ERROR) + + // serialize and "send" data to server + const serialized = engine1.getAllSheetsSerialized() + + // reload data and "restore" the previous state + const engine2 = HyperFormula.buildFromSheets(serialized) + + expect(engine2.getCellSerialized(adr('A1'))).toEqual(1) + expect(engine2.getCellValueFormat(adr('A1'))).toEqual(undefined) + expect(engine2.getCellValueDetailedType(adr('A1'))).toEqual(CellValueDetailedType.NUMBER_RAW) + + expect(engine2.getCellSerialized(adr('B1'))).toEqual('2') + expect(engine2.getCellValueFormat(adr('B1'))).toEqual(undefined) + expect(engine2.getCellValueDetailedType(adr('B1'))).toEqual(CellValueDetailedType.NUMBER_RAW) + + expect(engine2.getCellSerialized(adr('C1'))).toEqual('foo') + expect(engine2.getCellValueFormat(adr('C1'))).toEqual(undefined) + expect(engine2.getCellValueDetailedType(adr('C1'))).toEqual(CellValueDetailedType.STRING) + + expect(engine2.getCellSerialized(adr('D1'))).toEqual(true) + expect(engine2.getCellValueFormat(adr('D1'))).toEqual(undefined) + expect(engine2.getCellValueDetailedType(adr('D1'))).toEqual(CellValueDetailedType.BOOLEAN) + + expect(engine2.getCellSerialized(adr('E1'))).toEqual('\'1') + expect(engine2.getCellValueFormat(adr('E1'))).toEqual(undefined) + expect(engine2.getCellValueDetailedType(adr('E1'))).toEqual(CellValueDetailedType.STRING) + + expect(engine2.getCellSerialized(adr('F1'))).toEqual('33$') + expect(engine2.getCellValueFormat(adr('F1'))).toEqual('$') + expect(engine2.getCellValueDetailedType(adr('F1'))).toEqual(CellValueDetailedType.NUMBER_CURRENCY) + + expect(engine2.getCellSerialized(adr('G1'))).toEqual('12/01/15') + expect(engine2.getCellValueFormat(adr('G1'))).toEqual(undefined) //this is wrong, see issue 626 + expect(engine2.getCellValueDetailedType(adr('G1'))).toEqual(CellValueDetailedType.NUMBER_DATE) + + expect(engine1.getCellSerialized(adr('H1'))).toEqual('1%') + expect(engine1.getCellValueFormat(adr('H1'))).toEqual(undefined) + expect(engine1.getCellValueDetailedType(adr('H1'))).toEqual(CellValueDetailedType.NUMBER_PERCENT) + + expect(engine1.getCellSerialized(adr('I1'))).toEqual('=FOO(') + expect(engine1.getCellValueFormat(adr('I1'))).toEqual(undefined) + expect(engine1.getCellValueDetailedType(adr('I1'))).toEqual(CellValueDetailedType.ERROR) + + expect(engine1.getCellSerialized(adr('J1'))).toEqual('#DIV/0!') + expect(engine1.getCellValueFormat(adr('J1'))).toEqual(undefined) + expect(engine1.getCellValueDetailedType(adr('J1'))).toEqual(CellValueDetailedType.ERROR) + }) +})