diff --git a/package-lock.json b/package-lock.json index 1d852aa8..882c9da7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "idb-keyval": "^6.2.0", "mathjax": "^3.2.2", "mathjs": "^11.8.2", - "mathlive": "github:mgreminger/mathlive#29b14918088a7f0ad9f8204f982447a7dbc6c8df", + "mathlive": "github:mgreminger/mathlive#bbe2b44a1f84f4107784a2f92beda30bc62411f1", "plotly.js-basic-dist": "github:mgreminger/plotly.js#dist-basic-for-ep", "quick-lru": "^6.1.1", "quill": "^1.3.7", @@ -5274,8 +5274,8 @@ }, "node_modules/mathlive": { "version": "0.98.6", - "resolved": "git+ssh://git@github.com/mgreminger/mathlive.git#29b14918088a7f0ad9f8204f982447a7dbc6c8df", - "integrity": "sha512-TWC1iRy9bi9VQPk/5eRAPwm39SvvETm01KAY/wj+Nnzf7agSUbclRMRI530Mc3Gaz2ehbNZEzVmIsVhJQk/aZg==", + "resolved": "git+ssh://git@github.com/mgreminger/mathlive.git#bbe2b44a1f84f4107784a2f92beda30bc62411f1", + "integrity": "sha512-6frGRbwga8Q6/HLf9tGUQtUBwwzifTB1B/4TbAy2ahzUI26MMsQMunldVPcOaF6sc2GMhP4lVzSU0T72IEa7aQ==", "license": "MIT", "dependencies": { "@cortex-js/compute-engine": "0.23.1" @@ -11720,9 +11720,9 @@ } }, "mathlive": { - "version": "git+ssh://git@github.com/mgreminger/mathlive.git#29b14918088a7f0ad9f8204f982447a7dbc6c8df", - "integrity": "sha512-TWC1iRy9bi9VQPk/5eRAPwm39SvvETm01KAY/wj+Nnzf7agSUbclRMRI530Mc3Gaz2ehbNZEzVmIsVhJQk/aZg==", - "from": "mathlive@github:mgreminger/mathlive#29b14918088a7f0ad9f8204f982447a7dbc6c8df", + "version": "git+ssh://git@github.com/mgreminger/mathlive.git#bbe2b44a1f84f4107784a2f92beda30bc62411f1", + "integrity": "sha512-6frGRbwga8Q6/HLf9tGUQtUBwwzifTB1B/4TbAy2ahzUI26MMsQMunldVPcOaF6sc2GMhP4lVzSU0T72IEa7aQ==", + "from": "mathlive@github:mgreminger/mathlive#bbe2b44a1f84f4107784a2f92beda30bc62411f1", "requires": { "@cortex-js/compute-engine": "0.23.1" } diff --git a/package.json b/package.json index a6c4608e..fd1f57f5 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "idb-keyval": "^6.2.0", "mathjax": "^3.2.2", "mathjs": "^11.8.2", - "mathlive": "github:mgreminger/mathlive#29b14918088a7f0ad9f8204f982447a7dbc6c8df", + "mathlive": "github:mgreminger/mathlive#bbe2b44a1f84f4107784a2f92beda30bc62411f1", "plotly.js-basic-dist": "github:mgreminger/plotly.js#dist-basic-for-ep", "quick-lru": "^6.1.1", "quill": "^1.3.7", diff --git a/src/App.svelte b/src/App.svelte index 04804390..eedfe96e 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -7,7 +7,7 @@ import PlotCell from "./cells/PlotCell"; import PiecewiseCell from "./cells/PiecewiseCell"; import SystemCell from "./cells/SystemCell"; - import { cells, title, results, system_results, history, insertedSheets, activeCell, + import { cells, title, results, resultsInvalid, system_results, history, insertedSheets, activeCell, getSheetJson, getSheetObject, resetSheet, sheetId, mathCellChanged, nonMathCellChanged, addCell, prefersReducedMotion, modifierKey, inCellInsertMode, config, unsavedChange, incrementActiveCell, decrementActiveCell, deleteCell, activeMathField, @@ -90,7 +90,7 @@ const apiUrl = window.location.origin; - const currentVersion = 20240418; + const currentVersion = 20240513; const tutorialHash = "fFjTsnFoSQMLwcvteVoNtL"; const termsVersion = 20240110; @@ -762,7 +762,7 @@ refreshSheet(); // pushState does not trigger onpopstate event } - function getResults(statementsAndSystems: string, myRefreshCount: bigint) { + function getResults(statementsAndSystems: string, myRefreshCount: BigInt) { return new Promise((resolve, reject) => { function handleWorkerMessage(e) { forcePyodidePromiseRejection = null; @@ -819,13 +819,14 @@ statements.push(mathField.statement); } } else if (cell instanceof TableCell) { - const newStatements = cell.parseTableStatements(); + const newStatements = cell.tableStatements; for (const statement of newStatements) { endStatements.push(statement); } } else if (cell instanceof PiecewiseCell) { - const statement = cell.parsePiecewiseStatement(cellNum); - endStatements.push(statement); + if (cell.piecewiseStatement) { + endStatements.push(cell.piecewiseStatement); + } } else if (cell instanceof SystemCell) { const systemDefinition = cell.getSystemDefinition(); if (systemDefinition) { @@ -870,15 +871,18 @@ } } - async function handleCellUpdate() { - const myRefreshCount = ++refreshCounter; + async function handleCellUpdate(localRefreshCounter: BigInt) { + if (localRefreshCounter !== refreshCounter) { + return; + } + const myRefreshCount = localRefreshCounter; const firstRunAfterSheetLoad = initialSheetLoad; initialSheetLoad = false; inDebounce = false; if(noParsingErrors && !firstRunAfterSheetLoad) { - // remove existing results if all math fields are valid (while editing current cell) - // also, don't clear results if sheet was just loaded without modification (initialSheetLoad === true) - $results = []; + // invalidate results if all math fields are valid (while editing current cell) + // also, don't invalidate results if sheet was just loaded without modification (initialSheetLoad === true) + $resultsInvalid = true; error = ""; } await pyodidePromise; @@ -888,17 +892,20 @@ clearTimeout(pyodideTimeoutRef); pyodideTimeoutRef = window.setTimeout(() => pyodideTimeout=true, pyodideTimeoutLength); if (!firstRunAfterSheetLoad) { - $results = []; + $resultsInvalid = true; error = ""; } pyodidePromise = getResults(statementsAndSystems, myRefreshCount) .then((data: Results) => { $results = []; + $resultsInvalid = false; if (!data.error && data.results.length > 0) { let counter = 0; for (const [i, cell] of $cells.entries()) { if ((cell.type === "math" || cell.type === "plot") ) { $results[i] = data.results[counter++]; + } else { + $results[i] = null; } } } @@ -908,6 +915,8 @@ for (const [i, cell] of $cells.entries()) { if (cell.type === "system") { $system_results[i] = data.systemResults[counter++] + } else { + $system_results[i] = null; } } if (!firstRunAfterSheetLoad) { @@ -1089,6 +1098,7 @@ Please include a link to this sheet in the email to assist in debugging the prob $cells = []; $results = []; + $resultsInvalid = true; $system_results = []; $activeCell = -1; @@ -1114,10 +1124,12 @@ Please include a link to this sheet in the email to assist in debugging the prob if (noParsingErrors) { $results = sheet.results; + $resultsInvalid = false; // old documents in the database won't have the system_results property $system_results = sheet.system_results ? sheet.system_results : []; } else { $results = []; + $resultsInvalid = true; $system_results = []; } @@ -1479,6 +1491,7 @@ Please include a link to this sheet in the email to assist in debugging the prob try{ $results = []; + $resultsInvalid = true; $system_results = []; const newCells = sheet.cells.map(cellFactory); @@ -1912,29 +1925,21 @@ Please include a link to this sheet in the email to assist in debugging the prob }; } - $:{ - if (document.hasFocus() && showKeyboard !== Boolean($activeMathField)) { + $:if (document.hasFocus() && showKeyboard !== Boolean($activeMathField)) { showKeyboard = Boolean($activeMathField); } - } - $: { - document.title = `EngineeringPaper.xyz: ${$title}`; - } + $: document.title = `EngineeringPaper.xyz: ${$title}`; - $: if($cells) { + $: if($mathCellChanged) { + refreshCounter++; + $mathCellChanged = false; noParsingErrors = !checkParsingErrors(); - } - - $: if ($cells || $mathCellChanged) { - if($mathCellChanged) { - if (initialSheetLoad) { - handleCellUpdate(); - } else { - inDebounce = true; - debounceHandleCellUpdate(); - } - $mathCellChanged = false; + if (initialSheetLoad) { + handleCellUpdate(refreshCounter); + } else { + inDebounce = true; + debounceHandleCellUpdate(refreshCounter); } $unsavedChange = true; $autosaveNeeded = true; @@ -2194,6 +2199,12 @@ Please include a link to this sheet in the email to assist in debugging the prob } } + @media (max-width: 450px) { + :global(.hide-when-kinda-narrow) { + display: none; + } + } + @media (max-width: 400px) { :global(.hide-when-narrow) { display: none; diff --git a/src/Cell.svelte b/src/Cell.svelte index 84b47b19..82188909 100644 --- a/src/Cell.svelte +++ b/src/Cell.svelte @@ -1,6 +1,7 @@
-

Cell Deleted

+

Deleting Cell

- +
\ No newline at end of file diff --git a/src/GenerateCodeDialog.svelte b/src/GenerateCodeDialog.svelte index ff64ec93..0ad37db1 100644 --- a/src/GenerateCodeDialog.svelte +++ b/src/GenerateCodeDialog.svelte @@ -22,7 +22,6 @@ if (statement) { statement.generateCode = true; - $cells = $cells; $mathCellChanged = true; } }); diff --git a/src/InsertCell.svelte b/src/InsertCell.svelte index 02cb09a6..85baa171 100644 --- a/src/InsertCell.svelte +++ b/src/InsertCell.svelte @@ -1,7 +1,8 @@ +May 13, 2024 +

Usability Improvements and Bug Fixes

+

+ This release includes the following usability improvements and bug fixes: +

+ + +
+ April 18, 2024

Maximum Matrix Columns Limit Increased

diff --git a/src/cells/DeletedCell.ts b/src/cells/DeletedCell.ts index f938134b..dc2c04d5 100644 --- a/src/cells/DeletedCell.ts +++ b/src/cells/DeletedCell.ts @@ -4,10 +4,12 @@ import type { Cell } from "./Cells"; export default class DeletedCell extends BaseCell { deletedCell: Cell + height: number - constructor (deletedCell: Cell) { + constructor (deletedCell: Cell, height=0) { super("deleted"); this.deletedCell = deletedCell; + this.height = height; } serialize() { diff --git a/src/cells/PiecewiseCell.ts b/src/cells/PiecewiseCell.ts index a690683c..74fbd4f3 100644 --- a/src/cells/PiecewiseCell.ts +++ b/src/cells/PiecewiseCell.ts @@ -6,6 +6,7 @@ export default class PiecewiseCell extends BaseCell { parameterField: MathField; expressionFields: MathField[]; conditionFields: MathField[]; + piecewiseStatement: Statement | null; constructor (arg?: DatabasePiecewiseCell) { if (arg === undefined) { @@ -13,11 +14,13 @@ export default class PiecewiseCell extends BaseCell { this.parameterField = new MathField('', 'parameter'); this.expressionFields = [new MathField('', 'expression_no_blank'), new MathField('', 'expression_no_blank')]; this.conditionFields = [new MathField('', 'condition'), ]; + this.piecewiseStatement = null; } else { super("piecewise", arg.id); this.parameterField = new MathField(arg.parameterLatex, 'parameter'); this.expressionFields = arg.expressionLatexs.map((latex) => new MathField(latex, "expression_no_blank")); this.conditionFields = arg.conditionLatexs.map((latex) => new MathField(latex, "condition")); + this.piecewiseStatement = null; } } @@ -31,18 +34,24 @@ export default class PiecewiseCell extends BaseCell { }; } - parsePiecewiseStatement(cellNum: number): Statement { - let args = this.expressionFields - .slice(0,-1) - .map((exp, index) => `(${exp.latex},${this.conditionFields[index].latex}),`) - .reduce((accum, value) => accum + value, ''); - const latex = `${this.parameterField.latex} = piecewise( ${args}(${this.expressionFields.slice(-1)[0].latex}, 1>0) )`; + parsePiecewiseStatement() { + if (!(this.parameterField.parsingError || + this.expressionFields.some(value => value.parsingError) || + this.conditionFields.some(value => value.parsingError))) { + let args = this.expressionFields + .slice(0,-1) + .map((exp, index) => `(${exp.latex},${this.conditionFields[index].latex}),`) + .reduce((accum, value) => accum + value, ''); + const latex = `${this.parameterField.latex} = piecewise( ${args}(${this.expressionFields.slice(-1)[0].latex}, 1>0) )`; - const mathField = new MathField(latex, 'piecewise'); + const mathField = new MathField(latex, 'piecewise'); - mathField.parseLatex(latex); + mathField.parseLatex(latex); - return mathField.statement; + this.piecewiseStatement = mathField.statement; + } else { + this.piecewiseStatement = null; + } } diff --git a/src/cells/TableCell.ts b/src/cells/TableCell.ts index dd7c9257..750b4ce5 100644 --- a/src/cells/TableCell.ts +++ b/src/cells/TableCell.ts @@ -1,6 +1,7 @@ import { BaseCell, type DatabaseTableCell } from "./BaseCell"; import { MathField } from "./MathField"; import type { Statement } from "../parser/types"; +import QuickLRU from "quick-lru"; class TableRowLabelField { label: string; @@ -25,6 +26,8 @@ export default class TableCell extends BaseCell { hideUnselected: boolean; rowJsons: string[]; richTextInstance: HTMLElement | null; + tableStatements: Statement[]; + cache: QuickLRU; constructor (arg?: DatabaseTableCell) { if (arg === undefined) { @@ -41,6 +44,8 @@ export default class TableCell extends BaseCell { this.hideUnselected = false; this.rowJsons = []; this.richTextInstance = null; + this.tableStatements = []; + this.cache = new QuickLRU({maxSize: 100}); } else { super("table", arg.id); this.rowLabels = arg.rowLabels.map((label) => new TableRowLabelField(label)); @@ -54,6 +59,8 @@ export default class TableCell extends BaseCell { this.hideUnselected = arg.hideUnselected; this.rowJsons = arg.rowJsons; this.richTextInstance = null; + this.tableStatements = []; + this.cache = new QuickLRU({maxSize: 100}); } } @@ -87,9 +94,9 @@ export default class TableCell extends BaseCell { } - parseTableStatements(): Statement[] { + parseTableStatements() { const rowIndex = this.selectedRow; - const statements = []; + this.tableStatements = []; if (!(this.parameterFields.some(value => value.parsingError) || this.parameterUnitFields.some(value => value.parsingError) || @@ -101,14 +108,16 @@ export default class TableCell extends BaseCell { this.rhsFields[rowIndex][colIndex].latex + this.parameterUnitFields[colIndex].latex; - this.combinedFields[colIndex].parseLatex(combinedLatex); - - statements.push(this.combinedFields[colIndex].statement); + if (this.cache.has(combinedLatex)) { + this.tableStatements.push(this.cache.get(combinedLatex)); + } else { + this.combinedFields[colIndex].parseLatex(combinedLatex); + this.tableStatements.push(this.combinedFields[colIndex].statement); + this.cache.set(combinedLatex, this.combinedFields[colIndex].statement) + } } } - } - - return statements; + } } addRowDocumentation() { @@ -162,7 +171,7 @@ export default class TableCell extends BaseCell { this.rhsFields = [...this.rhsFields.slice(0,rowIndex), ...this.rhsFields.slice(rowIndex+1)]; - if (this.selectedRow === rowIndex) { + if (this.selectedRow >= rowIndex) { if (this.selectedRow !== 0) { this.selectedRow -= 1; return true diff --git a/src/stores.ts b/src/stores.ts index 7a54c203..4c39b608 100644 --- a/src/stores.ts +++ b/src/stores.ts @@ -25,8 +25,9 @@ export const autosaveNeeded = writable(false); export const config = writable(getDefaultConfig()); export const cells: Writable = writable([]); export const title = writable(defaultTitle); -export const results: Writable<(Result | FiniteImagResult | MatrixResult | PlotResult[])[]> = writable([]); -export const system_results: Writable = writable([]); +export const results: Writable<(Result | FiniteImagResult | MatrixResult | PlotResult[] | null)[]> = writable([]); +export const system_results: Writable = writable([]); +export const resultsInvalid = writable(false); export const sheetId = writable(''); export const insertedSheets: Writable = writable([]); @@ -52,8 +53,9 @@ export const mathJaxLoaded = writable(false); export function addCell(type: CellTypes, index?: number) { - const currentCells:Cell[] = get(cells); - const current_system_results:any[] = get(system_results); + const currentCells = get(cells); + const currentResults = get(results); + const currentSystemResults = get(system_results); if (index === undefined){ index = currentCells.length; @@ -81,21 +83,17 @@ export function addCell(type: CellTypes, index?: number) { } currentCells.splice(index, 0, newCell); - cells.set(currentCells); - results.set([]); + currentResults.splice(index, 0, null); + results.set(currentResults); - // Adding a cell cannot impact existing system cell results so adjust system_results array accordingly - current_system_results.splice(index, 0, null); - system_results.set(current_system_results); + currentSystemResults.splice(index, 0, null); + system_results.set(currentSystemResults); activeCell.set(index); - if (type === "documentation" || type === "table") { - mathCellChanged.set(true); // results will be cleared so force refresh - } - + mathCellChanged.set(true); } export function handleClickInCell(index: number) { @@ -111,7 +109,7 @@ export function getSheetObject(includeResults=true): Sheet { config: get(config), cells: get(cells).map(x => x.serialize()).filter(item => item !== null), title: get(title), - results: includeResults ? get(results) : [], + results: includeResults ? (get(resultsInvalid) ? [] : get(results)) : [], system_results: includeResults ? get(system_results) : [], nextId: BaseCell.nextId, sheetId: get(sheetId), @@ -130,6 +128,7 @@ export function resetSheet() { cells.set([]); title.set(defaultTitle); results.set([]); + resultsInvalid.set(true); system_results.set([]); BaseCell.nextId = 0; history.set([]); @@ -168,17 +167,32 @@ export function decrementActiveCell() { export function deleteCell(index: number, forceDelete=false) { const currentCells = get(cells); + const currentResults = get(results); + const currentSystemResults = get(system_results); const currentActiveCell = get(activeCell); let newCells: Cell[]; + let newResults: (Result | FiniteImagResult | MatrixResult | PlotResult[])[]; + let newSystemResults: SystemResult[]; if (currentCells[index].type !== "deleted" && currentCells[index].type !== "insert" && !forceDelete) { - newCells = [...currentCells.slice(0,index), new DeletedCellClass(currentCells[index]), ...currentCells.slice(index+1)]; + + const contentDiv = document.getElementById(`cell-${index}`); + let height = 0; + if (contentDiv) { + height = contentDiv.getBoundingClientRect().height; + } + + newCells = [...currentCells.slice(0,index), new DeletedCellClass(currentCells[index], height), ...currentCells.slice(index+1)]; + newResults = [...currentResults.slice(0,index), null, ...currentResults.slice(index+1)]; + newSystemResults = [...currentSystemResults.slice(0,index), null, ...currentSystemResults.slice(index+1)]; } else { // user comfirming delete of an undo delete cell or a insert cell newCells = [...currentCells.slice(0,index), ...currentCells.slice(index+1)]; + newResults = [...currentResults.slice(0,index), ...currentResults.slice(index+1)]; + newSystemResults = [...currentSystemResults.slice(0,index), ...currentSystemResults.slice(index+1)]; } if (currentActiveCell >= newCells.length) { @@ -186,6 +200,10 @@ export function deleteCell(index: number, forceDelete=false) { } cells.set(newCells); - results.set([]); + results.set(newResults); + system_results.set(newSystemResults); + + resultsInvalid.set(true); + mathCellChanged.set(true); } diff --git a/tests/images/chromium_reference.png b/tests/images/chromium_reference.png index ca59b283..635d5b3c 100644 Binary files a/tests/images/chromium_reference.png and b/tests/images/chromium_reference.png differ diff --git a/tests/images/firefox_reference.png b/tests/images/firefox_reference.png index 1a1fa0e4..2993f4fd 100644 Binary files a/tests/images/firefox_reference.png and b/tests/images/firefox_reference.png differ diff --git a/tests/images/webkit_reference.png b/tests/images/webkit_reference.png index 4e504959..ea01ecb4 100644 Binary files a/tests/images/webkit_reference.png and b/tests/images/webkit_reference.png differ diff --git a/tests/test_basic.spec.mjs b/tests/test_basic.spec.mjs index 838ff552..6bbba7f7 100644 --- a/tests/test_basic.spec.mjs +++ b/tests/test_basic.spec.mjs @@ -1288,9 +1288,9 @@ test('Check parsing error handling impact on displayed results', async () => { content = await page.textContent('#result-value-1'); expect(parseLatexFloat(content)).toBeCloseTo(5.11, precision); - // leave cell, result should dissapear + // leave cell, result should be made hidden await page.keyboard.press('Escape'); - await page.locator('#result-value-1').waitFor({state: "detached", timeout: 500}); + await page.locator('#cell-1 >> math-field:not(.editable)').waitFor({state: "hidden", timeout: 500}); }); diff --git a/tests/test_file_save_open.spec.mjs b/tests/test_file_save_open.spec.mjs index 31480fa3..58dca4d3 100644 --- a/tests/test_file_save_open.spec.mjs +++ b/tests/test_file_save_open.spec.mjs @@ -184,7 +184,7 @@ test('Test clearing results on valid input after page initial load form file', a await page.setLatex(0, '1='); // ensure that result is not displayed even though it is in file - await page.locator('#result-value-0').waitFor({state: "detached", timeout: 1000}); + await page.locator('#cell-0 >> math-field:not(.editable)').waitFor({state: "hidden", timeout: 1000}); }); diff --git a/tests/test_keyboard_shortcuts.spec.mjs b/tests/test_keyboard_shortcuts.spec.mjs index 422fff44..2fd6ebe1 100644 --- a/tests/test_keyboard_shortcuts.spec.mjs +++ b/tests/test_keyboard_shortcuts.spec.mjs @@ -202,4 +202,43 @@ test('Test math cell undo/redo', async ({ browserName }) => { content = await page.textContent('#result-value-2'); expect(parseLatexFloat(content)).toBeCloseTo(1010002, precision); +}); + + +test('Test esc to undo delete', async ({ browserName }) => { + + const modifierKey = (await page.evaluate('window.modifierKey') )=== "metaKey" ? "Meta" : "Control"; + + await page.locator('#cell-0 >> math-field.editable').type("x+y="); + await page.keyboard.press('Enter'); + await page.locator('#cell-1 >> math-field.editable').type("x=1"); + await page.keyboard.press('Enter'); + await page.locator('#cell-2 >> math-field.editable').type("y=2"); + + await page.waitForSelector('text=Updating...', {state: 'detached'}); + let content = await page.textContent(`#result-value-0`); + expect(parseLatexFloat(content)).toBeCloseTo(3, precision); + + // test undo delete using escape when delete triggered using keyboard shortcut + await page.locator('#cell-1 >> math-field.editable').click(); + await expect(page.locator('#cell-1 >> math-field.editable')).toBeFocused(); + await page.keyboard.press(modifierKey+'+D'); + await expect(page.locator('text=Undo Delete')).toBeVisible(); + await page.keyboard.press('Escape'); + + await page.waitForSelector('text=Updating...', {state: 'detached'}); + content = await page.textContent(`#result-value-0`); + expect(parseLatexFloat(content)).toBeCloseTo(3, precision); + + // test undo delete using escape when delete triggered using button + await page.locator('#cell-1 >> math-field.editable').click(); + await expect(page.locator('#cell-1 >> math-field.editable')).toBeFocused(); + await page.click('#delete-1'); + await expect(page.locator('text=Undo Delete')).toBeVisible(); + await page.keyboard.press('Escape'); + + await page.waitForSelector('text=Updating...', {state: 'detached'}); + content = await page.textContent(`#result-value-0`); + expect(parseLatexFloat(content)).toBeCloseTo(3, precision); + }); \ No newline at end of file diff --git a/tests/test_table_cell.spec.mjs b/tests/test_table_cell.spec.mjs index b5a80a9b..0a40e9f2 100644 --- a/tests/test_table_cell.spec.mjs +++ b/tests/test_table_cell.spec.mjs @@ -542,4 +542,35 @@ test('Test fix for crash when last column deleted', async () => { await page.locator('text=Updating...').waitFor({state: 'detached'}); content = await page.locator('#result-value-0').textContent(); expect(content).toBe('Var_{2}'); +}); + + +test('Test fix for crash when last row selected and not last row deleted', async () => { + + await page.locator('math-field.editable').nth(0).type('Var1='); + + await page.locator('#add-table-cell').click(); + + await page.locator('#grid-cell-1-0-0 math-field.editable').type('1'); + await page.locator('#grid-cell-1-1-0 math-field.editable').type('2'); + + await page.locator('#row-radio-1-1').check(); + + await page.locator('text=Updating...').waitFor({state: 'detached'}); + let content = await page.locator('#result-value-0').textContent(); + expect(parseLatexFloat(content)).toBeCloseTo(2, precision); + + // delete first row and make sure result updates (selected row should update to first row) + await page.locator('#delete-row-1-0').click(); + + // make sure second row is no longer visible + await page.locator('#row-radio-1-1').waitFor({state: 'detached', timeout: 1000}); + + // enter a value in column 2 to make sure everything is updating as expected + await page.locator('#grid-cell-1-0-1 math-field.editable').type('3'); + await page.setLatex(0, 'Var2='); + + await page.locator('text=Updating...').waitFor({state: 'detached'}); + content = await page.locator('#result-value-0').textContent(); + expect(parseLatexFloat(content)).toBeCloseTo(3, precision); }); \ No newline at end of file