mirror of
https://github.com/dream-num/univer.git
synced 2026-09-21 05:44:04 +08:00
fix(formula): fixed the issue of formula range reference calculating offset when moving rows, columns or ranges (#5412)
This commit is contained in:
+295
-3
@@ -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<Workbook>('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<Workbook>('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<Workbook>('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<Workbook>('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 () => {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Nullable<IFormulaDataItem>>, newFormulaMatrix: ObjectMatrix<Nullable<IFormulaDataItem>>) {
|
||||
const redoFormulaData = new ObjectMatrix<Nullable<ICellData>>({});
|
||||
|
||||
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<Nullable<IFormulaDataItem>>) {
|
||||
const undoFormulaData = new ObjectMatrix<Nullable<ICellData>>({});
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
+109
@@ -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": "{}"
|
||||
}
|
||||
]
|
||||
}
|
||||
+105
@@ -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": "{}"
|
||||
}
|
||||
]
|
||||
}
|
||||
+105
@@ -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": "{}"
|
||||
}
|
||||
]
|
||||
}
|
||||
+105
@@ -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": "{}"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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}`);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user