Skip to content

Fix #3028: make getWorksheet case-insensitive to match addWorksheet#51

Closed
senoff wants to merge 1 commit into
protobi:masterfrom
senoff:fix-3028-getworksheet-case
Closed

Fix #3028: make getWorksheet case-insensitive to match addWorksheet#51
senoff wants to merge 1 commit into
protobi:masterfrom
senoff:fix-3028-getworksheet-case

Conversation

@senoff
Copy link
Copy Markdown

@senoff senoff commented May 1, 2026

Summary

Closes the API contradiction reported in exceljs/exceljs#3028: Workbook.addWorksheet(name) enforces case-insensitive uniqueness (and Excel itself treats sheet names case-insensitively), but Workbook.getWorksheet(name) was matching with a case-sensitive ===. The two APIs disagreed:

const wb = new ExcelJS.Workbook();
wb.addWorksheet('Sheet1');
wb.getWorksheet('sheet1');  // undefined  <- previous behavior
wb.addWorksheet('sheet1');  // throws "Worksheet name already exists: sheet1"

After this PR, getWorksheet('sheet1') returns the existing sheet, matching what addWorksheet's constraint already implies.

Change

lib/doc/workbook.js — in the string branch of getWorksheet, lowercase both the lookup name and each candidate sheet name before comparing. The numeric-id branch and the no-arg first-sheet shortcut are unchanged.

     if (typeof id === 'string') {
-      return this._worksheets.find(worksheet => worksheet && worksheet.name === id);
+      // Case-insensitive to match addWorksheet's uniqueness rule (and Excel itself).
+      const target = id.toLowerCase();
+      return this._worksheets.find(ws => ws && ws.name.toLowerCase() === target);
     }

Behavior change

Minor and intentional: code that previously relied on a casing mismatch returning undefined will now find the sheet. We consider this a bug fix because the prior behavior contradicted addWorksheet's own case-insensitive constraint; there is no way for two sheets with names that differ only in case to coexist, so the case-sensitive lookup could never resolve a real ambiguity.

Documented under "Unreleased" in FORK.md.

Test plan

  • Added spec/integration/issues/issue-3028-getworksheet-case-insensitive.spec.js (5 cases):
    • mixed casings (TestSheet, testsheet, TESTSHEET, tEsTsHeEt) all resolve to the same sheet
    • unknown names still return undefined
    • numeric-id lookup unchanged
    • no-arg first-sheet shortcut unchanged
    • explicit assertion that addWorksheet and getWorksheet contracts now agree
  • npm run test:unit — 884 passing
  • npm run test:integration — 205 passing (including the new 5)
  • npm run test:end-to-end — 1 passing

…sheet

Workbook.addWorksheet enforces case-insensitive name uniqueness (and
Excel itself treats sheet names case-insensitively), but
Workbook.getWorksheet was matching with a case-sensitive ===. As a
result, after addWorksheet("Sheet1"), getWorksheet("sheet1") returned
undefined even though addWorksheet("sheet1") would throw "Worksheet
name already exists".

Lowercase both sides of the string comparison in getWorksheet so any
casing of an existing sheet name resolves to that sheet. Numeric-id
and no-arg branches are unchanged.

Adds spec/integration/issues/issue-3028-getworksheet-case-insensitive.spec.js
covering: mixed casings resolve to the same sheet, unknown names still
return undefined, numeric id and no-arg shortcuts preserved, and the
addWorksheet/getWorksheet contracts now agree.

Drive-by: collapse pre-existing multi-line constructs in the same
function (two console.trace deprecation warnings and an Object.assign
+ a reduce->for loop) so prettier no longer wraps them with a trailing
comma that eslint then rejects. The repo's .prettierrc
(trailingComma: "all") conflicts with .eslintrc (comma-dangle
functions: "never"), and these were the spots prettier wanted to
rewrite around the edited region. Behavior is unchanged.
@senoff
Copy link
Copy Markdown
Author

senoff commented May 3, 2026

Local test run on this branch — npm install --legacy-peer-deps && npm run test:unit && npm run test:integration:

Tier This PR Baseline (master @ 4c9e73c) Δ
test:unit 884 passing, 1 pending, 0 failing 884 passing, 1 pending, 0 failing +5 int (this PR adds tests)
test:integration 205 passing, 0 failing 200 passing, 0 failing +5 int (this PR adds tests)

No regressions vs baseline. Skipped test:end-to-end + test:jasmine (browser/grunt tiers) — same regression-catching surface for unit + integration on a feature branch like this one.

Posted because the tests.yml workflow at this repo hasn't auto-fired on this PR (GitHub's first-time-contributor approval gate). Just want to make the merge feel less risky if/when you get to it. Happy to wait for the official CI run if you'd prefer to flip the approval gate; this is purely supplementary.

@senoff
Copy link
Copy Markdown
Author

senoff commented May 4, 2026

Ran exceljs's headless test suite against this PR:

test:unit         884 passing
test:integration  205 passing
test:end-to-end   1 passing
                 ─────────────
                 1090 passing, 0 failing

(Skipped test:jasmine — browser harness not set up locally.)

All green. Hopefully useful evidence for review.

@senoff
Copy link
Copy Markdown
Author

senoff commented May 7, 2026

Regenerated per AGENTS.md guidance — see new PR #67. Closing this one in favor of the regenerated version.

@senoff senoff closed this May 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant