diff --git a/packages/sheets-formula-ui/src/controllers/__tests__/update-formula.controller.spec.ts b/packages/sheets-formula-ui/src/controllers/__tests__/update-formula.controller.spec.ts index 391e53bea4..47dab71585 100644 --- a/packages/sheets-formula-ui/src/controllers/__tests__/update-formula.controller.spec.ts +++ b/packages/sheets-formula-ui/src/controllers/__tests__/update-formula.controller.spec.ts @@ -379,6 +379,57 @@ const TEST_WORKBOOK_DATA_DEMO = (): IWorkbookData => ({ }, }, }, + sheet6: { + id: 'sheet6', + name: 'Sheet6', + cellData: { + 0: { + 0: { + v: 1, + t: 2, + }, + 1: { + f: '=A1', + v: 1, + t: 2, + }, + }, + 1: { + 0: { + v: 2, + t: 2, + }, + 1: { + f: '=A2', + si: 'W8Hdfc', + v: 2, + t: 2, + }, + }, + 2: { + 0: { + v: 3, + t: 2, + }, + 1: { + si: 'W8Hdfc', + v: 3, + t: 2, + }, + }, + 3: { + 0: { + v: 4, + t: 2, + }, + 1: { + si: 'W8Hdfc', + v: 4, + t: 2, + }, + }, + }, + }, }, locale: LocaleType.ZH_CN, name: '', @@ -557,6 +608,235 @@ describe('Test update formula ', () => { expect(valuesRedo).toStrictEqual([[null, { f: '=SUM(A1:B2)' }]]); }); + it('Move range, update reference, release si', async () => { + const workbook = get(IUniverInstanceService).getUnit('test'); + const sheetId = 'sheet6'; + const sheet6 = workbook?.getSheetBySheetId(sheetId); + if (!sheet6) { + throw new Error(`${sheetId}not found`); + } + workbook?.setActiveSheet(sheet6); + + const params: IMoveRangeCommandParams = { + fromRange: { + startRow: 0, + startColumn: 1, + endRow: 3, + endColumn: 1, + rangeType: 0, + }, + toRange: { + startRow: 2, + startColumn: 1, + endRow: 5, + endColumn: 1, + rangeType: 0, + }, + }; + + expect(await commandService.executeCommand(MoveRangeCommand.id, params)).toBeTruthy(); + const values = getValues(0, 1, 5, 1, sheetId); + expect(values).toStrictEqual([ + [null], + [null], + [{ f: '=A1', t: 2, v: 1 }], + [{ f: '=A2', t: 2, v: 2 }], + [{ f: '=A3', t: 2, v: 3 }], + [{ f: '=A4', t: 2, v: 4 }], + ]); + + expect(await commandService.executeCommand(UndoCommand.id)).toBeTruthy(); + const valuesUndo = getValues(0, 1, 5, 1, sheetId); + expect(valuesUndo).toStrictEqual([ + [{ f: '=A1', t: 2, v: 1 }], + [{ f: '=A2', si: 'W8Hdfc', t: 2, v: 2 }], + [{ si: 'W8Hdfc', t: 2, v: 3 }], + [{ si: 'W8Hdfc', t: 2, v: 4 }], + [null], + [null], + ]); + + expect(await commandService.executeCommand(RedoCommand.id)).toBeTruthy(); + const valuesRedo = getValues(0, 1, 5, 1, sheetId); + expect(valuesRedo).toStrictEqual([ + [null], + [null], + [{ f: '=A1', t: 2, v: 1 }], + [{ f: '=A2', t: 2, v: 2 }], + [{ f: '=A3', t: 2, v: 3 }], + [{ f: '=A4', t: 2, v: 4 }], + ]); + }); + + it('Move range with f/si', async () => { + const workbook = get(IUniverInstanceService).getUnit('test'); + const sheetId = 'sheet6'; + const sheet6 = workbook?.getSheetBySheetId(sheetId); + if (!sheet6) { + throw new Error(`${sheetId}not found`); + } + workbook?.setActiveSheet(sheet6); + + const params: IMoveRangeCommandParams = { + fromRange: { + startRow: 1, + startColumn: 1, + endRow: 1, + endColumn: 1, + rangeType: 0, + }, + toRange: { + startRow: 1, + startColumn: 3, + endRow: 1, + endColumn: 3, + rangeType: 0, + }, + }; + + expect(await commandService.executeCommand(MoveRangeCommand.id, params)).toBeTruthy(); + const values1 = getValues(1, 3, 1, 3, sheetId); + expect(values1).toStrictEqual([[{ f: '=A2', t: 2, v: 2 }]]); + const values2 = getValues(1, 1, 3, 1, sheetId); + expect(values2).toStrictEqual([ + [null], + [{ f: '=A3', t: 2, v: 3 }], + [{ f: '=A4', t: 2, v: 4 }], + ]); + + expect(await commandService.executeCommand(UndoCommand.id)).toBeTruthy(); + const valuesUndo1 = getValues(1, 3, 1, 3, sheetId); + expect(valuesUndo1).toStrictEqual([[null]]); + const valuesUndo2 = getValues(1, 1, 3, 1, sheetId); + expect(valuesUndo2).toStrictEqual([ + [{ f: '=A2', si: 'W8Hdfc', t: 2, v: 2 }], + [{ si: 'W8Hdfc', t: 2, v: 3 }], + [{ si: 'W8Hdfc', t: 2, v: 4 }], + ]); + + expect(await commandService.executeCommand(RedoCommand.id)).toBeTruthy(); + const valuesRedo1 = getValues(1, 3, 1, 3, sheetId); + expect(valuesRedo1).toStrictEqual([[{ f: '=A2', t: 2, v: 2 }]]); + const valuesRedo2 = getValues(1, 1, 3, 1, sheetId); + expect(valuesRedo2).toStrictEqual([ + [null], + [{ f: '=A3', t: 2, v: 3 }], + [{ f: '=A4', t: 2, v: 4 }], + ]); + }); + + it('Move range with si only', async () => { + const workbook = get(IUniverInstanceService).getUnit('test'); + const sheetId = 'sheet6'; + const sheet6 = workbook?.getSheetBySheetId(sheetId); + if (!sheet6) { + throw new Error(`${sheetId}not found`); + } + workbook?.setActiveSheet(sheet6); + + const params: IMoveRangeCommandParams = { + fromRange: { + startRow: 2, + startColumn: 1, + endRow: 2, + endColumn: 1, + rangeType: 0, + }, + toRange: { + startRow: 2, + startColumn: 3, + endRow: 2, + endColumn: 3, + rangeType: 0, + }, + }; + + expect(await commandService.executeCommand(MoveRangeCommand.id, params)).toBeTruthy(); + const values1 = getValues(2, 3, 2, 3, sheetId); + expect(values1).toStrictEqual([[{ f: '=A3', t: 2, v: 3 }]]); + const values2 = getValues(1, 1, 3, 1, sheetId); + expect(values2).toStrictEqual([ + [{ f: '=A2', t: 2, v: 2 }], + [null], + [{ f: '=A4', t: 2, v: 4 }], + ]); + + expect(await commandService.executeCommand(UndoCommand.id)).toBeTruthy(); + const valuesUndo1 = getValues(2, 3, 2, 3, sheetId); + expect(valuesUndo1).toStrictEqual([[null]]); + const valuesUndo2 = getValues(1, 1, 3, 1, sheetId); + expect(valuesUndo2).toStrictEqual([ + [{ f: '=A2', si: 'W8Hdfc', t: 2, v: 2 }], + [{ si: 'W8Hdfc', t: 2, v: 3 }], + [{ si: 'W8Hdfc', t: 2, v: 4 }], + ]); + + expect(await commandService.executeCommand(RedoCommand.id)).toBeTruthy(); + const valuesRedo1 = getValues(2, 3, 2, 3, sheetId); + expect(valuesRedo1).toStrictEqual([[{ f: '=A3', t: 2, v: 3 }]]); + const valuesRedo2 = getValues(1, 1, 3, 1, sheetId); + expect(valuesRedo2).toStrictEqual([ + [{ f: '=A2', t: 2, v: 2 }], + [null], + [{ f: '=A4', t: 2, v: 4 }], + ]); + }); + + it('Move range with f/si and si', async () => { + const workbook = get(IUniverInstanceService).getUnit('test'); + const sheetId = 'sheet6'; + const sheet6 = workbook?.getSheetBySheetId(sheetId); + if (!sheet6) { + throw new Error(`${sheetId}not found`); + } + workbook?.setActiveSheet(sheet6); + + const params: IMoveRangeCommandParams = { + fromRange: { + startRow: 1, + startColumn: 1, + endRow: 2, + endColumn: 1, + rangeType: 0, + }, + toRange: { + startRow: 1, + startColumn: 3, + endRow: 2, + endColumn: 3, + rangeType: 0, + }, + }; + + expect(await commandService.executeCommand(MoveRangeCommand.id, params)).toBeTruthy(); + const values1 = getValues(1, 3, 2, 3, sheetId); + expect(values1).toStrictEqual([[{ f: '=A2', t: 2, v: 2 }], [{ f: '=A3', t: 2, v: 3 }]]); + const values2 = getValues(1, 1, 3, 1, sheetId); + expect(values2).toStrictEqual([ + [null], + [null], + [{ f: '=A4', t: 2, v: 4 }], + ]); + + expect(await commandService.executeCommand(UndoCommand.id)).toBeTruthy(); + const valuesUndo = getValues(1, 1, 3, 1, sheetId); + expect(valuesUndo).toStrictEqual([ + [{ f: '=A2', si: 'W8Hdfc', t: 2, v: 2 }], + [{ si: 'W8Hdfc', t: 2, v: 3 }], + [{ si: 'W8Hdfc', t: 2, v: 4 }], + ]); + + expect(await commandService.executeCommand(RedoCommand.id)).toBeTruthy(); + const valuesRedo1 = getValues(1, 3, 2, 3, sheetId); + expect(valuesRedo1).toStrictEqual([[{ f: '=A2', t: 2, v: 2 }], [{ f: '=A3', t: 2, v: 3 }]]); + const valuesRedo2 = getValues(1, 1, 3, 1, sheetId); + expect(valuesRedo2).toStrictEqual([ + [null], + [null], + [{ f: '=A4', t: 2, v: 4 }], + ]); + }); + it('Move range, update reference with si ', async () => { const params: IMoveRangeCommandParams = { fromRange: { @@ -577,15 +857,27 @@ describe('Test update formula ', () => { expect(await commandService.executeCommand(MoveRangeCommand.id, params)).toBeTruthy(); const values = getValues(18, 1, 20, 2); - expect(values).toStrictEqual([[null, { f: '=SUM(A19)', t: 2, v: 1 }], [null, { f: '=SUM(A20)', si: 'id1', t: 2, v: 2 }], [null, { si: 'id1', t: 2, v: 3 }]]); + expect(values).toStrictEqual([ + [null, { f: '=SUM(A19)', t: 2, v: 1 }], + [null, { f: '=SUM(A20)', t: 2, v: 2 }], + [null, { f: '=SUM(A21)', t: 2, v: 3 }], + ]); expect(await commandService.executeCommand(UndoCommand.id)).toBeTruthy(); const valuesUndo = getValues(18, 1, 20, 2); - expect(valuesUndo).toStrictEqual([[{ f: '=SUM(A19)', t: 2, v: 1 }, null], [{ f: '=SUM(A20)', si: 'id1', t: 2, v: 2 }, null], [{ si: 'id1', t: 2, v: 3 }, null]]); + expect(valuesUndo).toStrictEqual([ + [{ f: '=SUM(A19)', t: 2, v: 1 }, null], + [{ f: '=SUM(A20)', si: 'id1', t: 2, v: 2 }, null], + [{ si: 'id1', t: 2, v: 3 }, null], + ]); expect(await commandService.executeCommand(RedoCommand.id)).toBeTruthy(); const valuesRedo = getValues(18, 1, 20, 2); - expect(valuesRedo).toStrictEqual([[null, { f: '=SUM(A19)', t: 2, v: 1 }], [null, { f: '=SUM(A20)', si: 'id1', t: 2, v: 2 }], [null, { si: 'id1', t: 2, v: 3 }]]); + expect(valuesRedo).toStrictEqual([ + [null, { f: '=SUM(A19)', t: 2, v: 1 }], + [null, { f: '=SUM(A20)', t: 2, v: 2 }], + [null, { f: '=SUM(A21)', t: 2, v: 3 }], + ]); }); it('Move rows, update reference', async () => { diff --git a/packages/sheets-formula/src/controllers/update-formula.controller.ts b/packages/sheets-formula/src/controllers/update-formula.controller.ts index 9983c4216d..fa2ecfbff7 100644 --- a/packages/sheets-formula/src/controllers/update-formula.controller.ts +++ b/packages/sheets-formula/src/controllers/update-formula.controller.ts @@ -514,6 +514,10 @@ export class UpdateFormulaController extends Disposable { shouldModify = true; refChangeIds.push(i); // newRefString = ErrorType.REF; + } else if (type === FormulaReferenceMoveType.MoveRange && si) { + // If the operation is a move range and the formula has si, unpack the si to f. + // This is to ensure that the si formula can be recalculated correctly after the move. + shouldModify = true; } } diff --git a/packages/sheets-formula/src/controllers/utils/ref-range-formula.ts b/packages/sheets-formula/src/controllers/utils/ref-range-formula.ts index 2e2c476c98..4858ae7686 100644 --- a/packages/sheets-formula/src/controllers/utils/ref-range-formula.ts +++ b/packages/sheets-formula/src/controllers/utils/ref-range-formula.ts @@ -17,7 +17,7 @@ import type { ICellData, IMutationInfo, IObjectMatrixPrimitiveType, IRange, Nullable } from '@univerjs/core'; import type { IFormulaData, IFormulaDataItem, IRangeChange, ISequenceNode } from '@univerjs/engine-formula'; import type { ISetRangeValuesMutationParams } from '@univerjs/sheets'; -import { cellToRange, Direction, isFormulaId, isFormulaString, ObjectMatrix, Rectangle, Tools } from '@univerjs/core'; +import { cellToRange, Direction, isFormulaId, isFormulaString, ObjectMatrix, Rectangle } from '@univerjs/core'; import { deserializeRangeWithSheetWithCache, sequenceNodeType, serializeRangeToRefString } from '@univerjs/engine-formula'; import { EffectRefRangId, handleDeleteRangeMoveLeft, handleDeleteRangeMoveUp, handleInsertCol, handleInsertRangeMoveDown, handleInsertRangeMoveRight, handleInsertRow, handleIRemoveCol, handleIRemoveRow, handleMoveCols, handleMoveRange, handleMoveRows, runRefRangeMutations, SetRangeValuesMutation } from '@univerjs/sheets'; import { checkFormulaDataNull } from './offset-formula-data'; @@ -156,14 +156,6 @@ export function getFormulaReferenceSheet(oldFormulaData: IFormulaData, newFormul export function getFormulaReferenceRange(oldFormulaData: IFormulaData, newFormulaData: IFormulaData, formulaReferenceMoveParam: IFormulaReferenceMoveParam) { const { redoFormulaData, undoFormulaData } = refRangeFormula(oldFormulaData, newFormulaData, formulaReferenceMoveParam); - // If the formula data is the same, no operation is required - if (Tools.diffValue(redoFormulaData, undoFormulaData)) { - return { - undos: [], - redos: [], - }; - } - const redos: IMutationInfo[] = []; const undos: IMutationInfo[] = []; @@ -528,22 +520,22 @@ function handleRefInsertMoveRight(range: IRange, oldCell: IRange) { function getRedoFormulaData(rangeList: IRangeChange[], oldFormulaMatrix: ObjectMatrix>, newFormulaMatrix: ObjectMatrix>) { const redoFormulaData = new ObjectMatrix>({}); - rangeList.forEach((item) => { - const { oldCell, newCell } = item; + for (let i = 0; i < rangeList.length; i++) { + const { oldCell, newCell } = rangeList[i]; - const { startRow: oldStartRow, startColumn: oldStartColumn } = oldCell; - - const newFormula = newFormulaMatrix.getValue(oldStartRow, oldStartColumn) || oldFormulaMatrix.getValue(oldStartRow, oldStartColumn); - // Use the formula result value to update the data to ensure accuracy, otherwise the new formula cannot be inferred from #REF - const newValue = formulaDataItemToCellData(newFormula); - - redoFormulaData.setValue(oldStartRow, oldStartColumn, { f: null, si: null }); + // Clear the old position formula data, if the position of redoFormulaData has set a new formula, no additional processing is required + if (!(redoFormulaData.getValue(oldCell.startRow, oldCell.startColumn)?.f || redoFormulaData.getValue(oldCell.startRow, oldCell.startColumn)?.si)) { + redoFormulaData.setValue(oldCell.startRow, oldCell.startColumn, { f: null, si: null }); + } if (newCell) { - const { startRow: newStartRow, startColumn: newStartColumn } = newCell; - redoFormulaData.setValue(newStartRow, newStartColumn, newValue); + // Save the new position formula data to redoFormulaData + // newFormulaMatrix.getValue(oldCell.startRow, oldCell.startColumn) may be null, it means that the formula is not offset, so use the old value + const newFormula = newFormulaMatrix.getValue(oldCell.startRow, oldCell.startColumn) ?? oldFormulaMatrix.getValue(oldCell.startRow, oldCell.startColumn); + const newValue = formulaDataItemToCellData(newFormula); + redoFormulaData.setValue(newCell.startRow, newCell.startColumn, newValue); } - }); + } return redoFormulaData.getMatrix(); } @@ -557,22 +549,22 @@ function getRedoFormulaData(rangeList: IRangeChange[], oldFormulaMatrix: ObjectM function getUndoFormulaData(rangeList: IRangeChange[], oldFormulaMatrix: ObjectMatrix>) { const undoFormulaData = new ObjectMatrix>({}); - // Maintaining the correct assignment order prevents overwriting data - rangeList.reverse().forEach((item) => { - const { oldCell, newCell } = item; + for (let i = rangeList.length - 1; i >= 0; i--) { + const { oldCell, newCell } = rangeList[i]; - const { startRow: oldStartRow, startColumn: oldStartColumn } = oldCell; - - const oldFormula = oldFormulaMatrix.getValue(oldStartRow, oldStartColumn); - const oldValue = formulaDataItemToCellData(oldFormula); + // Save old position old formula data to undoFormulaData + const oldCellOldFormula = oldFormulaMatrix.getValue(oldCell.startRow, oldCell.startColumn); + const oldCellOldValue = formulaDataItemToCellData(oldCellOldFormula); + undoFormulaData.setValue(oldCell.startRow, oldCell.startColumn, oldCellOldValue); if (newCell) { - const { startRow: newStartRow, startColumn: newStartColumn } = newCell; - undoFormulaData.setValue(newStartRow, newStartColumn, { f: null, si: null }); + // If the newCell is not null, save new position old formula data to undoFormulaData. + // If the new position old formula data is null, clear the new position formula data + const newCellOldFormula = oldFormulaMatrix.getValue(newCell.startRow, newCell.startColumn); + const newCellOldValue = formulaDataItemToCellData(newCellOldFormula); + undoFormulaData.setValue(newCell.startRow, newCell.startColumn, newCellOldValue ?? { f: null, si: null }); } - - undoFormulaData.setValue(oldStartRow, oldStartColumn, oldValue); - }); + } return undoFormulaData.getMatrix(); } diff --git a/packages/sheets-formula/src/controllers/utils/ref-range-move.ts b/packages/sheets-formula/src/controllers/utils/ref-range-move.ts index ab19fd998d..b1528b460d 100644 --- a/packages/sheets-formula/src/controllers/utils/ref-range-move.ts +++ b/packages/sheets-formula/src/controllers/utils/ref-range-move.ts @@ -119,7 +119,10 @@ export function getNewRangeByMoveParam( if ( remainRange == null && - ((from.endRow < sequenceRange.startRow && to.endRow < sequenceRange.startRow) || (from.startRow > sequenceRange.endRow && to.startRow > sequenceRange.endRow)) + ( + (from.endRow < sequenceRange.startRow && to.endRow <= sequenceRange.startRow) || + (from.startRow > sequenceRange.endRow && to.startRow > sequenceRange.endRow) + ) ) { return; } @@ -157,7 +160,10 @@ export function getNewRangeByMoveParam( if ( remainRange == null && - ((from.endColumn < sequenceRange.startColumn && to.endColumn < sequenceRange.startColumn) || (from.startColumn > sequenceRange.endColumn && to.startColumn > sequenceRange.endColumn)) + ( + (from.endColumn < sequenceRange.startColumn && to.endColumn <= sequenceRange.startColumn) || + (from.startColumn > sequenceRange.endColumn && to.startColumn > sequenceRange.endColumn) + ) ) { return; } diff --git a/tests/formula-integration/src/__snapshots__/test-formula-move---move-formula-cell-result.json b/tests/formula-integration/src/__snapshots__/test-formula-move---move-formula-cell-result.json new file mode 100644 index 0000000000..c0cfeefab6 --- /dev/null +++ b/tests/formula-integration/src/__snapshots__/test-formula-move---move-formula-cell-result.json @@ -0,0 +1,109 @@ +{ + "id": "Czx9Y5", + "sheetOrder": [ + "lXnRfcI9sd18sz0D_6OmT" + ], + "name": "", + "appVersion": "0.8.1", + "locale": "zhCN", + "styles": {}, + "sheets": { + "lXnRfcI9sd18sz0D_6OmT": { + "id": "lXnRfcI9sd18sz0D_6OmT", + "name": "Sheet1", + "tabColor": "", + "hidden": 0, + "rowCount": 1000, + "columnCount": 20, + "zoomRatio": 1, + "freeze": { + "xSplit": 0, + "ySplit": 0, + "startRow": -1, + "startColumn": -1 + }, + "scrollTop": 0, + "scrollLeft": 0, + "defaultColumnWidth": 88, + "defaultRowHeight": 24, + "mergeData": [], + "cellData": { + "3": { + "2": { + "v": 1, + "t": 2 + } + }, + "4": { + "2": { + "v": 2, + "t": 2 + } + }, + "5": { + "2": { + "v": 3, + "t": 2 + }, + "3": { + "f": "=C6", + "v": 3, + "t": 2 + } + }, + "12": { + "6": { + "f": "=C4", + "v": 1, + "t": 2 + } + }, + "13": { + "6": { + "f": "=C5", + "v": 2, + "t": 2 + } + } + }, + "rowData": {}, + "columnData": {}, + "showGridlines": 1, + "rowHeader": { + "width": 46, + "hidden": 0 + }, + "columnHeader": { + "height": 20, + "hidden": 0 + }, + "rightToLeft": 0 + } + }, + "resources": [ + { + "name": "SHEET_RANGE_PROTECTION_PLUGIN", + "data": "" + }, + { + "name": "SHEET_AuthzIoMockService_PLUGIN", + "data": "{}" + }, + { + "name": "SHEET_WORKSHEET_PROTECTION_PLUGIN", + "data": "{}" + }, + { + "name": "SHEET_WORKSHEET_PROTECTION_POINT_PLUGIN", + "data": "{}" + }, + { + "name": "SHEET_DEFINED_NAME_PLUGIN", + "data": "{}" + }, + { + "name": "SHEET_RANGE_THEME_MODEL_PLUGIN", + "data": "{}" + } + ] +} diff --git a/tests/formula-integration/src/__snapshots__/test-formula-move---move-formula-cell.json b/tests/formula-integration/src/__snapshots__/test-formula-move---move-formula-cell.json new file mode 100644 index 0000000000..b490011381 --- /dev/null +++ b/tests/formula-integration/src/__snapshots__/test-formula-move---move-formula-cell.json @@ -0,0 +1,105 @@ +{ + "id": "Czx9Y5", + "sheetOrder": [ + "lXnRfcI9sd18sz0D_6OmT" + ], + "name": "", + "appVersion": "0.8.1", + "locale": "zhCN", + "styles": {}, + "sheets": { + "lXnRfcI9sd18sz0D_6OmT": { + "id": "lXnRfcI9sd18sz0D_6OmT", + "name": "Sheet1", + "tabColor": "", + "hidden": 0, + "rowCount": 1000, + "columnCount": 20, + "zoomRatio": 1, + "freeze": { + "xSplit": 0, + "ySplit": 0, + "startRow": -1, + "startColumn": -1 + }, + "scrollTop": 0, + "scrollLeft": 0, + "defaultColumnWidth": 88, + "defaultRowHeight": 24, + "mergeData": [], + "cellData": { + "3": { + "2": { + "v": 1, + "t": 2 + }, + "3": { + "f": "=C4", + "v": 1, + "t": 2 + } + }, + "4": { + "2": { + "v": 2, + "t": 2 + }, + "3": { + "f": "=C5", + "v": 2, + "t": 2 + } + }, + "5": { + "2": { + "v": 3, + "t": 2 + }, + "3": { + "f": "=C6", + "v": 3, + "t": 2 + } + } + }, + "rowData": {}, + "columnData": {}, + "showGridlines": 1, + "rowHeader": { + "width": 46, + "hidden": 0 + }, + "columnHeader": { + "height": 20, + "hidden": 0 + }, + "rightToLeft": 0 + } + }, + "resources": [ + { + "name": "SHEET_RANGE_PROTECTION_PLUGIN", + "data": "" + }, + { + "name": "SHEET_AuthzIoMockService_PLUGIN", + "data": "{}" + }, + { + "name": "SHEET_WORKSHEET_PROTECTION_PLUGIN", + "data": "{}" + }, + { + "name": "SHEET_WORKSHEET_PROTECTION_POINT_PLUGIN", + "data": "{}" + }, + { + "name": "SHEET_DEFINED_NAME_PLUGIN", + "data": "{}" + }, + { + "name": "SHEET_RANGE_THEME_MODEL_PLUGIN", + "data": "{}" + } + ] +} diff --git a/tests/formula-integration/src/__snapshots__/test-formula-move---move-formula-rows-result.json b/tests/formula-integration/src/__snapshots__/test-formula-move---move-formula-rows-result.json new file mode 100644 index 0000000000..ec71e47a31 --- /dev/null +++ b/tests/formula-integration/src/__snapshots__/test-formula-move---move-formula-rows-result.json @@ -0,0 +1,105 @@ +{ + "id": "wcKMFW", + "sheetOrder": [ + "XkNPQsSrdwlQrVOFgt9FM" + ], + "name": "", + "appVersion": "0.8.1", + "locale": "zhCN", + "styles": {}, + "sheets": { + "XkNPQsSrdwlQrVOFgt9FM": { + "id": "XkNPQsSrdwlQrVOFgt9FM", + "name": "Sheet1", + "tabColor": "", + "hidden": 0, + "rowCount": 1000, + "columnCount": 20, + "zoomRatio": 1, + "freeze": { + "xSplit": 0, + "ySplit": 0, + "startRow": -1, + "startColumn": -1 + }, + "scrollTop": 0, + "scrollLeft": 0, + "defaultColumnWidth": 88, + "defaultRowHeight": 24, + "mergeData": [], + "cellData": { + "2": { + "2": { + "v": 2, + "t": 2 + }, + "3": { + "f": "=C3", + "v": 2, + "t": 2 + } + }, + "3": { + "2": { + "v": 1, + "t": 2 + }, + "3": { + "f": "=C4", + "v": 1, + "t": 2 + } + }, + "4": { + "2": { + "v": 3, + "t": 2 + }, + "3": { + "f": "=C5", + "v": 3, + "t": 2 + } + } + }, + "rowData": {}, + "columnData": {}, + "showGridlines": 1, + "rowHeader": { + "width": 46, + "hidden": 0 + }, + "columnHeader": { + "height": 20, + "hidden": 0 + }, + "rightToLeft": 0 + } + }, + "resources": [ + { + "name": "SHEET_RANGE_PROTECTION_PLUGIN", + "data": "" + }, + { + "name": "SHEET_AuthzIoMockService_PLUGIN", + "data": "{}" + }, + { + "name": "SHEET_WORKSHEET_PROTECTION_PLUGIN", + "data": "{}" + }, + { + "name": "SHEET_WORKSHEET_PROTECTION_POINT_PLUGIN", + "data": "{}" + }, + { + "name": "SHEET_DEFINED_NAME_PLUGIN", + "data": "{}" + }, + { + "name": "SHEET_RANGE_THEME_MODEL_PLUGIN", + "data": "{}" + } + ] +} diff --git a/tests/formula-integration/src/__snapshots__/test-formula-move---move-formula-rows.json b/tests/formula-integration/src/__snapshots__/test-formula-move---move-formula-rows.json new file mode 100644 index 0000000000..ab1f965c16 --- /dev/null +++ b/tests/formula-integration/src/__snapshots__/test-formula-move---move-formula-rows.json @@ -0,0 +1,105 @@ +{ + "id": "wcKMFW", + "sheetOrder": [ + "XkNPQsSrdwlQrVOFgt9FM" + ], + "name": "", + "appVersion": "0.8.1", + "locale": "zhCN", + "styles": {}, + "sheets": { + "XkNPQsSrdwlQrVOFgt9FM": { + "id": "XkNPQsSrdwlQrVOFgt9FM", + "name": "Sheet1", + "tabColor": "", + "hidden": 0, + "rowCount": 1000, + "columnCount": 20, + "zoomRatio": 1, + "freeze": { + "xSplit": 0, + "ySplit": 0, + "startRow": -1, + "startColumn": -1 + }, + "scrollTop": 0, + "scrollLeft": 0, + "defaultColumnWidth": 88, + "defaultRowHeight": 24, + "mergeData": [], + "cellData": { + "2": { + "2": { + "v": 1, + "t": 2 + }, + "3": { + "f": "=C3", + "v": 1, + "t": 2 + } + }, + "3": { + "2": { + "v": 2, + "t": 2 + }, + "3": { + "f": "=C4", + "v": 2, + "t": 2 + } + }, + "4": { + "2": { + "v": 3, + "t": 2 + }, + "3": { + "f": "=C5", + "v": 3, + "t": 2 + } + } + }, + "rowData": {}, + "columnData": {}, + "showGridlines": 1, + "rowHeader": { + "width": 46, + "hidden": 0 + }, + "columnHeader": { + "height": 20, + "hidden": 0 + }, + "rightToLeft": 0 + } + }, + "resources": [ + { + "name": "SHEET_RANGE_PROTECTION_PLUGIN", + "data": "" + }, + { + "name": "SHEET_AuthzIoMockService_PLUGIN", + "data": "{}" + }, + { + "name": "SHEET_WORKSHEET_PROTECTION_PLUGIN", + "data": "{}" + }, + { + "name": "SHEET_WORKSHEET_PROTECTION_POINT_PLUGIN", + "data": "{}" + }, + { + "name": "SHEET_DEFINED_NAME_PLUGIN", + "data": "{}" + }, + { + "name": "SHEET_RANGE_THEME_MODEL_PLUGIN", + "data": "{}" + } + ] +} diff --git a/tests/formula-integration/src/__testing__/test-formula-move.ts b/tests/formula-integration/src/__testing__/test-formula-move.ts new file mode 100644 index 0000000000..03179a94a5 --- /dev/null +++ b/tests/formula-integration/src/__testing__/test-formula-move.ts @@ -0,0 +1,152 @@ +/** + * Copyright 2023-present DreamNum Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { IWorkbookData } from '@univerjs/core'; +import fs from 'node:fs'; +import path from 'node:path'; +import { IUniverInstanceService, LocaleType, Univer } from '@univerjs/core'; +import { FUniver } from '@univerjs/core/facade'; +import { UniverFormulaEnginePlugin } from '@univerjs/engine-formula'; +import zhCN from '@univerjs/mockdata/locales/zh-CN'; +import { MoveRangeCommand, UniverSheetsPlugin } from '@univerjs/sheets'; +import { UniverSheetsFormulaPlugin } from '@univerjs/sheets-formula'; +import { expect } from 'vitest'; +import { getTestFilePath, getTestName } from './util'; + +function createTestBed() { + const univer = new Univer({ + locale: LocaleType.ZH_CN, + locales: { + [LocaleType.ZH_CN]: zhCN, + }, + }); + + univer.registerPlugin(UniverFormulaEnginePlugin); + univer.registerPlugin(UniverSheetsPlugin); + univer.registerPlugin(UniverSheetsFormulaPlugin); + + const injector = univer.__getInjector(); + + return { + univer, + get: injector.get.bind(injector), + api: FUniver.newAPI(univer), + }; +} + +export async function expectMoveFormulaRowsResultMatchesSnapshot() { + const testBed = createTestBed(); + const snapshotRootDir = path.join(import.meta.dirname, '../__snapshots__'); + + const testSnapshotPath = path.resolve(snapshotRootDir, `${getTestFilePath()}.json`); + if (!fs.existsSync(testSnapshotPath)) { + throw new Error(`Cannot find snapshot file for test "${getTestName()}".`); + } + + const testSnapshotRaw = fs.readFileSync(testSnapshotPath, 'utf-8'); + const testSnapshot = JSON.parse(testSnapshotRaw) as IWorkbookData; + + const workbook = testBed.api.createWorkbook(testSnapshot); + const univerInstanceService = testBed.get(IUniverInstanceService); + univerInstanceService.focusUnit(workbook.getId()); + const worksheet = workbook.getActiveSheet(); + + // move row 3 to before row 5 + const rowSpec = worksheet.getRange('3:3'); + worksheet.moveRows(rowSpec, 4); + + const resultSnapshot = workbook.save(); + const snapshotFilePath = path.resolve(snapshotRootDir, `${getTestFilePath()}-result.json`); + if (fs.existsSync(snapshotFilePath)) { + const resultSnapshotFileString = fs.readFileSync(snapshotFilePath, 'utf-8'); + expect(resultSnapshot).toMatchObject(JSON.parse(resultSnapshotFileString)); + } else { + fs.writeFileSync(snapshotFilePath, JSON.stringify(resultSnapshot, null, 4)); + + // eslint-disable-next-line no-console + console.log(`Snapshot file created at: ${snapshotFilePath}`); + } + + // perform undo operation + await testBed.api.undo(); + + // compare the result with the snapshot + const resultSnapshot_undo = workbook.save(); + const snapshotFilePath_undo = path.resolve(snapshotRootDir, `${getTestFilePath()}.json`); + if (fs.existsSync(snapshotFilePath_undo)) { + const resultSnapshotFileString = fs.readFileSync(snapshotFilePath_undo, 'utf-8'); + expect(resultSnapshot_undo).toMatchObject(JSON.parse(resultSnapshotFileString)); + } else { + fs.writeFileSync(snapshotFilePath_undo, JSON.stringify(resultSnapshot_undo, null, 4)); + + // eslint-disable-next-line no-console + console.log(`Snapshot file created at: ${snapshotFilePath_undo}`); + } +} + +export async function expectMoveFormulaCellResultMatchesSnapshot() { + const testBed = createTestBed(); + const snapshotRootDir = path.join(import.meta.dirname, '../__snapshots__'); + + const testSnapshotPath = path.resolve(snapshotRootDir, `${getTestFilePath()}.json`); + if (!fs.existsSync(testSnapshotPath)) { + throw new Error(`Cannot find snapshot file for test "${getTestName()}".`); + } + + const testSnapshotRaw = fs.readFileSync(testSnapshotPath, 'utf-8'); + const testSnapshot = JSON.parse(testSnapshotRaw) as IWorkbookData; + + const workbook = testBed.api.createWorkbook(testSnapshot); + const univerInstanceService = testBed.get(IUniverInstanceService); + univerInstanceService.focusUnit(workbook.getId()); + const worksheet = workbook.getActiveSheet(); + + // move D4:D5 to G13:G14 + const fromRange = worksheet.getRange('D4:D5').getRange(); + const toRange = worksheet.getRange('G13:G14').getRange(); + await testBed.api.executeCommand(MoveRangeCommand.id, { + fromRange, + toRange, + }); + + const resultSnapshot = workbook.save(); + const snapshotFilePath = path.resolve(snapshotRootDir, `${getTestFilePath()}-result.json`); + if (fs.existsSync(snapshotFilePath)) { + const resultSnapshotFileString = fs.readFileSync(snapshotFilePath, 'utf-8'); + expect(resultSnapshot).toMatchObject(JSON.parse(resultSnapshotFileString)); + } else { + fs.writeFileSync(snapshotFilePath, JSON.stringify(resultSnapshot, null, 4)); + + // eslint-disable-next-line no-console + console.log(`Snapshot file created at: ${snapshotFilePath}`); + } + + // perform undo operation + await testBed.api.undo(); + + // compare the result with the snapshot + const resultSnapshot_undo = workbook.save(); + const snapshotFilePath_undo = path.resolve(snapshotRootDir, `${getTestFilePath()}.json`); + if (fs.existsSync(snapshotFilePath_undo)) { + const resultSnapshotFileString = fs.readFileSync(snapshotFilePath_undo, 'utf-8'); + expect(resultSnapshot_undo).toMatchObject(JSON.parse(resultSnapshotFileString)); + } else { + fs.writeFileSync(snapshotFilePath_undo, JSON.stringify(resultSnapshot_undo, null, 4)); + + // eslint-disable-next-line no-console + console.log(`Snapshot file created at: ${snapshotFilePath_undo}`); + } +} diff --git a/tests/formula-integration/src/__testing__/test-remove-rows-of-filter-rows.ts b/tests/formula-integration/src/__testing__/test-remove-rows-of-filter-rows.ts index 1c844b7ea8..bc2a618fec 100644 --- a/tests/formula-integration/src/__testing__/test-remove-rows-of-filter-rows.ts +++ b/tests/formula-integration/src/__testing__/test-remove-rows-of-filter-rows.ts @@ -63,7 +63,7 @@ export async function expectRemoveRowsOfFilterRowsResultMatchesSnapshot() { const workbook = testBed.api.createWorkbook(testSnapshot); const univerInstanceService = testBed.get(IUniverInstanceService); - univerInstanceService.focusUnit('YoRIim'); + univerInstanceService.focusUnit(workbook.getId()); const worksheet = workbook.getActiveSheet(); // remove rows 2 to 5, where the 3 to 4 rows are filtered rows diff --git a/tests/formula-integration/src/__tests__/test-formula-move.spec.ts b/tests/formula-integration/src/__tests__/test-formula-move.spec.ts new file mode 100644 index 0000000000..741ac9eb6d --- /dev/null +++ b/tests/formula-integration/src/__tests__/test-formula-move.spec.ts @@ -0,0 +1,28 @@ +/** + * Copyright 2023-present DreamNum Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { describe, it } from 'vitest'; +import { expectMoveFormulaCellResultMatchesSnapshot, expectMoveFormulaRowsResultMatchesSnapshot } from '../__testing__/test-formula-move'; + +describe('Test formula move', () => { + it('move formula rows', () => { + expectMoveFormulaRowsResultMatchesSnapshot(); + }); + + it('move formula cell', () => { + expectMoveFormulaCellResultMatchesSnapshot(); + }); +});