Skip to content

Commit aeada8d

Browse files
committed
Test the cleanup of placeholder sheets
1 parent 682c274 commit aeada8d

File tree

6 files changed

+122
-24
lines changed

6 files changed

+122
-24
lines changed

.eslintrc.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ module.exports = {
128128
'jest/no-standalone-expect': 'warn',
129129
'jest/no-test-prefixes': 'off',
130130
'jest/prefer-to-be': 'warn',
131-
'jest/prefer-to-have-length': 'warn',
131+
'jest/prefer-to-have-length': 'off',
132132
},
133133
overrides: [
134134
{

src/DependencyGraph/AddressMapping/AddressMapping.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,9 +180,11 @@ export class AddressMapping {
180180

181181
/**
182182
* Removes a sheet from the address mapping.
183+
* If sheet does not exist, does nothing.
184+
* @returns {boolean} true if sheet was removed, false if it did not exist.
183185
*/
184-
public removeSheet(sheetId: number): void {
185-
this.mapping.delete(sheetId)
186+
public removeSheetIfExists(sheetId: number): boolean {
187+
return this.mapping.delete(sheetId)
186188
}
187189

188190
/**

src/DependencyGraph/DependencyGraph.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -323,14 +323,14 @@ export class DependencyGraph {
323323
* - If nothing is left, removes the sheet from sheet mapping and address mapping.
324324
* - Otherwise, marks it as placeholder.
325325
*/
326-
public removeSheet(sheetId: number) {
326+
public removeSheet(sheetId: number): void {
327327
this.clearSheet(sheetId)
328328
const addressMappingCleared = !this.addressMapping.hasAnyEntries(sheetId)
329329
const rangeMappingCleared = this.rangeMapping.getNumberOfRangesInSheet(sheetId) === 0
330330

331331
if (addressMappingCleared && rangeMappingCleared) {
332-
this.sheetMapping.removeSheet(sheetId)
333-
this.addressMapping.removeSheet(sheetId)
332+
this.sheetMapping.removeSheetIfExists(sheetId)
333+
this.addressMapping.removeSheetIfExists(sheetId)
334334
} else {
335335
this.sheetMapping.markSheetAsPlaceholder(sheetId)
336336
}
@@ -352,7 +352,7 @@ export class DependencyGraph {
352352

353353
this.mergeRangeVertices(sheetToKeep, placeholderSheetToDelete)
354354
this.mergeCellVertices(sheetToKeep, placeholderSheetToDelete)
355-
this.addressMapping.removeSheet(placeholderSheetToDelete)
355+
this.addressMapping.removeSheetIfExists(placeholderSheetToDelete)
356356
this.addStructuralNodesToChangeSet()
357357
}
358358

@@ -1340,7 +1340,7 @@ export class DependencyGraph {
13401340
!this.addressMapping.hasAnyEntries(sheetId) &&
13411341
this.rangeMapping.getNumberOfRangesInSheet(sheetId) === 0) {
13421342
this.sheetMapping.removeSheet(sheetId, { includePlaceholders: true })
1343-
this.addressMapping.removeSheet(sheetId)
1343+
this.addressMapping.removeSheetIfExists(sheetId)
13441344
}
13451345
}
13461346
}

src/DependencyGraph/SheetMapping.ts

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ export class SheetMapping {
166166

167167
this.lastSheetId++
168168
const sheet = new Sheet(this.lastSheetId, newSheetDisplayName)
169-
this.storeSheetInMappings(sheet)
169+
this._storeSheetInMappings(sheet)
170170
return sheet.id
171171
}
172172

@@ -197,7 +197,7 @@ export class SheetMapping {
197197
}
198198

199199
const sheet = new Sheet(sheetId, sheetDisplayName)
200-
this.storeSheetInMappings(sheet)
200+
this._storeSheetInMappings(sheet)
201201
}
202202

203203
/**
@@ -212,7 +212,7 @@ export class SheetMapping {
212212

213213
this.lastSheetId++
214214
const sheet = new Sheet(this.lastSheetId, sheetName, true)
215-
this.storeSheetInMappings(sheet)
215+
this._storeSheetInMappings(sheet)
216216
return sheet.id
217217
}
218218

@@ -237,17 +237,44 @@ export class SheetMapping {
237237
this.lastSheetId = sheetId
238238
}
239239
const sheet = new Sheet(sheetId, sheetDisplayName, true)
240-
this.storeSheetInMappings(sheet)
240+
this._storeSheetInMappings(sheet)
241241
}
242242

243243
/**
244-
* Removes sheet with given ID. Ignores placeholders
244+
* Removes sheet with given ID.
245245
* @throws {NoSheetWithIdError} if the sheet with the given ID does not exist or is a placeholder
246246
*/
247247
public removeSheet(sheetId: number, options: SheetMappingQueryOptions = {}): void {
248248
const sheet = this._getSheetOrThrowError(sheetId, options)
249249
this.allSheets.delete(sheetId)
250250
this.mappingFromCanonicalNameToId.delete(sheet.canonicalName)
251+
252+
if (sheetId === this.lastSheetId) {
253+
this.lastSheetId--
254+
}
255+
}
256+
257+
/**
258+
*
259+
* Removes sheet with given ID.
260+
* If sheet does not exist, does nothing.
261+
* @returns {boolean} true if sheet was removed, false if it did not exist.
262+
*/
263+
public removeSheetIfExists(sheetId: number, options: SheetMappingQueryOptions = {}): boolean {
264+
const sheet = this._getSheet(sheetId, options)
265+
266+
if (!sheet) {
267+
return false
268+
}
269+
270+
this.allSheets.delete(sheetId)
271+
this.mappingFromCanonicalNameToId.delete(sheet.canonicalName)
272+
273+
if (sheetId === this.lastSheetId) {
274+
this.lastSheetId--
275+
}
276+
277+
return true
251278
}
252279

253280
/**
@@ -285,6 +312,11 @@ export class SheetMapping {
285312
} else {
286313
this.mappingFromCanonicalNameToId.delete(sheetWithConflictingName.canonicalName)
287314
this.allSheets.delete(sheetWithConflictingName.id)
315+
316+
if (sheetWithConflictingName.id === this.lastSheetId) {
317+
this.lastSheetId--
318+
}
319+
288320
mergedWithPlaceholderSheet = sheetWithConflictingName.id
289321
}
290322
}
@@ -293,7 +325,7 @@ export class SheetMapping {
293325
this.mappingFromCanonicalNameToId.delete(currentCanonicalName)
294326

295327
sheet.displayName = newDisplayName
296-
this.storeSheetInMappings(sheet)
328+
this._storeSheetInMappings(sheet)
297329
return { previousDisplayName: currentDisplayName, mergedWithPlaceholderSheet }
298330
}
299331

@@ -304,7 +336,7 @@ export class SheetMapping {
304336
*
305337
* @internal
306338
*/
307-
private storeSheetInMappings(sheet: Sheet): void {
339+
private _storeSheetInMappings(sheet: Sheet): void {
308340
this.allSheets.set(sheet.id, sheet)
309341
this.mappingFromCanonicalNameToId.set(sheet.canonicalName, sheet.id)
310342
}

test/unit/cruds/adding-sheet.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@ describe('recalculates formulas after adding new sheet (issue #1116)', () => {
326326
const engine = HyperFormula.buildFromSheets({
327327
'FirstSheet': [
328328
['=MEDIAN(A2)', '=MEDIAN(B2)', '=MEDIAN(C2)', '=MEDIAN(D2)'],
329-
[`='NewSheet'!A1:A1`, `='NewSheet'!A1:B2`, `='NewSheet'!A1:A3`, `='NewSheet'!A1:A4`],
329+
['=\'NewSheet\'!A1:A1', '=\'NewSheet\'!A1:B2', '=\'NewSheet\'!A1:A3', '=\'NewSheet\'!A1:A4'],
330330
],
331331
}, {useArrayArithmetic: false})
332332
const sheet1Id = engine.getSheetId('FirstSheet')!
@@ -349,7 +349,7 @@ describe('recalculates formulas after adding new sheet (issue #1116)', () => {
349349
const engine = HyperFormula.buildFromSheets({
350350
'FirstSheet': [
351351
['=SUM(A2)', '=SUM(B2)', '=SUM(C2)', '=SUM(D2)'],
352-
[`='NewSheet'!A1:A1`, `='NewSheet'!A1:B2`, `='NewSheet'!A1:A3`, `='NewSheet'!A1:A4`],
352+
['=\'NewSheet\'!A1:A1', '=\'NewSheet\'!A1:B2', '=\'NewSheet\'!A1:A3', '=\'NewSheet\'!A1:A4'],
353353
],
354354
}, {useArrayArithmetic: false})
355355
const sheet1Id = engine.getSheetId('FirstSheet')!
@@ -370,7 +370,7 @@ describe('recalculates formulas after adding new sheet (issue #1116)', () => {
370370

371371
it('function calling a named expression', () => {
372372
const engine = HyperFormula.buildFromSheets({
373-
'FirstSheet': [[`='NewSheet'!A1:A4`]],
373+
'FirstSheet': [['=\'NewSheet\'!A1:A4']],
374374
}, {useArrayArithmetic: false}, [
375375
{ name: 'ExprA', expression: '=MEDIAN(NewSheet!$A$1:$A$1)' },
376376
{ name: 'ExprB', expression: '=MEDIAN(NewSheet!$A$1:$A$2)' },

test/unit/cruds/removing-sheet.spec.ts

Lines changed: 70 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,20 @@ import {
1010
detailedError,
1111
detailedErrorWithOrigin,
1212
expectArrayWithSameContent,
13-
expectEngineToBeTheSameAs,
14-
expectReferenceToHaveRefError,
1513
extractReference,
1614
} from '../testUtils'
1715

1816
describe('Removing sheet - checking if its possible', () => {
1917
it('no if theres no such sheet', () => {
2018
const engine = HyperFormula.buildFromArray([[]])
2119

22-
expect(engine.isItPossibleToRemoveSheet(1)).toEqual(false)
20+
expect(engine.isItPossibleToRemoveSheet(1)).toBe(false)
2321
})
2422

2523
it('yes otherwise', () => {
2624
const engine = HyperFormula.buildFromArray([[]])
2725

28-
expect(engine.isItPossibleToRemoveSheet(0)).toEqual(true)
26+
expect(engine.isItPossibleToRemoveSheet(0)).toBe(true)
2927
})
3028
})
3129

@@ -116,6 +114,70 @@ describe('remove sheet', () => {
116114
expect(engine.getCellValue(adr('A2', 1))).toBe(2)
117115
expect(engine.getCellValue(adr('A3', 1))).toBe(3)
118116
})
117+
118+
it('converts sheet to placeholder if other sheet depends on it', () => {
119+
const engine = HyperFormula.buildFromSheets({
120+
Sheet1: [[42]],
121+
Sheet2: [['=Sheet1!A1']],
122+
})
123+
124+
const sheet1Id = engine.getSheetId('Sheet1')!
125+
126+
engine.removeSheet(sheet1Id)
127+
128+
expect(engine.sheetMapping.hasSheetWithId(sheet1Id, { includePlaceholders: false })).toBe(false)
129+
expect(engine.sheetMapping.hasSheetWithId(sheet1Id, { includePlaceholders: true })).toBe(true)
130+
})
131+
132+
it('removes sheet completely if nothing depends on it', () => {
133+
const engine = HyperFormula.buildFromSheets({
134+
Sheet1: [[42]],
135+
Sheet2: [[100]],
136+
})
137+
138+
const sheet1Id = engine.getSheetId('Sheet1')!
139+
140+
engine.removeSheet(sheet1Id)
141+
142+
expect(engine.sheetMapping.hasSheetWithId(sheet1Id, { includePlaceholders: false })).toBe(false)
143+
expect(engine.sheetMapping.hasSheetWithId(sheet1Id, { includePlaceholders: true })).toBe(false)
144+
})
145+
146+
it('removes the placeholder sheet if nothing depends on it any longer', () => {
147+
const engine = HyperFormula.buildFromSheets({
148+
Sheet1: [[42]],
149+
Sheet2: [['=Sheet1!A1']],
150+
})
151+
152+
const sheet1Id = engine.getSheetId('Sheet1')!
153+
const sheet2Id = engine.getSheetId('Sheet2')!
154+
155+
engine.removeSheet(sheet1Id)
156+
157+
expect(engine.sheetMapping.hasSheetWithId(sheet1Id, { includePlaceholders: false })).toBe(false)
158+
expect(engine.sheetMapping.hasSheetWithId(sheet1Id, { includePlaceholders: true })).toBe(true)
159+
160+
engine.setCellContents(adr('A1', sheet2Id), 100)
161+
162+
expect(engine.sheetMapping.hasSheetWithId(sheet1Id, { includePlaceholders: false })).toBe(false)
163+
expect(engine.sheetMapping.hasSheetWithId(sheet1Id, { includePlaceholders: true })).toBe(false)
164+
})
165+
166+
it('decreases lastSheetId if removed sheet was the last one', () => {
167+
const engine = HyperFormula.buildFromSheets({
168+
Sheet1: [[1]],
169+
Sheet2: [[2]],
170+
})
171+
172+
const sheet2Id = engine.getSheetId('Sheet2')!
173+
174+
engine.removeSheet(sheet2Id)
175+
176+
engine.addSheet('Sheet3')
177+
const sheet3Id = engine.getSheetId('Sheet3')!
178+
179+
expect(sheet3Id).toBe(sheet2Id) // new sheet reuses the ID
180+
})
119181
})
120182

121183
describe('remove sheet - adjust edges', () => {
@@ -149,6 +211,7 @@ describe('remove sheet - adjust edges', () => {
149211

150212
const a1From0 = engine.addressMapping.getCell(adr('A1'))
151213
const a1From1 = engine.addressMapping.getCell(adr('A1', 1))
214+
152215
expect(engine.graph.existsEdge(a1From1!, a1From0!)).toBe(true)
153216

154217
engine.removeSheet(1)
@@ -221,7 +284,7 @@ describe('remove sheet - adjust formula dependencies', () => {
221284

222285
})
223286

224-
describe('remove sheet - issue #1116', () => {
287+
describe('removeSheet() recalculates formulas (issue #1116)', () => {
225288
it('returns REF error after removing sheet referenced without quotes', () => {
226289
const engine = HyperFormula.buildEmpty()
227290
const table1Name = 'table1'
@@ -727,6 +790,7 @@ describe('remove sheet - adjust matrix mapping', () => {
727790
['=TRANSPOSE(A1:B1)'],
728791
],
729792
})
793+
730794
expect(engine.arrayMapping.getArray(AbsoluteCellRange.spanFrom(adr('A2'), 1, 2))).toBeInstanceOf(ArrayFormulaVertex)
731795

732796
engine.removeSheet(0)
@@ -746,7 +810,7 @@ describe('remove sheet - adjust column index', () => {
746810

747811
engine.removeSheet(0)
748812

749-
expect(removeSheetSpy).toHaveBeenCalled()
813+
expect(removeSheetSpy).toHaveBeenCalledWith(0)
750814
expectArrayWithSameContent([], index.getValueIndex(0, 0, 1).index)
751815
})
752816
})

0 commit comments

Comments
 (0)