From 61a9528137ece342d2539846688d04f317d95147 Mon Sep 17 00:00:00 2001 From: L4XB Date: Fri, 25 Sep 2026 18:07:14 +0200 Subject: [PATCH 1/4] fix(xlsx): read filled-down formulas, formula errors and times as shown ExcelJS reads the cells of a formula filled down or across as { sharedFormula, result }, which the "formula" in value check missed, so they read as [object Object]. A formula result also went through toString(), so an error result read as [object Object] and a date result as a local-time JS date string. Every Date was cut to its ISO date, so a date-time lost its time and a time of day or a duration read as 1899-12-30 or 1899-12-31. Read a formula's result like a plain value (cell.formula gives the text of a shared formula without one). Keep the time of a date-time, and read a cell whose number format shows no day or year as a time of day, or as an elapsed duration for [h], [m] and [s], counted from the workbook's day 0. Fixes #1780 --- .../__tests__/extract-text-from-xlsx.test.ts | 67 +++++++++++++++++++ .../misc/extract-text-from-xlsx.ts | 45 ++++++++++--- 2 files changed, 101 insertions(+), 11 deletions(-) diff --git a/src/integrations/misc/__tests__/extract-text-from-xlsx.test.ts b/src/integrations/misc/__tests__/extract-text-from-xlsx.test.ts index a3c46e30b7..5e577b3373 100644 --- a/src/integrations/misc/__tests__/extract-text-from-xlsx.test.ts +++ b/src/integrations/misc/__tests__/extract-text-from-xlsx.test.ts @@ -173,6 +173,73 @@ describe("extractTextFromXLSX", () => { expect(result).toContain("30") // Formula with result expect(result).toContain("[Formula: SUM(B1:B10)]") // Formula without result }) + + it("should read every cell of a filled-down formula", async () => { + const workbook = new ExcelJS.Workbook() + const worksheet = workbook.addWorksheet("Sheet1") + + // Excel saves a formula filled down a column as one shared formula + worksheet.getCell("A1").value = { formula: "B1*2", result: 2 } + worksheet.getCell("A2").value = { sharedFormula: "A1", result: 4 } + worksheet.getCell("A3").value = { sharedFormula: "A1" } + + const result = await extractTextFromXLSX(workbook) + + expect(result).toBe("--- Sheet: Sheet1 ---\n2\n4\n[Formula: B3*2]") + }) + + it("should read formula errors and dates like plain values", async () => { + const workbook = new ExcelJS.Workbook() + const worksheet = workbook.addWorksheet("Sheet1") + + worksheet.getCell("A1").value = { formula: "1/0", result: { error: "#DIV/0!" } } + worksheet.getCell("B1").value = { formula: "A2+30", result: new Date("2024-10-30T00:00:00.000Z") } + worksheet.getCell("B1").numFmt = "yyyy-mm-dd" + + const result = await extractTextFromXLSX(workbook) + + expect(result).toBe("--- Sheet: Sheet1 ---\n[Error: #DIV/0!]\t2024-10-30") + }) + + it("should keep the time of dates and read times of day and durations", async () => { + const workbook = new ExcelJS.Workbook() + const worksheet = workbook.addWorksheet("Sheet1") + + // ExcelJS reads a time of day or a duration as a Date counted from 1899-12-30 + const cells: [string, string, string][] = [ + ["2024-09-30T14:04:59.900Z", "yyyy-mm-dd h:mm", "2024-09-30 14:05:00"], + ["2024-09-30T09:30:00.000Z", "DD.MM.YYYY HH:MM", "2024-09-30 09:30:00"], + ["2024-05-01T00:00:00.000Z", "mmm yyyy", "2024-05-01"], + ["2024-09-30T00:00:00.000Z", "d-mmm", "2024-09-30"], + ["1899-12-30T06:00:00.000Z", "h:mm", "06:00:00"], + ["1899-12-30T12:00:00.000Z", "[$-x-systime]h:mm:ss AM/PM", "12:00:00"], + ["1899-12-30T18:00:00.000Z", 'h:mm" daily"', "18:00:00"], + ["1899-12-31T12:00:00.000Z", "[h]:mm:ss", "36:00:00"], + ["1899-12-31T01:00:00.000Z", "[mm]:ss", "25:00:00"], + ] + cells.forEach(([date, numFmt], index) => { + const cell = worksheet.getRow(1).getCell(index + 1) + cell.value = new Date(date) + cell.numFmt = numFmt + }) + + const result = await extractTextFromXLSX(workbook) + + expect(result).toBe(`--- Sheet: Sheet1 ---\n${cells.map(([, , text]) => text).join("\t")}`) + }) + + it("should count durations from 1904-01-01 in a 1904 workbook", async () => { + const workbook = new ExcelJS.Workbook() + workbook.properties.date1904 = true + const worksheet = workbook.addWorksheet("Sheet1") + + worksheet.getCell("A1").value = new Date("1904-01-02T12:00:00.000Z") + worksheet.getCell("A1").numFmt = "[h]:mm" + + const result = await extractTextFromXLSX(workbook) + + expect(result).toBe("--- Sheet: Sheet1 ---\n36:00:00") + }) }) describe("edge cases", () => { diff --git a/src/integrations/misc/extract-text-from-xlsx.ts b/src/integrations/misc/extract-text-from-xlsx.ts index 82e1d4db9a..4b6aa2ede3 100644 --- a/src/integrations/misc/extract-text-from-xlsx.ts +++ b/src/integrations/misc/extract-text-from-xlsx.ts @@ -2,12 +2,44 @@ import ExcelJS from "exceljs" const ROW_LIMIT = 50000 +/** + * Excel stores a date or a time as a day count shown through the cell's number format, and + * ExcelJS hands it over as a UTC Date. A format with no day or year outside quoted text and + * [...] sections is a time of day or, with [h], [m] or [s], an elapsed duration. Those count + * from day 0 (1899-12-30, or 1904-01-01 in a 1904 workbook), so they must not read as a date. + */ +function formatDate(date: Date, cell: ExcelJS.Cell): string { + // Round to the second, as Excel shows it: a NOW() stamp of 14:04:59.9 reads 14:05:00 + const time = Math.round(date.getTime() / 1000) * 1000 + const iso = new Date(time).toISOString() + const numFmt = cell.numFmt ?? "" + if (!numFmt || /[dy]/i.test(numFmt.replace(/"[^"]*"|\[[^\]]*\]/g, ""))) { + return iso.endsWith("T00:00:00.000Z") ? iso.slice(0, 10) : iso.slice(0, 19).replace("T", " ") + } + if (!/\[[hms]+\]/i.test(numFmt)) { + return iso.slice(11, 19) + } + const epoch = cell.worksheet.workbook.properties.date1904 ? Date.UTC(1904, 0, 1) : Date.UTC(1899, 11, 30) + const seconds = (time - epoch) / 1000 + const pad = (n: number) => String(n).padStart(2, "0") + return `${Math.floor(seconds / 3600)}:${pad(Math.floor(seconds / 60) % 60)}:${pad(seconds % 60)}` +} + function formatCellValue(cell: ExcelJS.Cell): string { - const value = cell.value + let value = cell.value if (value === null || value === undefined) { return "" } + // Handle formulas - read the calculated result like a plain value. The cells of a formula + // filled down or across hold { sharedFormula, result }; cell.formula gives their formula. + if (typeof value === "object" && ("formula" in value || "sharedFormula" in value)) { + if (value.result === undefined || value.result === null) { + return `[Formula: ${cell.formula}]` + } + value = value.result + } + // Handle error values (#DIV/0!, #N/A, etc.) if (typeof value === "object" && "error" in value) { return `[Error: ${value.error}]` @@ -15,7 +47,7 @@ function formatCellValue(cell: ExcelJS.Cell): string { // Handle dates - ExcelJS can parse them as Date objects if (value instanceof Date) { - return value.toISOString().split("T")[0] + return formatDate(value, cell) } // Handle rich text @@ -28,15 +60,6 @@ function formatCellValue(cell: ExcelJS.Cell): string { return `${value.text} (${value.hyperlink})` } - // Handle formulas - get the calculated result - if (typeof value === "object" && "formula" in value) { - if ("result" in value && value.result !== undefined && value.result !== null) { - return value.result.toString() - } else { - return `[Formula: ${value.formula}]` - } - } - return value.toString() } From 9ec4fa17a48dbeed9dbefd4d40e640a3293e0a23 Mon Sep 17 00:00:00 2001 From: L4XB Date: Mon, 5 Oct 2026 12:17:03 +0200 Subject: [PATCH 2/4] fix(xlsx): read month-only formats as dates, elapsed time in its unit, and falsy formula results A format with a month but no hour or second beside it (mmm, mmmm) shows a date, so it no longer reads as 00:00:00. Escaped characters are skipped like quoted text, so h:mm\ \d\a\i\l\y stays a time. The elapsed check now runs before the date rule, which would otherwise read [h]:mm as a month. Elapsed time counts in the unit of its bracket: [mm]:ss of 25 hours and 30 seconds reads 1500:30, and [ss] reads total seconds. A formula's cached result is read from cell.result. ExcelJS leaves a cached 0 or false out of cell.value, so such a cell read as its formula. --- .../__tests__/extract-text-from-xlsx.test.ts | 19 +++++++++++++- .../misc/extract-text-from-xlsx.ts | 26 ++++++++++++++----- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/src/integrations/misc/__tests__/extract-text-from-xlsx.test.ts b/src/integrations/misc/__tests__/extract-text-from-xlsx.test.ts index 5e577b3373..7c2bb31837 100644 --- a/src/integrations/misc/__tests__/extract-text-from-xlsx.test.ts +++ b/src/integrations/misc/__tests__/extract-text-from-xlsx.test.ts @@ -188,6 +188,20 @@ describe("extractTextFromXLSX", () => { expect(result).toBe("--- Sheet: Sheet1 ---\n2\n4\n[Formula: B3*2]") }) + it("should read a cached 0 or false formula result, not the formula", async () => { + const workbook = new ExcelJS.Workbook() + const worksheet = workbook.addWorksheet("Sheet1") + + // ExcelJS keeps a falsy cached result in cell.result but leaves it out of cell.value + worksheet.getCell("A1").value = { formula: "B1*2", result: 0 } + worksheet.getCell("A2").value = { sharedFormula: "A1", result: 0 } + worksheet.getCell("A3").value = { formula: "B1>1", result: false } + + const result = await extractTextFromXLSX(workbook) + + expect(result).toBe("--- Sheet: Sheet1 ---\n0\n0\nfalse") + }) + it("should read formula errors and dates like plain values", async () => { const workbook = new ExcelJS.Workbook() const worksheet = workbook.addWorksheet("Sheet1") @@ -215,7 +229,10 @@ describe("extractTextFromXLSX", () => { ["1899-12-30T12:00:00.000Z", "[$-x-systime]h:mm:ss AM/PM", "12:00:00"], ["1899-12-30T18:00:00.000Z", 'h:mm" daily"', "18:00:00"], ["1899-12-31T12:00:00.000Z", "[h]:mm:ss", "36:00:00"], - ["1899-12-31T01:00:00.000Z", "[mm]:ss", "25:00:00"], + ["1899-12-31T01:00:30.000Z", "[mm]:ss", "1500:30"], + ["1899-12-30T01:30:00.000Z", "[ss]", "5400"], + ["2024-09-30T00:00:00.000Z", "mmmm", "2024-09-30"], + ["1899-12-30T18:00:00.000Z", "h:mm\\ \\d\\a\\i\\l\\y", "18:00:00"], ] cells.forEach(([date, numFmt], index) => { const cell = worksheet.getRow(1).getCell(index + 1) diff --git a/src/integrations/misc/extract-text-from-xlsx.ts b/src/integrations/misc/extract-text-from-xlsx.ts index 4b6aa2ede3..21ad9e9318 100644 --- a/src/integrations/misc/extract-text-from-xlsx.ts +++ b/src/integrations/misc/extract-text-from-xlsx.ts @@ -4,8 +4,9 @@ const ROW_LIMIT = 50000 /** * Excel stores a date or a time as a day count shown through the cell's number format, and - * ExcelJS hands it over as a UTC Date. A format with no day or year outside quoted text and - * [...] sections is a time of day or, with [h], [m] or [s], an elapsed duration. Those count + * ExcelJS hands it over as a UTC Date. Outside quoted text, escaped characters and [...] sections, + * a format with a day, a year, or a month with no hour or second beside it (mmm) shows a date. + * Any other format is a time of day or, with [h], [m] or [s], an elapsed duration. Those count * from day 0 (1899-12-30, or 1904-01-01 in a 1904 workbook), so they must not read as a date. */ function formatDate(date: Date, cell: ExcelJS.Cell): string { @@ -13,15 +14,26 @@ function formatDate(date: Date, cell: ExcelJS.Cell): string { const time = Math.round(date.getTime() / 1000) * 1000 const iso = new Date(time).toISOString() const numFmt = cell.numFmt ?? "" - if (!numFmt || /[dy]/i.test(numFmt.replace(/"[^"]*"|\[[^\]]*\]/g, ""))) { + const elapsed = /\[(h+|m+|s+)\]/i.exec(numFmt) + const tokens = numFmt.replace(/"[^"]*"|\\.|\[[^\]]*\]/g, "") + const isDate = !numFmt || /[dy]/i.test(tokens) || (/m/i.test(tokens) && !/[hs]/i.test(tokens)) + if (!elapsed && isDate) { return iso.endsWith("T00:00:00.000Z") ? iso.slice(0, 10) : iso.slice(0, 19).replace("T", " ") } - if (!/\[[hms]+\]/i.test(numFmt)) { + if (!elapsed) { return iso.slice(11, 19) } const epoch = cell.worksheet.workbook.properties.date1904 ? Date.UTC(1904, 0, 1) : Date.UTC(1899, 11, 30) const seconds = (time - epoch) / 1000 const pad = (n: number) => String(n).padStart(2, "0") + // Elapsed time counts in the unit of its bracket: [mm]:ss of 25 hours reads 1500:00 + const unit = elapsed[1][0].toLowerCase() + if (unit === "s") { + return String(seconds) + } + if (unit === "m") { + return `${Math.floor(seconds / 60)}:${pad(seconds % 60)}` + } return `${Math.floor(seconds / 3600)}:${pad(Math.floor(seconds / 60) % 60)}:${pad(seconds % 60)}` } @@ -33,11 +45,13 @@ function formatCellValue(cell: ExcelJS.Cell): string { // Handle formulas - read the calculated result like a plain value. The cells of a formula // filled down or across hold { sharedFormula, result }; cell.formula gives their formula. + // The result comes from cell.result: ExcelJS leaves a cached 0 or false out of cell.value. if (typeof value === "object" && ("formula" in value || "sharedFormula" in value)) { - if (value.result === undefined || value.result === null) { + const result = cell.result + if (result === undefined || result === null) { return `[Formula: ${cell.formula}]` } - value = value.result + value = result } // Handle error values (#DIV/0!, #N/A, etc.) From 3799c565d261ec7f513c091ee470c75453b1ca5d Mon Sep 17 00:00:00 2001 From: L4XB Date: Sat, 17 Oct 2026 11:32:25 +0200 Subject: [PATCH 3/4] fix(xlsx): read a merged formula range through its top-left cell The other cells of a merged range return the top-left cell's value object, but cell.result and cell.formula are undefined on them, so a merged formula printed [Formula: undefined] in every cell but the first. Read the range through cell.master, as main did through value.result. Elapsed durations now show seconds only when the format has them: [h]:mm of 36 hours reads 36:00, as Excel shows it. --- .../__tests__/extract-text-from-xlsx.test.ts | 22 ++++++++++++++++++- .../misc/extract-text-from-xlsx.ts | 13 +++++++---- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/integrations/misc/__tests__/extract-text-from-xlsx.test.ts b/src/integrations/misc/__tests__/extract-text-from-xlsx.test.ts index 7c2bb31837..e18ed8494c 100644 --- a/src/integrations/misc/__tests__/extract-text-from-xlsx.test.ts +++ b/src/integrations/misc/__tests__/extract-text-from-xlsx.test.ts @@ -202,6 +202,24 @@ describe("extractTextFromXLSX", () => { expect(result).toBe("--- Sheet: Sheet1 ---\n0\n0\nfalse") }) + it("should read every cell of a merged formula through the top-left cell", async () => { + const workbook = new ExcelJS.Workbook() + const worksheet = workbook.addWorksheet("Sheet1") + + // The other cells of a merged range return the top-left cell's value, but not its result + worksheet.getCell("A1").value = { formula: "C1*2", result: 4 } + worksheet.mergeCells("A1:B1") + worksheet.getCell("A2").value = { formula: "C2*2", result: 0 } + worksheet.mergeCells("A2:B2") + worksheet.getCell("A3").value = { formula: "C3+30", result: new Date("2024-10-30T00:00:00.000Z") } + worksheet.getCell("A3").numFmt = "yyyy-mm-dd" + worksheet.mergeCells("A3:B3") + + const result = await extractTextFromXLSX(workbook) + + expect(result).toBe("--- Sheet: Sheet1 ---\n4\t4\n0\t0\n2024-10-30\t2024-10-30") + }) + it("should read formula errors and dates like plain values", async () => { const workbook = new ExcelJS.Workbook() const worksheet = workbook.addWorksheet("Sheet1") @@ -229,6 +247,8 @@ describe("extractTextFromXLSX", () => { ["1899-12-30T12:00:00.000Z", "[$-x-systime]h:mm:ss AM/PM", "12:00:00"], ["1899-12-30T18:00:00.000Z", 'h:mm" daily"', "18:00:00"], ["1899-12-31T12:00:00.000Z", "[h]:mm:ss", "36:00:00"], + ["1899-12-31T12:00:30.000Z", "[h]:mm", "36:00"], + ["1899-12-31T01:00:30.000Z", "[m]", "1500"], ["1899-12-31T01:00:30.000Z", "[mm]:ss", "1500:30"], ["1899-12-30T01:30:00.000Z", "[ss]", "5400"], ["2024-09-30T00:00:00.000Z", "mmmm", "2024-09-30"], @@ -255,7 +275,7 @@ describe("extractTextFromXLSX", () => { const result = await extractTextFromXLSX(workbook) - expect(result).toBe("--- Sheet: Sheet1 ---\n36:00:00") + expect(result).toBe("--- Sheet: Sheet1 ---\n36:00") }) }) diff --git a/src/integrations/misc/extract-text-from-xlsx.ts b/src/integrations/misc/extract-text-from-xlsx.ts index 21ad9e9318..27a8539fa5 100644 --- a/src/integrations/misc/extract-text-from-xlsx.ts +++ b/src/integrations/misc/extract-text-from-xlsx.ts @@ -26,18 +26,23 @@ function formatDate(date: Date, cell: ExcelJS.Cell): string { const epoch = cell.worksheet.workbook.properties.date1904 ? Date.UTC(1904, 0, 1) : Date.UTC(1899, 11, 30) const seconds = (time - epoch) / 1000 const pad = (n: number) => String(n).padStart(2, "0") - // Elapsed time counts in the unit of its bracket: [mm]:ss of 25 hours reads 1500:00 + // Elapsed time counts in the unit of its bracket: [mm]:ss of 25 hours reads 1500:00. Seconds + // show only when the format has them: [h]:mm of 36 hours reads 36:00 const unit = elapsed[1][0].toLowerCase() + const secondsShown = /s/i.test(tokens) ? `:${pad(seconds % 60)}` : "" if (unit === "s") { return String(seconds) } if (unit === "m") { - return `${Math.floor(seconds / 60)}:${pad(seconds % 60)}` + return `${Math.floor(seconds / 60)}${secondsShown}` } - return `${Math.floor(seconds / 3600)}:${pad(Math.floor(seconds / 60) % 60)}:${pad(seconds % 60)}` + return `${Math.floor(seconds / 3600)}:${pad(Math.floor(seconds / 60) % 60)}${secondsShown}` } -function formatCellValue(cell: ExcelJS.Cell): string { +function formatCellValue(mergedOrCell: ExcelJS.Cell): string { + // The other cells of a merged range return the top-left cell's value but not its formula + // result, so the whole range is read through that cell + const cell = mergedOrCell.type === ExcelJS.ValueType.Merge ? mergedOrCell.master : mergedOrCell let value = cell.value if (value === null || value === undefined) { return "" From 94dcdee7b668528cdc831d015b45209f2cd73554 Mon Sep 17 00:00:00 2001 From: L4XB Date: Sat, 17 Oct 2026 16:58:39 +0200 Subject: [PATCH 4/4] test(xlsx): read the workbooks of the new tests through formatWorkbook Main made extractTextFromXLSX take only a Buffer (#2002) and moved the workbook tests to formatWorkbook. The tests this branch adds still passed a Workbook to extractTextFromXLSX, which no longer type-checks. --- .../__tests__/extract-text-from-xlsx.test.ts | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/integrations/misc/__tests__/extract-text-from-xlsx.test.ts b/src/integrations/misc/__tests__/extract-text-from-xlsx.test.ts index dc5acd3b9a..ed0a31c528 100644 --- a/src/integrations/misc/__tests__/extract-text-from-xlsx.test.ts +++ b/src/integrations/misc/__tests__/extract-text-from-xlsx.test.ts @@ -215,7 +215,7 @@ describe("formatWorkbook", () => { expect(result).toContain("[Formula: SUM(B1:B10)]") // Formula without result }) - it("should read every cell of a filled-down formula", async () => { + it("should read every cell of a filled-down formula", () => { const workbook = new ExcelJS.Workbook() const worksheet = workbook.addWorksheet("Sheet1") @@ -224,12 +224,12 @@ describe("formatWorkbook", () => { worksheet.getCell("A2").value = { sharedFormula: "A1", result: 4 } worksheet.getCell("A3").value = { sharedFormula: "A1" } - const result = await extractTextFromXLSX(workbook) + const result = formatWorkbook(workbook) expect(result).toBe("--- Sheet: Sheet1 ---\n2\n4\n[Formula: B3*2]") }) - it("should read a cached 0 or false formula result, not the formula", async () => { + it("should read a cached 0 or false formula result, not the formula", () => { const workbook = new ExcelJS.Workbook() const worksheet = workbook.addWorksheet("Sheet1") @@ -238,12 +238,12 @@ describe("formatWorkbook", () => { worksheet.getCell("A2").value = { sharedFormula: "A1", result: 0 } worksheet.getCell("A3").value = { formula: "B1>1", result: false } - const result = await extractTextFromXLSX(workbook) + const result = formatWorkbook(workbook) expect(result).toBe("--- Sheet: Sheet1 ---\n0\n0\nfalse") }) - it("should read every cell of a merged formula through the top-left cell", async () => { + it("should read every cell of a merged formula through the top-left cell", () => { const workbook = new ExcelJS.Workbook() const worksheet = workbook.addWorksheet("Sheet1") @@ -256,12 +256,12 @@ describe("formatWorkbook", () => { worksheet.getCell("A3").numFmt = "yyyy-mm-dd" worksheet.mergeCells("A3:B3") - const result = await extractTextFromXLSX(workbook) + const result = formatWorkbook(workbook) expect(result).toBe("--- Sheet: Sheet1 ---\n4\t4\n0\t0\n2024-10-30\t2024-10-30") }) - it("should read formula errors and dates like plain values", async () => { + it("should read formula errors and dates like plain values", () => { const workbook = new ExcelJS.Workbook() const worksheet = workbook.addWorksheet("Sheet1") @@ -269,12 +269,12 @@ describe("formatWorkbook", () => { worksheet.getCell("B1").value = { formula: "A2+30", result: new Date("2024-10-30T00:00:00.000Z") } worksheet.getCell("B1").numFmt = "yyyy-mm-dd" - const result = await extractTextFromXLSX(workbook) + const result = formatWorkbook(workbook) expect(result).toBe("--- Sheet: Sheet1 ---\n[Error: #DIV/0!]\t2024-10-30") }) - it("should keep the time of dates and read times of day and durations", async () => { + it("should keep the time of dates and read times of day and durations", () => { const workbook = new ExcelJS.Workbook() const worksheet = workbook.addWorksheet("Sheet1") @@ -301,12 +301,12 @@ describe("formatWorkbook", () => { cell.numFmt = numFmt }) - const result = await extractTextFromXLSX(workbook) + const result = formatWorkbook(workbook) expect(result).toBe(`--- Sheet: Sheet1 ---\n${cells.map(([, , text]) => text).join("\t")}`) }) - it("should count durations from 1904-01-01 in a 1904 workbook", async () => { + it("should count durations from 1904-01-01 in a 1904 workbook", () => { const workbook = new ExcelJS.Workbook() workbook.properties.date1904 = true const worksheet = workbook.addWorksheet("Sheet1") @@ -314,7 +314,7 @@ describe("formatWorkbook", () => { worksheet.getCell("A1").value = new Date("1904-01-02T12:00:00.000Z") worksheet.getCell("A1").numFmt = "[h]:mm" - const result = await extractTextFromXLSX(workbook) + const result = formatWorkbook(workbook) expect(result).toBe("--- Sheet: Sheet1 ---\n36:00") })