Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ jobs:
- run: node --import=@litejs/cli/test.js test/load.mjs
- run: npx lj lint
- run: npx tsc -p test/tsconfig.json --noEmit
- run: npm run validate-snaps
- uses: coverallsapp/github-action@v2
name: Upload to coveralls.io
with:
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"repository": "github:litejs/xlsx",
"scripts": {
"lint": "npx jshint -c .github/jshint.json *.js",
"test": "lj t --tz=UTC test/index.js"
"test": "lj t --tz=UTC test/index.js",
"validate-snaps": "node test/validate-snaps.js"
},
"devDependencies": {
"@litejs/cli": "26.4.0"
Expand Down
30 changes: 30 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,34 @@ describe("xlsx", function() {
assert.ok(sheet.indexOf('ref="A1:B2"') > -1, 'dimension uses column count from row data')
assert.end()
})
test("dimension spans the widest row", function(assert) {
var sheet = createFiles({
sheets: [{
data: [
['Report', 'Q1'],
['Year', 2026, 'Status', 'Draft'],
[],
['A','B','C','D','E','F','G','H','I','J','K','L','M','N'],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14],
]
}]
}).find(function(f) { return f.name === 'xl/worksheets/sheet1.xml' }).content
assert.ok(sheet.indexOf('ref="A1:N5"') > -1, 'dimension covers all used columns')
assert.end()
})
test("styles.xml is Excel-compatible", function(assert) {
var styles = createFiles({
styles: {
Bold: { font: { b: true } },
Header: { font: { b: true }, fill: 'E8E8E8' },
},
sheets: [{ data: [[{ style: 'Bold', value: 'Title' }]] }]
}).find(function(f) { return f.name === 'xl/styles.xml' }).content
assert.ok(styles.indexOf('<cellStyleXfs') > -1, 'cellStyleXfs present')
assert.ok(styles.indexOf('<cellStyles') > -1, 'cellStyles present')
assert.ok(styles.indexOf('<b val="1"/>') > -1, 'bold fonts use val attribute')
assert.ok(styles.indexOf('<font><b/></font>') === -1, 'fonts include defaults')
assert.ok(styles.indexOf('rgb="FFE8E8E8"') > -1, 'fill colors use ARGB')
assert.end()
})
})
4 changes: 2 additions & 2 deletions test/snap/readme.snap.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
},
{
"name": "xl/styles.xml",
"content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><styleSheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"><numFmts count=\"2\"><numFmt numFmtId=\"164\" formatCode=\"yyyy-mm-dd\"/><numFmt numFmtId=\"165\" formatCode=\"yyyy-mm-dd hh:mm:ss\"/></numFmts><fonts count=\"2\"><font><sz val=\"11\"/><name val=\"Calibri\"/></font><font><sz val=\"11\"/><name val=\"Calibri\"/><b/></font></fonts><fills count=\"2\"><fill><patternFill patternType=\"none\"/></fill><fill><patternFill patternType=\"gray125\"/></fill></fills><borders count=\"1\"><border/></borders><cellXfs count=\"4\"><xf fontId=\"0\" applyFont=\"1\"/><xf numFmtId=\"164\" applyNumberFormat=\"1\"/><xf numFmtId=\"165\" applyNumberFormat=\"1\"/><xf numFmtId=\"0\" fontId=\"1\" applyFont=\"1\"/></cellXfs></styleSheet>"
"content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><styleSheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"><numFmts count=\"2\"><numFmt numFmtId=\"164\" formatCode=\"yyyy-mm-dd\"/><numFmt numFmtId=\"165\" formatCode=\"yyyy-mm-dd hh:mm:ss\"/></numFmts><fonts count=\"2\"><font><sz val=\"11\"/><name val=\"Calibri\"/></font><font><b val=\"1\"/><sz val=\"11\"/><name val=\"Calibri\"/></font></fonts><fills count=\"2\"><fill><patternFill patternType=\"none\"/></fill><fill><patternFill patternType=\"gray125\"/></fill></fills><borders count=\"1\"><border/></borders><cellStyleXfs count=\"1\"><xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\"/></cellStyleXfs><cellXfs count=\"4\"><xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\" xfId=\"0\" applyFont=\"1\"/><xf numFmtId=\"164\" fontId=\"0\" fillId=\"0\" borderId=\"0\" xfId=\"0\" applyNumberFormat=\"1\"/><xf numFmtId=\"165\" fontId=\"0\" fillId=\"0\" borderId=\"0\" xfId=\"0\" applyNumberFormat=\"1\"/><xf numFmtId=\"0\" fontId=\"1\" fillId=\"0\" borderId=\"0\" xfId=\"0\" applyFont=\"1\"/></cellXfs><cellStyles count=\"1\"><cellStyle name=\"Normal\" xfId=\"0\" builtinId=\"0\"/></cellStyles></styleSheet>"
},
{
"name": "xl/workbook.xml",
Expand All @@ -33,6 +33,6 @@
},
{
"name": "xl/worksheets/sheet4.xml",
"content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"><sheetViews><sheetView workbookViewId=\"0\"><pane ySplit=\"1\" topLeftCell=\"A2\" activePane=\"bottomLeft\" state=\"frozen\"/><selection pane=\"bottomLeft\"/></sheetView></sheetViews><dimension ref=\"A1:B10\"/><cols><col min=\"1\" max=\"1\" width=\"20\" customWidth=\"1\"/><col min=\"2\" max=\"2\" width=\"40\" customWidth=\"1\"/></cols><sheetData><row r=\"1\"><c r=\"A1\" t=\"inlineStr\"><is><t>null</t></is></c></row><row r=\"2\"><c r=\"A2\" t=\"inlineStr\"><is><t>true</t></is></c><c r=\"B2\" t=\"b\"><v>1</v></c></row><row r=\"3\"><c r=\"A3\" t=\"inlineStr\"><is><t>false</t></is></c><c r=\"B3\" t=\"b\"><v>0</v></c></row><row r=\"4\"><c r=\"A4\" t=\"inlineStr\"><is><t>Empty string</t></is></c><c r=\"B4\"></c></row><row r=\"5\"><c r=\"A5\" t=\"inlineStr\"><is><t>Empty object</t></is></c><c r=\"B5\"></c></row><row r=\"6\"><c r=\"A6\" t=\"inlineStr\"><is><t>Object as value</t></is></c><c r=\"B6\"></c></row><row r=\"7\"><c r=\"A7\" t=\"inlineStr\"><is><t>Empty array</t></is></c><c r=\"B7\"></c></row><row r=\"8\"><c r=\"A8\" t=\"inlineStr\"><is><t>Default Date</t></is></c><c r=\"B8\" s=\"2\"><v>43102.573495</v></c></row><row r=\"9\"><c r=\"A9\" t=\"inlineStr\"><is><t>Datetime</t></is></c><c r=\"B9\" s=\"2\"><v>43102.573495</v></c></row><row r=\"10\"><c r=\"A10\" t=\"inlineStr\"><is><t>Date</t></is></c><c r=\"B10\" s=\"1\"><v>43102.573495</v></c></row></sheetData></worksheet>"
"content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"><dimension ref=\"A1:B10\"/><sheetViews><sheetView workbookViewId=\"0\"><pane ySplit=\"1\" topLeftCell=\"A2\" activePane=\"bottomLeft\" state=\"frozen\"/><selection pane=\"bottomLeft\"/></sheetView></sheetViews><cols><col min=\"1\" max=\"1\" width=\"20\" customWidth=\"1\"/><col min=\"2\" max=\"2\" width=\"40\" customWidth=\"1\"/></cols><sheetData><row r=\"1\"><c r=\"A1\" t=\"inlineStr\"><is><t>null</t></is></c></row><row r=\"2\"><c r=\"A2\" t=\"inlineStr\"><is><t>true</t></is></c><c r=\"B2\" t=\"b\"><v>1</v></c></row><row r=\"3\"><c r=\"A3\" t=\"inlineStr\"><is><t>false</t></is></c><c r=\"B3\" t=\"b\"><v>0</v></c></row><row r=\"4\"><c r=\"A4\" t=\"inlineStr\"><is><t>Empty string</t></is></c><c r=\"B4\"></c></row><row r=\"5\"><c r=\"A5\" t=\"inlineStr\"><is><t>Empty object</t></is></c><c r=\"B5\"></c></row><row r=\"6\"><c r=\"A6\" t=\"inlineStr\"><is><t>Object as value</t></is></c><c r=\"B6\"></c></row><row r=\"7\"><c r=\"A7\" t=\"inlineStr\"><is><t>Empty array</t></is></c><c r=\"B7\"></c></row><row r=\"8\"><c r=\"A8\" t=\"inlineStr\"><is><t>Default Date</t></is></c><c r=\"B8\" s=\"2\"><v>43102.573495</v></c></row><row r=\"9\"><c r=\"A9\" t=\"inlineStr\"><is><t>Datetime</t></is></c><c r=\"B9\" s=\"2\"><v>43102.573495</v></c></row><row r=\"10\"><c r=\"A10\" t=\"inlineStr\"><is><t>Date</t></is></c><c r=\"B10\" s=\"1\"><v>43102.573495</v></c></row></sheetData></worksheet>"
}
]
Binary file modified test/snap/readme.snap.xlsx
Binary file not shown.
4 changes: 2 additions & 2 deletions test/snap/styles.snap.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@
},
{
"name": "xl/styles.xml",
"content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><styleSheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"><numFmts count=\"2\"><numFmt numFmtId=\"164\" formatCode=\"yyyy-mm-dd\"/><numFmt numFmtId=\"165\" formatCode=\"yyyy-mm-dd hh:mm:ss\"/></numFmts><fonts count=\"3\"><font><sz val=\"11\"/><name val=\"Calibri\"/></font><font><sz val=\"11\"/><name val=\"Calibri\"/><b/></font><font><sz val=\"15\"/><name val=\"Calibri\"/></font></fonts><fills count=\"4\"><fill><patternFill patternType=\"none\"/></fill><fill><patternFill patternType=\"gray125\"/></fill><fill><patternFill patternType=\"solid\"><fgColor rgb=\"FFFF00\"/></patternFill></fill><fill><patternFill patternType=\"solid\"><bgColor rgb=\"FF9900\"/></patternFill></fill></fills><borders count=\"3\"><border/><border><left style=\"thin\"/><right style=\"thin\"/><top style=\"thin\"/><bottom style=\"thin\"/></border><border><top style=\"double\"/></border></borders><cellXfs count=\"10\"><xf fontId=\"0\" applyFont=\"1\"/><xf numFmtId=\"164\" applyNumberFormat=\"1\"/><xf numFmtId=\"165\" applyNumberFormat=\"1\"/><xf numFmtId=\"0\" fontId=\"1\" applyFont=\"1\"/><xf fontId=\"2\"/><xf fontId=\"0\"/><xf fontId=\"0\" borderId=\"1\" applyBorder=\"1\"/><xf fontId=\"0\" borderId=\"2\" applyBorder=\"1\"/><xf fontId=\"0\" fillId=\"2\" applyFill=\"1\"/><xf fontId=\"0\" fillId=\"3\" applyFill=\"1\"/></cellXfs></styleSheet>"
"content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><styleSheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"><numFmts count=\"2\"><numFmt numFmtId=\"164\" formatCode=\"yyyy-mm-dd\"/><numFmt numFmtId=\"165\" formatCode=\"yyyy-mm-dd hh:mm:ss\"/></numFmts><fonts count=\"3\"><font><sz val=\"11\"/><name val=\"Calibri\"/></font><font><b val=\"1\"/><sz val=\"11\"/><name val=\"Calibri\"/></font><font><sz val=\"15\"/><name val=\"Calibri\"/></font></fonts><fills count=\"4\"><fill><patternFill patternType=\"none\"/></fill><fill><patternFill patternType=\"gray125\"/></fill><fill><patternFill patternType=\"solid\"><fgColor rgb=\"FFFFFF00\"/></patternFill></fill><fill><patternFill patternType=\"solid\"><bgColor rgb=\"FFFF9900\"/></patternFill></fill></fills><borders count=\"3\"><border/><border><left style=\"thin\"/><right style=\"thin\"/><top style=\"thin\"/><bottom style=\"thin\"/></border><border><top style=\"double\"/></border></borders><cellStyleXfs count=\"1\"><xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\"/></cellStyleXfs><cellXfs count=\"10\"><xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\" xfId=\"0\" applyFont=\"1\"/><xf numFmtId=\"164\" fontId=\"0\" fillId=\"0\" borderId=\"0\" xfId=\"0\" applyNumberFormat=\"1\"/><xf numFmtId=\"165\" fontId=\"0\" fillId=\"0\" borderId=\"0\" xfId=\"0\" applyNumberFormat=\"1\"/><xf numFmtId=\"0\" fontId=\"1\" fillId=\"0\" borderId=\"0\" xfId=\"0\" applyFont=\"1\"/><xf numFmtId=\"0\" fontId=\"2\" borderId=\"0\" fillId=\"0\" xfId=\"0\" applyFont=\"1\"/><xf numFmtId=\"0\" fontId=\"0\" borderId=\"0\" fillId=\"0\" xfId=\"0\"/><xf numFmtId=\"0\" fontId=\"0\" borderId=\"1\" fillId=\"0\" xfId=\"0\" applyBorder=\"1\"/><xf numFmtId=\"0\" fontId=\"0\" borderId=\"2\" fillId=\"0\" xfId=\"0\" applyBorder=\"1\"/><xf numFmtId=\"0\" fontId=\"0\" borderId=\"0\" fillId=\"2\" xfId=\"0\" applyFill=\"1\"/><xf numFmtId=\"0\" fontId=\"0\" borderId=\"0\" fillId=\"3\" xfId=\"0\" applyFill=\"1\"/></cellXfs><cellStyles count=\"1\"><cellStyle name=\"Normal\" xfId=\"0\" builtinId=\"0\"/></cellStyles></styleSheet>"
},
{
"name": "xl/workbook.xml",
"content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><workbook xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\"><sheets><sheet name=\"Styles\" sheetId=\"1\" r:id=\"rId1\"/></sheets></workbook>"
},
{
"name": "xl/worksheets/sheet1.xml",
"content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"><sheetViews><sheetView workbookViewId=\"0\"><pane xSplit=\"1\" topLeftCell=\"B1\" activePane=\"topRight\" state=\"frozen\"/><selection pane=\"topRight\"/></sheetView></sheetViews><dimension ref=\"A1:B4\"/><cols><col min=\"1\" max=\"1\"/></cols><sheetData><row r=\"1\"><c r=\"A1\" s=\"4\" t=\"inlineStr\"><is><t>Apple My1</t></is></c><c r=\"B1\" s=\"5\" t=\"inlineStr\"><is><t>Banana Plain</t></is></c></row><row r=\"2\" hidden=\"1\"><c r=\"A2\" t=\"inlineStr\"><is><t>Hidden Row</t></is></c><c r=\"B2\"><v>1</v></c></row><row r=\"3\" ht=\"25\" customHeight=\"1\"><c r=\"A3\" t=\"inlineStr\"><is><t>Sized Row</t></is></c><c r=\"B3\" s=\"6\"><v>1</v></c></row><row r=\"4\"><c r=\"A4\" s=\"8\" t=\"inlineStr\"><is><t>Filled</t></is></c><c r=\"B4\" s=\"9\"><v>2</v></c></row></sheetData></worksheet>"
"content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"><dimension ref=\"A1:B4\"/><sheetViews><sheetView workbookViewId=\"0\"><pane xSplit=\"1\" topLeftCell=\"B1\" activePane=\"topRight\" state=\"frozen\"/><selection pane=\"topRight\"/></sheetView></sheetViews><cols><col min=\"1\" max=\"1\"/></cols><sheetData><row r=\"1\"><c r=\"A1\" s=\"4\" t=\"inlineStr\"><is><t>Apple My1</t></is></c><c r=\"B1\" s=\"5\" t=\"inlineStr\"><is><t>Banana Plain</t></is></c></row><row r=\"2\" hidden=\"1\"><c r=\"A2\" t=\"inlineStr\"><is><t>Hidden Row</t></is></c><c r=\"B2\"><v>1</v></c></row><row r=\"3\" ht=\"25\" customHeight=\"1\"><c r=\"A3\" t=\"inlineStr\"><is><t>Sized Row</t></is></c><c r=\"B3\" s=\"6\"><v>1</v></c></row><row r=\"4\"><c r=\"A4\" s=\"8\" t=\"inlineStr\"><is><t>Filled</t></is></c><c r=\"B4\" s=\"9\"><v>2</v></c></row></sheetData></worksheet>"
}
]
Binary file modified test/snap/styles.snap.xlsx
Binary file not shown.
76 changes: 76 additions & 0 deletions test/validate-snaps.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
'use strict'
var cp = require('child_process')
var fs = require('fs')
var os = require('os')
var path = require('path')

var root = path.join(__dirname, '..')
var snaps = fs.readdirSync(path.join(__dirname, 'snap'))
.filter(function (f) { return f.endsWith('.snap.xlsx') })
.map(function (f) { return path.join(__dirname, 'snap', f) })
var versions = ['Microsoft365', 'Office2007']

function platformId() {
var plat = { linux: 'linux', darwin: 'macos', win32: 'windows' }[process.platform] || 'linux'
var arch = { x64: 'x64', arm64: 'arm64', ia32: 'x86' }[process.arch] || process.arch
return 'ooxml-validator-' + plat + '-' + arch
}

function findBin() {
var caches = [process.env.npm_config_cache, process.env.NPM_CONFIG_CACHE, path.join(os.homedir(), '.npm')]
if (process.env.LOCALAPPDATA) caches.push(path.join(process.env.LOCALAPPDATA, 'npm-cache'))
var id = platformId()
for (var i = 0; i < caches.length; i++) {
if (!caches[i]) continue
var npxDir = path.join(caches[i], '_npx')
if (!fs.existsSync(npxDir)) continue
var hit = walk(npxDir, id)
if (hit) return hit
}
return null
}

function walk(dir, id) {
var entries
try { entries = fs.readdirSync(dir, { withFileTypes: true }) } catch (e) { return null }
for (var i = 0; i < entries.length; i++) {
var ent = entries[i]
var p = path.join(dir, ent.name)
if (!ent.isDirectory()) continue
if (ent.name === id) {
var bin = path.join(p, 'ooxml-validator' + (process.platform === 'win32' ? '.exe' : ''))
if (fs.existsSync(bin)) return bin
}
var nested = walk(p, id)
if (nested) return nested
}
return null
}

function ensureBin() {
var bin = findBin()
if (bin) return bin
cp.execFileSync('npx', ['--yes', '@xarsh/ooxml-validator', snaps[0]], { stdio: 'pipe', cwd: root })
bin = findBin()
if (!bin) throw new Error('ooxml-validator binary not found after npx install')
return bin
}

var bin = ensureBin()
var fails = 0

snaps.forEach(function (file) {
versions.forEach(function (ver) {
var out = cp.execFileSync(bin, [file, ver], { encoding: 'utf8' })
var result = JSON.parse(out)
console.log('# validate %s (%s) %s', path.basename(file), ver, result.ok ? 'ok' : 'FAIL')
if (!result.ok) {
fails++
result.errors.forEach(function (e) {
console.error(' %s: %s', e.path, e.description)
})
}
})
})

process.exit(fails ? 1 : 0)
Loading