Fix #2943: preserve falsy formula results (0, false, empty string) in copy#52
Fix #2943: preserve falsy formula results (0, false, empty string) in copy#52senoff wants to merge 1 commit intoprotobi:masterfrom
Conversation
…ing) in copy The FormulaValue copy path used truthy checks (`if (value)`) that dropped formula results equal to 0, false, or ''. Copying a cell with cached result === 0 produced a destination cell with no result. Fixes: - lib/doc/cell.js FormulaValue._copyModel: replace `if (value)` with `if (name in model && model[name] !== undefined)` so falsy results survive value getter/setter copy. - lib/doc/cell.js FormulaValue.toCsvString / toString: stop collapsing falsy results to '' via `||` / ternary; only treat null/undefined as absent. - lib/doc/cell.js FormulaValue.effectiveType: add Boolean branch so a result === false returns ValueType.Boolean instead of crashing on `false.text`. - lib/xlsx/xform/sheet/cell-xform.js parseClose: replace `if (model.value)` with `if (model.value !== undefined)` and special-case `<v/>` for `t="str"` formula cells so empty-string results survive xlsx round-trip. Drive-by: extract long shared-formula error message to a local so prettier doesn't restructure the throw across lines (avoids prettier/eslint trailing-comma conflict on edits to this file). Standalone Mocha regression at spec/integration/issues covers value-copy and xlsx round-trip for all three falsy results plus toCsvString/toString. Refs exceljs#2943.
|
Local test run on this branch —
No regressions vs baseline. Skipped Posted because the |
|
Ran exceljs's headless test suite against this PR: (Skipped All green. Hopefully useful evidence for review. |
|
Regenerated per AGENTS.md guidance — see new PR #68. Closing this one in favor of the regenerated version. |
Summary
Fixes exceljs/exceljs#2943: copying a formula cell whose cached
resultis0,false, or''produced a destination cell with noresult.The root cause is
FormulaValue._copyModelinlib/doc/cell.js, which iterates over candidate fields withif (value)— a truthy check that drops every falsy result. Because_copyModelbacks both thevaluegetter and setter, the bug fires on the canonical copy idiomdest.value = src.value(and on every plain read ofcell.valueon a formula cell with a falsy cached result).While auditing the same file I found three sibling falsy-check bugs and one xlsx-reader bug with the same shape, all fixed in this PR.
Changes
lib/doc/cell.js—FormulaValue_copyModel: replaceif (value)withif (name in model && model[name] !== undefined)so0,false,''survive the copy.toCsvString: replace${this.model.result || ''}with an explicitnull/undefinedcheck; previouslyresult === 0rendered as empty string in CSV.toString: replacethis.model.result ? ... : ''ternary with the same explicit check.effectiveType: add atypeof v === 'boolean'branch so a formula withresult: falsereturnsValueType.Booleaninstead of throwing onfalse.textwhen falling through to the hyperlink check.lib/xlsx/xform/sheet/cell-xform.js—parseCloseif (model.value)withif (model.value !== undefined)and add anelse if (this.t === 'str')branch that setsmodel.result = ''for<c t="str"><f>...</f><v/></c>. Without this, a string-typed formula with empty-string result drops the result on read.comma-dangleconflict that fires whenever this file is edited).Tests
spec/integration/issues/issue-2943-formula-falsy-result.spec.js— 9 assertions:result === 0,false,''result === 0,false,''toCsvStringreturns'0'forresult === 0and'false'forresult === falsetoStringreturns'0'forresult === 0All pre-existing tests pass:
884 unit + 209 integration + 1 end-to-end.Test plan
npm run test:unit— 884 passingnpm run test:integration— 209 passing (was 207; +2 from new spec)npm run test:end-to-end— 1 passingeslintclean on changed filesprettier --checkclean on changed fileslint-staged) succeeds without--no-verify