roundtrip 1904 date setting

This commit is contained in:
SheetJS 2022-06-06 19:05:27 -04:00
parent 83ddb4c120
commit 4cc0412154
22 changed files with 3554 additions and 552 deletions

View File

@ -20,7 +20,7 @@ jobs:
- run: sudo chmod a+x /usr/bin/rooster
- run: make init
- run: 'cd test_files; make all; cd -'
- run: deno test --allow-env --allow-read --allow-write test.ts
- run: deno test --allow-env --allow-read --allow-write --config misc/test.deno.jsonc test.ts
# full test
full:
runs-on: ubuntu-latest
@ -36,4 +36,4 @@ jobs:
- run: sudo chmod a+x /usr/bin/rooster
- run: make init
- run: 'cd test_files; make all; cd -'
- run: deno test --allow-env --allow-read --allow-write test.ts
- run: deno test --allow-env --allow-read --allow-write --config misc/test.deno.jsonc test.ts

View File

@ -141,9 +141,12 @@ test mocha: test.js ## Run test suite
test-esm: test.mjs ## Run Node ESM test suite
npx -y mocha@9 -R spec -t 30000 $<
test.ts: test.mts
node -pe 'var data = fs.readFileSync("'$<'", "utf8"); data.split("\n").map(function(l) { return l.replace(/^describe\((.*?)function\(\)/, "Deno.test($$1async function(t)").replace(/\b(?:it|describe)\((.*?)function\(\)/g, "await t.step($$1async function(t)").replace("assert.ok", "assert.assert"); }).join("\n")' > $@
.PHONY: test-deno
test-deno: test.ts ## Run Deno test suite
deno test --allow-env --allow-read --allow-write $<
deno test --allow-env --allow-read --allow-write --config misc/test.deno.jsonc $<
#* To run tests for one format, make test_<fmt>
#* To run the core test suite, make test_misc

View File

@ -12,7 +12,9 @@ port calculations to web apps; automate common spreadsheet tasks, and much more!
![License](https://img.shields.io/github/license/SheetJS/sheetjs)
[![Build Status](https://img.shields.io/github/workflow/status/sheetjs/sheetjs/Tests:%20node.js)](https://github.com/SheetJS/sheetjs/actions)
[![Snyk Vulnerabilities](https://img.shields.io/snyk/vulnerabilities/github/SheetJS/sheetjs)](https://snyk.io/test/github/SheetJS/sheetjs)
[![npm Downloads](https://img.shields.io/npm/dm/xlsx.svg)](https://npmjs.org/package/xlsx)
[![npm Downloads](https://img.shields.io/npm/dm/xlsx.svg)](https://cdn.sheetjs.com/)
[![GitHub Repo stars](https://img.shields.io/github/stars/SheetJS/sheetjs?style=social)](https://github.com/SheetJS/sheetjs)
[![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/sheetjs?pixel)](https://github.com/SheetJS/sheetjs)
[![Build Status](https://saucelabs.com/browser-matrix/sheetjs.svg)](https://saucelabs.com/u/sheetjs)
@ -25,13 +27,13 @@ port calculations to web apps; automate common spreadsheet tasks, and much more!
## Related Projects
- <https://oss.sheetjs.com/notes/> file format notes
- <https://oss.sheetjs.com/notes/>: File Format Notes
- [`ssf`](packages/ssf): Format data using ECMA-376 spreadsheet format codes
- [`xlsx-cli`](packages/xlsx-cli/): NodeJS command-line tool for processing files
- [`test_files`](https://github.com/SheetJS/test_files): test files repo
- [`test_files`](https://github.com/SheetJS/test_files): Sample spreadsheets
- [`cfb`](https://github.com/SheetJS/js-cfb): Container (OLE/ZIP) format library

View File

@ -157,8 +157,8 @@ function sheet_add_aoa(_ws/*:?Worksheet*/, data/*:AOA*/, opts/*:?any*/)/*:Worksh
else if(typeof cell.v === 'boolean') cell.t = 'b';
else if(cell.v instanceof Date) {
cell.z = o.dateNF || table_fmt[14];
if(o.cellDates) { cell.t = 'd'; cell.w = SSF_format(cell.z, datenum(cell.v)); }
else { cell.t = 'n'; cell.v = datenum(cell.v); cell.w = SSF_format(cell.z, cell.v); }
if(o.cellDates) { cell.t = 'd'; cell.w = SSF_format(cell.z, datenum(cell.v, o.date1904)); }
else { cell.t = 'n'; cell.v = datenum(cell.v, o.date1904); cell.w = SSF_format(cell.z, cell.v); }
}
else cell.t = 's';
}

View File

@ -440,7 +440,7 @@ var SYLK = /*#__PURE__*/(function() {
wb.Workbook.Names.push(nn);
} break;
case 'C': /* cell */
var C_seen_K = false, C_seen_X = false, C_seen_S = false, C_seen_E = false, _R = -1, _C = -1;
var C_seen_K = false, C_seen_X = false, C_seen_S = false, C_seen_E = false, _R = -1, _C = -1, formula = "", cell_t = "z";
for(rj=1; rj<record.length; ++rj) switch(record[rj].charAt(0)) {
case 'A': break; // TODO: comment
case 'X': C = parseInt(record[rj].slice(1), 10)-1; C_seen_X = true; break;
@ -450,26 +450,24 @@ var SYLK = /*#__PURE__*/(function() {
break;
case 'K':
val = record[rj].slice(1);
if(val.charAt(0) === '"') val = val.slice(1,val.length - 1);
else if(val === 'TRUE') val = true;
else if(val === 'FALSE') val = false;
if(val.charAt(0) === '"') { val = val.slice(1,val.length - 1); cell_t = "s"; }
else if(val === 'TRUE' || val === 'FALSE') { val = val === 'TRUE'; cell_t = "b"; }
else if(!isNaN(fuzzynum(val))) {
val = fuzzynum(val);
if(next_cell_format !== null && fmt_is_date(next_cell_format)) val = numdate(wb.Workbook.WBProps.date1904 ? val + 1462 : val);
val = fuzzynum(val); cell_t = "n";
if(next_cell_format !== null && fmt_is_date(next_cell_format) && opts.cellDates) { val = numdate(wb.Workbook.WBProps.date1904 ? val + 1462 : val); cell_t = "d"; }
} else if(!isNaN(fuzzydate(val).getDate())) {
val = parseDate(val);
val = parseDate(val); cell_t = "d";
if(!opts.cellDates) { cell_t = "n"; val = datenum(val, wb.Workbook.WBProps.date1904); }
}
if(typeof $cptable !== 'undefined' && typeof val == "string" && ((opts||{}).type != "string") && (opts||{}).codepage) val = $cptable.utils.decode(opts.codepage, val);
C_seen_K = true;
break;
case 'E':
C_seen_E = true;
var formula = rc_to_a1(record[rj].slice(1), {r:R,c:C});
arr[R][C] = [arr[R][C], formula];
formula = rc_to_a1(record[rj].slice(1), {r:R,c:C});
break;
case 'S':
C_seen_S = true;
arr[R][C] = [arr[R][C], "S5S"];
break;
case 'G': break; // unknown
case 'R': _R = parseInt(record[rj].slice(1), 10)-1; break;
@ -477,15 +475,21 @@ var SYLK = /*#__PURE__*/(function() {
default: if(opts && opts.WTF) throw new Error("SYLK bad record " + rstr);
}
if(C_seen_K) {
if(arr[R][C] && arr[R][C].length == 2) arr[R][C][0] = val;
else arr[R][C] = val;
if(!arr[R][C]) arr[R][C] = { t: cell_t, v: val };
else { arr[R][C].t = cell_t; arr[R][C].v = val; }
if(next_cell_format) arr[R][C].z = next_cell_format;
if(opts.cellText !== false && next_cell_format) arr[R][C].w = SSF_format(arr[R][C].z, arr[R][C].v, { date1904: wb.Workbook.WBProps.date1904 });
next_cell_format = null;
}
if(C_seen_S) {
if(C_seen_E) throw new Error("SYLK shared formula cannot have own formula");
var shrbase = _R > -1 && arr[_R][_C];
if(!shrbase || !shrbase[1]) throw new Error("SYLK shared formula cannot find base");
arr[R][C][1] = shift_formula_str(shrbase[1], {r: R - _R, c: C - _C});
formula = shift_formula_str(shrbase[1], {r: R - _R, c: C - _C});
}
if(formula) {
if(!arr[R][C]) arr[R][C] = { t: 'n', f: formula };
else arr[R][C].f = formula;
}
break;
case 'F': /* Format */
@ -537,7 +541,8 @@ var SYLK = /*#__PURE__*/(function() {
function sylk_to_workbook(d/*:RawData*/, opts)/*:Workbook*/ {
var aoasht = sylk_to_aoa(d, opts);
var aoa = aoasht[0], ws = aoasht[1], wb = aoasht[2];
var o = aoa_to_sheet(aoa, opts);
var _opts = dup(opts); _opts.date1904 = (((wb||{}).Workbook || {}).WBProps || {}).date1904;
var o = aoa_to_sheet(aoa, _opts);
keys(ws).forEach(function(k) { o[k] = ws[k]; });
var outwb = sheet_to_workbook(o, opts);
keys(wb).forEach(function(k) { outwb[k] = wb[k]; });
@ -581,11 +586,12 @@ var SYLK = /*#__PURE__*/(function() {
});
}
function sheet_to_sylk(ws/*:Worksheet*/, opts/*:?any*/)/*:string*/ {
function sheet_to_sylk(ws/*:Worksheet*/, opts/*:?any*/, wb/*:?WorkBook*/)/*:string*/ {
var preamble/*:Array<string>*/ = ["ID;PSheetJS;N;E"], o/*:Array<string>*/ = [];
var r = safe_decode_range(ws['!ref']), cell/*:Cell*/;
var dense = Array.isArray(ws);
var RS = "\r\n";
var d1904 = (((wb||{}).Workbook||{}).WBProps||{}).date1904;
preamble.push("P;PGeneral");
preamble.push("F;P0;DG0G8;M255");
@ -593,12 +599,13 @@ var SYLK = /*#__PURE__*/(function() {
if(ws['!rows']) write_ws_rows_sylk(preamble, ws['!rows']);
preamble.push("B;Y" + (r.e.r - r.s.r + 1) + ";X" + (r.e.c - r.s.c + 1) + ";D" + [r.s.c,r.s.r,r.e.c,r.e.r].join(" "));
preamble.push("O;L;D;B" + (d1904 ? ";V4" : "") + ";K47;G100 0.001");
for(var R = r.s.r; R <= r.e.r; ++R) {
for(var C = r.s.c; C <= r.e.c; ++C) {
var coord = encode_cell({r:R,c:C});
cell = dense ? (ws[R]||[])[C]: ws[coord];
if(!cell || (cell.v == null && (!cell.f || cell.F))) continue;
o.push(write_ws_cell_sylk(cell, ws, R, C, opts));
o.push(write_ws_cell_sylk(cell, ws, R, C, opts)); // TODO: pass date1904 info
}
}
return preamble.join(RS) + RS + o.join(RS) + RS + "E" + RS;

View File

@ -45,6 +45,7 @@ function write_BrtWbProp(data/*:?WBProps*/, o) {
var flags = 0;
if(data) {
/* TODO: mirror parse_BrtWbProp fields */
if(data.date1904) flags |= 0x01;
if(data.filterPrivacy) flags |= 0x08;
}
o.write_shift(4, flags);

View File

@ -942,9 +942,10 @@ function write_props_xlml(wb/*:Workbook*/, opts)/*:string*/ {
return o.join("");
}
/* TODO */
function write_wb_xlml(/*::wb, opts*/)/*:string*/ {
function write_wb_xlml(wb/*::, opts*/)/*:string*/ {
/* OfficeDocumentSettings */
/* ExcelWorkbook */
if((((wb||{}).Workbook||{}).WBProps||{}).date1904) return '<ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel"><Date1904/></ExcelWorkbook>';
return "";
}
/* TODO */

View File

@ -49,13 +49,14 @@ function parse_content_xml(d/*:string*/, _opts)/*:Workbook*/ {
var merges/*:Array<Range>*/ = [], mrange = {}, mR = 0, mC = 0;
var rowinfo/*:Array<RowInfo>*/ = [], rowpeat = 1, colpeat = 1;
var arrayf/*:Array<[Range, string]>*/ = [];
var WB = {Names:[]};
var WB = {Names:[], WBProps:{}};
var atag = ({}/*:any*/);
var _Ref/*:[string, string]*/ = ["", ""];
var comments/*:Array<Comment>*/ = [], comment/*:Comment*/ = ({}/*:any*/);
var creator = "", creatoridx = 0;
var isstub = false, intable = false;
var i = 0;
var baddate = 1;
xlmlregex.lastIndex = 0;
str = str.replace(/<!--([\s\S]*?)-->/mg,"").replace(/<!DOCTYPE[^\[]*\[[^\]]*\]>/gm,"");
while((Rn = xlmlregex.exec(str))) switch((Rn[3]=Rn[3].replace(/_.*$/,""))) {
@ -167,7 +168,7 @@ function parse_content_xml(d/*:string*/, _opts)/*:Workbook*/ {
case 'percentage': q.t = 'n'; q.v = parseFloat(ctag.value); break;
case 'currency': q.t = 'n'; q.v = parseFloat(ctag.value); break;
case 'date': q.t = 'd'; q.v = parseDate(ctag['date-value']);
if(!opts.cellDates) { q.t = 'n'; q.v = datenum(q.v); }
if(!opts.cellDates) { q.t = 'n'; q.v = datenum(q.v, WB.WBProps.date1904) - baddate; }
q.z = 'm/d/yy'; break;
case 'time': q.t = 'n'; q.v = parse_isodur(ctag['time-value'])/86400;
if(opts.cellDates) { q.t = 'd'; q.v = numdate(q.v); }
@ -368,7 +369,14 @@ function parse_content_xml(d/*:string*/, _opts)/*:Workbook*/ {
case 'table-header-columns': break; // 9.1.11 <table:table-header-columns>
case 'table-columns': break; // 9.1.12 <table:table-columns>
case 'null-date': break; // 9.4.2 <table:null-date> TODO: date1904
case 'null-date': // 9.4.2 <table:null-date>
tag = parsexmltag(Rn[0], false);
switch(tag["date-value"]) {
case "1904-01-01": WB.WBProps.date1904 = true;
/* falls through */
case "1900-01-01": baddate = 0;
}
break;
case 'graphic-properties': break; // 17.21 <style:graphic-properties>
case 'calculation-settings': break; // 9.4.1 <table:calculation-settings>

View File

@ -255,6 +255,7 @@ var write_content_ods/*:{(wb:any, opts:any):string}*/ = /* @__PURE__ */(function
write_automatic_styles_ods(o, wb);
o.push(' <office:body>\n');
o.push(' <office:spreadsheet>\n');
if(((wb.Workbook||{}).WBProps||{}).date1904) o.push(' <table:calculation-settings table:case-sensitive="false" table:search-criteria-must-apply-to-whole-cell="true" table:use-wildcards="true" table:use-regular-expressions="false" table:automatic-find-labels="false">\n <table:null-date table:date-value="1904-01-01"/>\n </table:calculation-settings>\n');
for(var i = 0; i != wb.SheetNames.length; ++i) o.push(write_ws(wb.Sheets[wb.SheetNames[i]], wb, i, opts));
if((wb.Workbook||{}).Names) o.push(write_names_ods(wb.Workbook.Names, wb.SheetNames, -1));
o.push(' </office:spreadsheet>\n');

View File

@ -134,7 +134,7 @@ function writeSync(wb/*:Workbook*/, opts/*:?WriteOpts*/) {
case 'xml':
case 'xlml': return write_string_type(write_xlml(wb, o), o);
case 'slk':
case 'sylk': return write_string_type(SYLK.from_sheet(wb.Sheets[wb.SheetNames[idx]], o), o);
case 'sylk': return write_string_type(SYLK.from_sheet(wb.Sheets[wb.SheetNames[idx]], o, wb), o);
case 'htm':
case 'html': return write_string_type(sheet_to_html(wb.Sheets[wb.SheetNames[idx]], o), o);
case 'txt': return write_stxt_type(sheet_to_txt(wb.Sheets[wb.SheetNames[idx]], o), o);

View File

@ -45,6 +45,7 @@ can be installed with Bash on Windows or with `cygwin`.
- [`Chrome / Chromium extensions`](chrome/)
- [`Google Sheets API`](https://docs.sheetjs.com/docs/getting-started/demos/gsheet)
- [`Adobe Apps`](https://docs.sheetjs.com/docs/getting-started/demos/extendscript)
- [`Excel JavaScript API`](https://docs.sheetjs.com/docs/getting-started/demos/excel)
- [`Headless Browsers`](headless/)
- [`canvas-datagrid`](datagrid/)
- [`x-spreadsheet`](xspreadsheet/)

5
misc/import_map.json Normal file
View File

@ -0,0 +1,5 @@
{
"imports": {
"jsdom": "https://esm.sh/jsdom"
}
}

11
misc/test.deno.jsonc Normal file
View File

@ -0,0 +1,11 @@
{
"compilerOptions": {
"lib": [
"deno.ns",
"dom",
"dom.iterable",
"dom.asynciterable"
]
},
"importMap": "./import_map.json"
}

473
test.js

File diff suppressed because it is too large Load Diff

336
test.mjs generated
View File

@ -342,7 +342,7 @@ function parsetest(x/*:string*/, wb/*:Workbook*/, full/*:boolean*/, ext/*:?strin
var wbtable = {};
(browser ? describe.skip : describe)('should parse test files', function() {
if(!browser) describe('should parse test files', function() {
files.forEach(function(x) {
if(x.slice(-8) == ".pending" || !fs.existsSync(dir + x)) return;
it(x, function() {
@ -383,13 +383,13 @@ function each_sheet(wb, f) { wb.SheetNames.forEach(function(n, i) { f(wb.Sheets[
/* comments_stress_test family */
function check_comments(wb) {
var ws0 = wb.Sheets.Sheet2;
var ws0 = wb.Sheets["Sheet2"];
assert.equal(get_cell(ws0,"A1").c[0].a, 'Author');
assert.equal(get_cell(ws0,"A1").c[0].t, 'Author:\nGod thinks this is good');
assert.equal(get_cell(ws0,"C1").c[0].a, 'Author');
assert.equal(get_cell(ws0,"C1").c[0].t, 'I really hope that xlsx decides not to use magic like rPr');
var ws3 = wb.Sheets.Sheet4;
var ws3 = wb.Sheets["Sheet4"];
assert.equal(get_cell(ws3,"B1").c[0].a, 'Author');
assert.equal(get_cell(ws3,"B1").c[0].t, 'The next comment is empty');
assert.equal(get_cell(ws3,"B2").c[0].a, 'Author');
@ -401,14 +401,14 @@ describe('parse options', function() {
describe('cell', function() {
it('XLSX should generate HTML by default', function() {
var wb = X.read(fs.readFileSync(paths.cstxlsx), {type:TYPE});
var ws = wb.Sheets.Sheet1;
var ws = wb.Sheets["Sheet1"];
each_cell(ws, function(cell) {
assert.ok(html_cell_types.indexOf(cell.t) === -1 || cell.h);
});
});
it('XLSX should not generate HTML when requested', function() {
var wb = X.read(fs.readFileSync(paths.cstxlsx), {type:TYPE, cellHTML:false});
var ws = wb.Sheets.Sheet1;
var ws = wb.Sheets["Sheet1"];
each_cell(ws, function(cell) {
assert.ok(typeof cell.h === 'undefined');
});
@ -483,7 +483,7 @@ describe('parse options', function() {
});
it('should not generate cell styles by default', function() {
CSSPaths.forEach(function(p) {
var wb = X.read(fs.readFileSync(p), {type:TYPE, WTF:1});
var wb = X.read(fs.readFileSync(p), {type:TYPE, WTF:true});
wb.SheetNames.forEach(function(s) {
var ws = wb.Sheets[s];
each_cell(ws, function(cell) {
@ -513,7 +513,7 @@ describe('parse options', function() {
});
it('should generate cell dates when requested', function() {
DTPaths.forEach(function(p) {
var wb = X.read(fs.readFileSync(p), {type:TYPE, cellDates: true, WTF:1});
var wb = X.read(fs.readFileSync(p), {type:TYPE, cellDates: true, WTF:true});
var found = false;
each_sheet(wb, function(ws/*::, i*/) { /*:: void i; */each_cell(ws, function(cell) {
if(cell.t === 'd') return (found = true);
@ -529,9 +529,9 @@ describe('parse options', function() {
var str = X.write(wb, {bookType: "xlsx", type: "binary"});
var wb2 = X.read(str, {type: "binary"});
/*jshint -W069 */
assert.equal(wb2.Sheets.Sheet1["A1"].f, "IFS(2>3,1,3>2,2)");
assert.equal(wb2.Sheets["Sheet1"]["A1"].f, "IFS(2>3,1,3>2,2)");
var wb3 = X.read(str, {type: "binary", xlfn: true});
assert.equal(wb3.Sheets.Sheet1["A1"].f, "_xlfn.IFS(2>3,1,3>2,2)");
assert.equal(wb3.Sheets["Sheet1"]["A1"].f, "_xlfn.IFS(2>3,1,3>2,2)");
/*jshint +W069 */
});
});
@ -539,27 +539,27 @@ describe('parse options', function() {
it('should not generate sheet stubs by default', function() {
MCPaths.forEach(function(p) {
var wb = X.read(fs.readFileSync(p), {type:TYPE});
assert.throws(function() { return get_cell(wb.Sheets.Merge, "A2").v; });
assert.throws(function() { return get_cell(wb.Sheets["Merge"], "A2").v; });
});
});
it('should generate sheet stubs when requested', function() {
MCPaths.forEach(function(p) {
var wb = X.read(fs.readFileSync(p), {type:TYPE, sheetStubs:true});
assert.ok(get_cell(wb.Sheets.Merge, "A2").t == 'z');
assert.ok(get_cell(wb.Sheets["Merge"], "A2").t == 'z');
});
});
it('should handle stub cells', function() {
MCPaths.forEach(function(p) {
var wb = X.read(fs.readFileSync(p), {type:TYPE, sheetStubs:true});
X.utils.sheet_to_csv(wb.Sheets.Merge);
X.utils.sheet_to_json(wb.Sheets.Merge);
X.utils.sheet_to_formulae(wb.Sheets.Merge);
X.utils.sheet_to_csv(wb.Sheets["Merge"]);
X.utils.sheet_to_json(wb.Sheets["Merge"]);
X.utils.sheet_to_formulae(wb.Sheets["Merge"]);
ofmt.forEach(function(f) { if(f != "dbf") X.write(wb, {type:TYPE, bookType:f}); });
});
});
function checkcells(wb, A46, B26, C16, D2) {
[ ["A46", A46], ["B26", B26], ["C16", C16], ["D2", D2] ].forEach(function(r) {
assert.ok((typeof get_cell(wb.Sheets.Text, r[0]) !== 'undefined') == r[1]);
assert.ok((typeof get_cell(wb.Sheets["Text"], r[0]) !== 'undefined') == r[1]);
});
}
it('should read all cells by default', function() { FSTPaths.forEach(function(p) {
@ -581,35 +581,35 @@ describe('parse options', function() {
assert.ok(ws['!ref'] === "A1:B3");
var wb = X.utils.book_new();
X.utils.book_append_sheet(wb, ws, "Sheet1");
var bs = X.write(wb, { bookType: fmt, type: ot, WTF: 1 });
var bs = X.write(wb, { bookType: fmt, type: ot, WTF:true });
var wb0 = X.read(bs, { type: ot, WTF: 1 });
var ws0 = wb0.Sheets.Sheet1;
var wb0 = X.read(bs, { type: ot, WTF:true });
var ws0 = wb0.Sheets["Sheet1"];
assert.equal(ws0['!ref'], "A1:B3");
assert.equal(get_cell(ws0, "A1").v, 1);
assert.equal(get_cell(ws0, "A1").v, fmt == "dbf" ? "1" : 1);
assert.equal(get_cell(ws0, "B2").v, 4);
assert.equal(get_cell(ws0, "A3").v, 5);
var wb1 = X.read(bs, { type: ot, sheetRows: 1 });
var ws1 = wb1.Sheets.Sheet1;
var ws1 = wb1.Sheets["Sheet1"];
assert.equal(ws1['!ref'], "A1:B1");
assert.equal(get_cell(ws1, "A1").v, 1);
assert.equal(get_cell(ws1, "A1").v, fmt == "dbf" ? "1" : 1);
assert.ok(!get_cell(ws1, "B2"));
assert.ok(!get_cell(ws1, "A3"));
if(ws1['!fullref']) assert.equal(ws1['!fullref'], "A1:B3");
var wb2 = X.read(bs, { type: ot, sheetRows: 2 });
var ws2 = wb2.Sheets.Sheet1;
var ws2 = wb2.Sheets["Sheet1"];
assert.equal(ws2['!ref'], "A1:B2");
assert.equal(get_cell(ws2, "A1").v, 1);
assert.equal(get_cell(ws2, "A1").v, fmt == "dbf" ? "1" : 1);
assert.equal(get_cell(ws2, "B2").v, 4);
assert.ok(!get_cell(ws2, "A3"));
if(ws2['!fullref']) assert.equal(ws2['!fullref'], "A1:B3");
var wb3 = X.read(bs, { type: ot, sheetRows: 3 });
var ws3 = wb3.Sheets.Sheet1;
var ws3 = wb3.Sheets["Sheet1"];
assert.equal(ws3['!ref'], "A1:B3");
assert.equal(get_cell(ws3, "A1").v, 1);
assert.equal(get_cell(ws3, "A1").v, fmt == "dbf" ? "1" : 1);
assert.equal(get_cell(ws3, "B2").v, 4);
assert.equal(get_cell(ws3, "A3").v, 5);
if(ws3['!fullref']) assert.equal(ws3['!fullref'], "A1:B3");
@ -685,13 +685,13 @@ describe('input formats', function() {
it('should read base64 strings', function() { artifax.forEach(function(p) {
X.read(fs.readFileSync(p, 'base64'), {type: 'base64'});
}); });
(typeof Uint8Array !== 'undefined' ? it : it.skip)('should read array', function() { artifax.forEach(function(p) {
if(typeof Uint8Array !== 'undefined') it('should read array', function() { artifax.forEach(function(p) {
X.read(fs.readFileSync(p, 'binary').split("").map(function(x) { return x.charCodeAt(0); }), {type:'array'});
}); });
((browser || typeof Buffer === 'undefined') ? it.skip : it)('should read Buffers', function() { artifax.forEach(function(p) {
X.read(fs.readFileSync(p), {type: 'buffer'});
}); });
(typeof Uint8Array !== 'undefined' ? it : it.skip)('should read ArrayBuffer / Uint8Array', function() { artifax.forEach(function(p) {
if(typeof Uint8Array !== 'undefined') it('should read ArrayBuffer / Uint8Array', function() { artifax.forEach(function(p) {
var payload = fs.readFileSync(p, browser ? 'buffer' : null);
var ab = new ArrayBuffer(payload.length), vu = new Uint8Array(ab);
for(var i = 0; i < payload.length; ++i) vu[i] = payload[i];
@ -731,9 +731,9 @@ describe('output formats', function() {
fmts.forEach(function(fmt) {
var wb = X.utils.book_new();
X.utils.book_append_sheet(wb, X.utils.aoa_to_sheet([['R',"\u2603"],["\u0BEE",2]]), "Sheet1");
if(T == 'string' && !fmt[2]) return assert.throws(function() {X.write(wb, {type: T, bookType:fmt[0], WTF:1});});
var out = X.write(wb, {type: T, bookType:fmt[0], WTF:1});
var nwb = X.read(out, {type: T, PRN: fmt[0] == 'prn', WTF:1});
if(T == 'string' && !fmt[2]) return assert.throws(function() {X.write(wb, {type: T, bookType:fmt[0], WTF:true});});
var out = X.write(wb, {type: T, bookType:fmt[0], WTF:true});
var nwb = X.read(out, {type: T, PRN: fmt[0] == 'prn', WTF:true});
var nws = nwb.Sheets[nwb.SheetNames[0]];
assert.equal(get_cell(nws, "B2").v, 2);
assert.equal(get_cell(nws, "A1").v, "R");
@ -930,7 +930,7 @@ describe('parse features', function() {
].forEach(function(m) { it(m[0] + ' stress test', function() {
var wb = X.read(fs.readFileSync(m[1]), {type:TYPE});
check_comments(wb);
var ws0 = wb.Sheets.Sheet2;
var ws0 = wb.Sheets["Sheet2"];
assert.equal(get_cell(ws0,"A1").c[0].a, 'Author');
assert.equal(get_cell(ws0,"A1").c[0].t, 'Author:\nGod thinks this is good');
assert.equal(get_cell(ws0,"C1").c[0].a, 'Author');
@ -942,10 +942,10 @@ describe('parse features', function() {
var wbs=[];
var bef = (function() {
wbs = [
X.read(fs.readFileSync(paths.cpxlsx), {type:TYPE, WTF:1}),
X.read(fs.readFileSync(paths.cpxlsb), {type:TYPE, WTF:1}),
X.read(fs.readFileSync(paths.cpxls), {type:TYPE, WTF:1}),
X.read(fs.readFileSync(paths.cpxml), {type:TYPE, WTF:1})
X.read(fs.readFileSync(paths.cpxlsx), {type:TYPE, WTF:true}),
X.read(fs.readFileSync(paths.cpxlsb), {type:TYPE, WTF:true}),
X.read(fs.readFileSync(paths.cpxls), {type:TYPE, WTF:true}),
X.read(fs.readFileSync(paths.cpxml), {type:TYPE, WTF:true})
];
});
if(typeof before != 'undefined') before(bef);
@ -984,7 +984,7 @@ describe('parse features', function() {
it('should use original range if not set', function() {
var opts = {type:TYPE};
FSTPaths.map(function(p) { return X.read(fs.readFileSync(p), opts); }).forEach(function(wb) {
assert.equal(wb.Sheets.Text["!ref"],"A1:F49");
assert.equal(wb.Sheets["Text"]["!ref"],"A1:F49");
});
});
it('should adjust range if set', function() {
@ -992,8 +992,8 @@ describe('parse features', function() {
var wbs = FSTPaths.map(function(p) { return X.read(fs.readFileSync(p), opts); });
/* TODO: XLS, XML, ODS */
wbs.slice(0,2).forEach(function(wb) {
assert.equal(wb.Sheets.Text["!fullref"],"A1:F49");
assert.equal(wb.Sheets.Text["!ref"],"A1:F10");
assert.equal(wb.Sheets["Text"]["!fullref"],"A1:F49");
assert.equal(wb.Sheets["Text"]["!ref"],"A1:F10");
});
});
it('should not generate comment cells', function() {
@ -1001,8 +1001,8 @@ describe('parse features', function() {
var wbs = CSTPaths.map(function(p) { return X.read(fs.readFileSync(p), opts); });
/* TODO: XLS, XML, ODS */
wbs.slice(0,2).forEach(function(wb) {
assert.equal(wb.Sheets.Sheet7["!fullref"],"A1:N34");
assert.equal(wb.Sheets.Sheet7["!ref"],"A1");
assert.equal(wb.Sheets["Sheet7"]["!fullref"],"A1:N34");
assert.equal(wb.Sheets["Sheet7"]["!ref"],"A1");
});
});
});
@ -1016,16 +1016,16 @@ describe('parse features', function() {
if(typeof before != 'undefined') before(bef);
else it('before', bef);
it('should have "!cols"', function() {
wbs.forEach(function(wb) { assert.ok(wb.Sheets.Sheet1['!cols']); });
wbs.forEach(function(wb) { assert.ok(wb.Sheets["Sheet1"]['!cols']); });
});
it('should have correct widths', function() {
/* SYLK rounds wch so skip non-integral */
wbs_no_slk.map(function(x) { return x.Sheets.Sheet1['!cols']; }).forEach(function(x) {
wbs_no_slk.map(function(x) { return x.Sheets["Sheet1"]['!cols']; }).forEach(function(x) {
assert.equal(x[1].width, 0.1640625);
assert.equal(x[2].width, 16.6640625);
assert.equal(x[3].width, 1.6640625);
});
wbs.map(function(x) { return x.Sheets.Sheet1['!cols']; }).forEach(function(x) {
wbs.map(function(x) { return x.Sheets["Sheet1"]['!cols']; }).forEach(function(x) {
assert.equal(x[4].width, 4.83203125);
assert.equal(x[5].width, 8.83203125);
assert.equal(x[6].width, 12.83203125);
@ -1034,12 +1034,12 @@ describe('parse features', function() {
});
it('should have correct pixels', function() {
/* SYLK rounds wch so skip non-integral */
wbs_no_slk.map(function(x) { return x.Sheets.Sheet1['!cols']; }).forEach(function(x) {
wbs_no_slk.map(function(x) { return x.Sheets["Sheet1"]['!cols']; }).forEach(function(x) {
assert.equal(x[1].wpx, 1);
assert.equal(x[2].wpx, 100);
assert.equal(x[3].wpx, 10);
});
wbs.map(function(x) { return x.Sheets.Sheet1['!cols']; }).forEach(function(x) {
wbs.map(function(x) { return x.Sheets["Sheet1"]['!cols']; }).forEach(function(x) {
assert.equal(x[4].wpx, 29);
assert.equal(x[5].wpx, 53);
assert.equal(x[6].wpx, 77);
@ -1060,25 +1060,25 @@ describe('parse features', function() {
if(typeof before != 'undefined') before(bef);
else it('before', bef);
it('should have "!rows"', function() {
wbs.forEach(function(wb) { assert.ok(wb.Sheets.Sheet1['!rows']); });
wbs.forEach(function(wb) { assert.ok(wb.Sheets["Sheet1"]['!rows']); });
});
it('should have correct points', function() {
wbs.map(function(x) { return x.Sheets.Sheet1['!rows']; }).forEach(function(x) {
wbs.map(function(x) { return x.Sheets["Sheet1"]['!rows']; }).forEach(function(x) {
assert.equal(x[1].hpt, 1);
assert.equal(x[2].hpt, 10);
assert.equal(x[3].hpt, 100);
});
});
it('should have correct pixels', function() {
wbs.map(function(x) { return x.Sheets.Sheet1['!rows']; }).forEach(function(x) {
wbs.map(function(x) { return x.Sheets["Sheet1"]['!rows']; }).forEach(function(x) {
/* note: at 96 PPI hpt == hpx */
assert.equal(x[1].hpx, 1);
assert.equal(x[2].hpx, 10);
assert.equal(x[3].hpx, 100);
});
});
(ol ? it : it.skip)('should have correct outline levels', function() {
ols.map(function(x) { return x.Sheets.Sheet1; }).forEach(function(ws) {
it('should have correct outline levels', function() {
ols.map(function(x) { return x.Sheets["Sheet1"]; }).forEach(function(ws) {
var rows = ws['!rows'];
for(var i = 0; i < 29; ++i) {
var cell = get_cell(ws, "A" + X.utils.encode_row(i));
@ -1103,9 +1103,9 @@ describe('parse features', function() {
else it('before', bef);
it('should have !merges', function() {
wbs.forEach(function(wb) {
assert.ok(wb.Sheets.Merge['!merges']);
assert.ok(wb.Sheets["Merge"]['!merges']);
});
var m = wbs.map(function(x) { return x.Sheets.Merge['!merges'].map(function(y) { return X.utils.encode_range(y); });});
var m = wbs.map(function(x) { return x.Sheets["Merge"]['!merges'].map(function(y) { return X.utils.encode_range(y); });});
m.slice(1).forEach(function(x) {
assert.deepEqual(m[0].sort(),x.sort());
});
@ -1115,17 +1115,17 @@ describe('parse features', function() {
describe('should find hyperlinks', function() {
var wb1, wb2;
var bef = (function() {
wb1 = HLPaths.map(function(p) { return X.read(fs.readFileSync(p), {type:TYPE, WTF:1}); });
wb2 = ILPaths.map(function(p) { return X.read(fs.readFileSync(p), {type:TYPE, WTF:1}); });
wb1 = HLPaths.map(function(p) { return X.read(fs.readFileSync(p), {type:TYPE, WTF:true}); });
wb2 = ILPaths.map(function(p) { return X.read(fs.readFileSync(p), {type:TYPE, WTF:true}); });
});
if(typeof before != 'undefined') before(bef);
else it('before', bef);
['xlsx', 'xlsb', 'xls', 'xml'].forEach(function(x, i) {
it(x + " external", function() { hlink1(wb1[i].Sheets.Sheet1); });
it(x + " external", function() { hlink1(wb1[i].Sheets["Sheet1"]); });
});
['xlsx', 'xlsb', 'xls', 'xml', 'ods'].forEach(function(x, i) {
it(x + " internal", function() { hlink2(wb2[i].Sheets.Sheet1); });
it(x + " internal", function() { hlink2(wb2[i].Sheets["Sheet1"]); });
});
});
@ -1275,10 +1275,10 @@ describe('parse features', function() {
else it('before', bef);
['xlsx'].forEach(function(m) { it(m, function() {
var wb2 = X.read(X.write(wb, {bookType:m, type:TYPE}),{type:TYPE, cellHTML:true});
assert.equal(get_cell(wb2.Sheets.Sheet1, "A2").h, "&amp;");
assert.equal(get_cell(wb2.Sheets.Sheet1, "B2").h, "&lt;");
assert.equal(get_cell(wb2.Sheets.Sheet1, "C2").h, "&gt;");
var h = get_cell(wb2.Sheets.Sheet1, "D2").h;
assert.equal(get_cell(wb2.Sheets["Sheet1"], "A2").h, "&amp;");
assert.equal(get_cell(wb2.Sheets["Sheet1"], "B2").h, "&lt;");
assert.equal(get_cell(wb2.Sheets["Sheet1"], "C2").h, "&gt;");
var h = get_cell(wb2.Sheets["Sheet1"], "D2").h;
assert.ok(h == "&#x000a;" || h == "<br/>");
}); });
});
@ -1287,7 +1287,7 @@ describe('parse features', function() {
var wbs=[];
var bef = (function() {
if(!fs.existsSync(paths.pmxls)) return;
wbs = PMPaths.map(function(p) { return X.read(fs.readFileSync(p), {type:TYPE, WTF:1}); });
wbs = PMPaths.map(function(p) { return X.read(fs.readFileSync(p), {type:TYPE, WTF:true}); });
});
if(typeof before != 'undefined') before(bef);
else it('before', bef);
@ -1306,8 +1306,8 @@ describe('parse features', function() {
describe('should correctly handle styles', function() {
var wsxls, wsxlsx, rn, rn2;
var bef = (function() {
wsxls=X.read(fs.readFileSync(paths.cssxls), {type:TYPE,cellStyles:true,WTF:1}).Sheets.Sheet1;
wsxlsx=X.read(fs.readFileSync(paths.cssxlsx), {type:TYPE,cellStyles:true,WTF:1}).Sheets.Sheet1;
wsxls=X.read(fs.readFileSync(paths.cssxls), {type:TYPE,cellStyles:true,WTF:true}).Sheets["Sheet1"];
wsxlsx=X.read(fs.readFileSync(paths.cssxlsx), {type:TYPE,cellStyles:true,WTF:true}).Sheets["Sheet1"];
rn = function(range) {
var r = X.utils.decode_range(range);
var out = [];
@ -1395,12 +1395,36 @@ describe('parse features', function() {
assert.equal(data[4][1], '456.00');
assert.equal(data[5][1], '7,890');
}); }); });
it('date system', function() {[
"biff5", "ods", "slk", "xls", "xlsb", "xlsx", "xml"
].forEach(function(ext) {
// TODO: verify actual date values
var wb0 = X.read(fs.readFileSync("./test_files/1904/1900." + ext), {type: TYPE, cellNF: true});
assert.ok(!wb0.Workbook || !wb0.Workbook.WBProps || !wb0.Workbook.WBProps.date1904);
assert.equal(X.utils.sheet_to_csv(wb0.Sheets[wb0.SheetNames[0]]), [
"1,1900-01-01,1900-01-01,1900-01-01",
"11,1900-01-11,1900-01-11,1900-01-11",
"111,1900-04-20,1900-04-20,1900-04-20",
"1111,1903-01-15,1903-01-15,1903-01-15",
"11111,1930-06-02,1930-06-02,1930-06-02"
].join("\n"));
var wb4 = X.read(fs.readFileSync("./test_files/1904/1904." + ext), {type: TYPE, cellNF: true});
assert.ok(wb4.Workbook.WBProps.date1904);
assert.equal(X.utils.sheet_to_csv(wb4.Sheets[wb4.SheetNames[0]]), [
"1,1904-01-02,1904-01-02,1904-01-02",
"11,1904-01-12,1904-01-12,1904-01-12",
"111,1904-04-21,1904-04-21,1904-04-21",
"1111,1907-01-16,1907-01-16,1907-01-16",
"11111,1934-06-03,1934-06-03,1934-06-03"
].join("\n"));
}); });
});
describe('write features', function() {
describe('props', function() {
describe('core', function() {
var ws;
var baseprops = {
Category: "Newspaper",
ContentStatus: "Published",
@ -1416,6 +1440,7 @@ describe('write features', function() {
Subject: "Superman",
Title: "Man of Steel"
};
var ws;
var bef = (function() {
ws = X.utils.aoa_to_sheet([["a","b","c"],[1,2,3]]);
});
@ -1452,17 +1477,17 @@ describe('write features', function() {
["xlsb", "XFD1048576"]
].forEach(function(r) { it(r[0], function() {
var C = X.utils.decode_cell(r[1]);
var wopts = {bookType:r[0], type:'binary', WTF:1};
var wopts = {bookType:r[0], type:'binary', WTF:true};
var wb = { SheetNames: ["Sheet1"], Sheets: { Sheet1: {} } };
wb.Sheets.Sheet1['!ref'] = "A1:" + X.utils.encode_cell({r:0, c:C.c});
wb.Sheets["Sheet1"]['!ref'] = "A1:" + X.utils.encode_cell({r:0, c:C.c});
X.write(wb, wopts);
wb.Sheets.Sheet1['!ref'] = "A" + X.utils.encode_row(C.r - 5) + ":" + X.utils.encode_cell({r:C.r, c:0});
wb.Sheets["Sheet1"]['!ref'] = "A" + X.utils.encode_row(C.r - 5) + ":" + X.utils.encode_cell({r:C.r, c:0});
X.write(wb, wopts);
wb.Sheets.Sheet1['!ref'] = "A1:" + X.utils.encode_cell({r:0, c:C.c+1});
wb.Sheets["Sheet1"]['!ref'] = "A1:" + X.utils.encode_cell({r:0, c:C.c+1});
assert.throws(function() { X.write(wb, wopts); });
wb.Sheets.Sheet1['!ref'] = "A" + X.utils.encode_row(C.r - 5) + ":" + X.utils.encode_cell({r:C.r+1, c:0});
wb.Sheets["Sheet1"]['!ref'] = "A" + X.utils.encode_row(C.r - 5) + ":" + X.utils.encode_cell({r:C.r+1, c:0});
assert.throws(function() { X.write(wb, wopts); });
}); }); });
it('single worksheet formats', function() {
@ -1485,8 +1510,8 @@ describe('write features', function() {
((wb2.Workbook||{}).Names || []).forEach(function(dn) { if(dn.Name == "_xlnm._FilterDatabase" && dn.Sheet == 0) Name = dn; });
assert.ok(!!Name, "Could not find _xlnm._FilterDatabases name WR");
assert.equal(Name.Ref, "Sheet1!$A$1:$C$2");
X.utils.sheet_add_aoa(wb2.Sheets.Sheet1, [[4,5,6]], { origin: -1 });
wb2.Sheets.Sheet1["!autofilter"].ref = wb2.Sheets.Sheet1["!ref"];
X.utils.sheet_add_aoa(wb2.Sheets["Sheet1"], [[4,5,6]], { origin: -1 });
wb2.Sheets["Sheet1"]["!autofilter"].ref = wb2.Sheets["Sheet1"]["!ref"];
var wb3 = X.read(X.write(wb2, {bookType:fmt, type:TYPE}), {type:TYPE});
Name = void 0;
((wb3.Workbook||{}).Names || []).forEach(function(dn) { if(dn.Name == "_xlnm._FilterDatabase" && dn.Sheet == 0) Name = dn; });
@ -1562,8 +1587,8 @@ describe('roundtrip features', function() {
["xlsx", "xlsb", "xlml", "ods", "biff8", "numbers"].forEach(function(f) { it(f, function() {
var wb1 = X.read(fs.readFileSync(paths.mcxlsx), {type:TYPE});
var wb2 = X.read(X.write(wb1,{bookType:f,type:'binary',numbers:XLSX_ZAHL}),{type:'binary'});
var m1 = wb1.Sheets.Merge['!merges'].map(X.utils.encode_range);
var m2 = wb2.Sheets.Merge['!merges'].map(X.utils.encode_range);
var m1 = wb1.Sheets["Merge"]['!merges'].map(X.utils.encode_range);
var m2 = wb2.Sheets["Merge"]['!merges'].map(X.utils.encode_range);
assert.equal(m1.length, m2.length);
for(var i = 0; i < m1.length; ++i) assert.ok(m1.indexOf(m2[i]) > -1);
}); });
@ -1595,13 +1620,13 @@ describe('roundtrip features', function() {
});
});
describe('should preserve formulae', function() { [
describe('should preserve formulae', function() { var ff = [
['xlml', paths.fstxml],
['xlsx', paths.fstxlsx],
['ods', paths.fstods]
].forEach(function(w) { it(w[0], function() {
var wb1 = X.read(fs.readFileSync(w[1]), {type:TYPE, cellFormula:true, WTF:1});
var wb2 = X.read(X.write(wb1, {bookType:w[0], type:TYPE}), {cellFormula:true, type:TYPE, WTF:1});
]; ff.forEach(function(w) { it(w[0], function() {
var wb1 = X.read(fs.readFileSync(w[1]), {type:TYPE, cellFormula:true, WTF:true});
var wb2 = X.read(X.write(wb1, {bookType:w[0], type:TYPE}), {cellFormula:true, type:TYPE, WTF:true});
wb1.SheetNames.forEach(function(n) {
assert.equal(
X.utils.sheet_to_formulae(wb1.Sheets[n]).sort().join("\n"),
@ -1613,17 +1638,17 @@ describe('roundtrip features', function() {
describe('should preserve dynamic array formulae', function() { [
['xlsx', paths.m19xlsx]
].forEach(function(w) { it(w[0], function() {
var wb1 = X.read(fs.readFileSync(w[1]), {xlfn: true, type:TYPE, cellFormula:true, WTF:1});
var wb2 = X.read(X.write(wb1, {bookType:w[0], type:TYPE}), {cellFormula:true, xlfn: true, type:TYPE, WTF:1});
assert.equal(!!get_cell(wb2.Sheets.Sheet1, "B3").D, true);
assert.equal(!!get_cell(wb2.Sheets.Sheet1, "B13").D, true);
assert.equal(!!get_cell(wb2.Sheets.Sheet1, "C13").D, true);
var wb1 = X.read(fs.readFileSync(w[1]), {xlfn: true, type:TYPE, cellFormula:true, WTF:true});
var wb2 = X.read(X.write(wb1, {bookType:w[0], type:TYPE}), {cellFormula:true, xlfn: true, type:TYPE, WTF:true});
assert.equal(!!get_cell(wb2.Sheets["Sheet1"], "B3").D, true);
assert.equal(!!get_cell(wb2.Sheets["Sheet1"], "B13").D, true);
assert.equal(!!get_cell(wb2.Sheets["Sheet1"], "C13").D, true);
get_cell(wb2.Sheets.Sheet1, "B3").D = false;
var wb3 = X.read(X.write(wb2, {bookType:w[0], type:TYPE}), {cellFormula:true, xlfn: true, type:TYPE, WTF:1});
assert.equal(!!get_cell(wb3.Sheets.Sheet1, "B3").D, false);
assert.equal(!!get_cell(wb3.Sheets.Sheet1, "B13").D, true);
assert.equal(!!get_cell(wb3.Sheets.Sheet1, "C13").D, true);
get_cell(wb2.Sheets["Sheet1"], "B3").D = false;
var wb3 = X.read(X.write(wb2, {bookType:w[0], type:TYPE}), {cellFormula:true, xlfn: true, type:TYPE, WTF:true});
assert.equal(!!get_cell(wb3.Sheets["Sheet1"], "B3").D, false);
assert.equal(!!get_cell(wb3.Sheets["Sheet1"], "B13").D, true);
assert.equal(!!get_cell(wb3.Sheets["Sheet1"], "C13").D, true);
}); }); });
describe('should preserve hyperlink', function() { [
@ -1638,12 +1663,12 @@ describe('roundtrip features', function() {
['ods', paths.ilods, false]
].forEach(function(w) { it(w[0]+" "+(w[2]?"ex":"in")+ "ternal", function() {
var wb = X.read(fs.readFileSync(w[1]), {type:TYPE, WTF:opts.WTF});
var hlink = (w[2] ? hlink1 : hlink2); hlink(wb.Sheets.Sheet1);
var hlink = (w[2] ? hlink1 : hlink2); hlink(wb.Sheets["Sheet1"]);
wb = X.read(X.write(wb, {bookType:w[0], type:TYPE, WTF:opts.WTF}), {type:TYPE, WTF:opts.WTF});
hlink(wb.Sheets.Sheet1);
hlink(wb.Sheets["Sheet1"]);
}); }); });
(fs.existsSync(paths.pmxlsx) ? describe : describe.skip)('should preserve page margins', function() {[
describe('should preserve page margins', function() {[
['xlml', paths.pmxml],
['xlsx', paths.pmxlsx],
['xlsb', paths.pmxlsb]
@ -1691,7 +1716,7 @@ describe('roundtrip features', function() {
ws1['!cols'] = [{wch:9},{wpx:100},{width:80},{hidden:true}];
var wb1 = {SheetNames:["Sheet1"], Sheets:{Sheet1:ws1}};
var wb2 = X.read(X.write(wb1, {bookType:w, type:TYPE}), {type:TYPE, cellStyles:true});
var ws2 = wb2.Sheets.Sheet1;
var ws2 = wb2.Sheets["Sheet1"];
assert.equal(ws2['!cols'][3].hidden, true);
assert.equal(ws2['!cols'][0].wch, 9);
if(w == 'slk') return;
@ -1711,7 +1736,7 @@ describe('roundtrip features', function() {
for(var i = 0; i <= 7; ++i) ws1['!rows'].push({level:i});
var wb1 = {SheetNames:["Sheet1"], Sheets:{Sheet1:ws1}};
var wb2 = X.read(X.write(wb1, {bookType:w, type:TYPE, cellStyles:true}), {type:TYPE, cellStyles:true});
var ws2 = wb2.Sheets.Sheet1;
var ws2 = wb2.Sheets["Sheet1"];
assert.equal(ws2['!rows'][0].hpx, 12);
assert.equal(ws2['!rows'][1].hpt, 24);
assert.equal(ws2['!rows'][2].hpx, 48);
@ -1767,6 +1792,7 @@ describe('roundtrip features', function() {
assert.equal(wb2.Sheets[wb2.SheetNames[i]]['!autofilter'].ref,"A1:E22");
}
}); });
});
//function password_file(x){return x.match(/^password.*\.xls$/); }
@ -2002,7 +2028,7 @@ describe('json output', function() {
seq(8).forEach(function(n) {
var opts = {};
if(n & 1) opts.header = 1;
if(n & 2) opts.raw = 1;
if(n & 2) opts.raw = true;
if(n & 4) opts.defval = null;
var J = X.utils.sheet_to_json(ws, opts);
for(var i = 0; i < 3; ++i) {
@ -2015,7 +2041,7 @@ describe('json output', function() {
var codes = [["ใ‚ 1", "\u00E3\u0081\u0082 1"]];
var plaintext_val = [
var plaintext_val = ([
["A1", 'n', -0.08, "-0.08"],
["B1", 'n', 4001, "4,001"],
["C1", 's', "ใ‚ 1", "ใ‚ 1"],
@ -2025,12 +2051,12 @@ var plaintext_val = [
["D3", 'b', false, "FALSE"],
["B3", 's', " ", " "],
["A3"]
];
]);
function plaintext_test(wb, raw) {
var sheet = wb.Sheets[wb.SheetNames[0]];
plaintext_val.forEach(function(x) {
var cell = get_cell(sheet, x[0]);
var tcval = x[2+!!raw];
var tcval = x[2+(!!raw ? 1 : 0)];
var type = raw ? 's' : x[1];
if(x.length == 1) { if(cell) { assert.equal(cell.t, 'z'); assert.ok(!cell.v); } return; }
assert.equal(cell.v, tcval); assert.equal(cell.t, type);
@ -2055,14 +2081,14 @@ describe('CSV', function() {
var b = "1,2,3,\nTRUE,FALSE,,sheetjs\nfoo,bar,2/19/14,0.3\n,,,\nbaz,,qux,\n";
it('should generate date numbers by default', function() {
var opts = {type:"binary"};
var cell = get_cell(X.read(b, opts).Sheets.Sheet1, "C3");
var cell = get_cell(X.read(b, opts).Sheets["Sheet1"], "C3");
assert.equal(cell.w, '2/19/14');
assert.equal(cell.t, 'n');
assert.ok(typeof cell.v == "number");
});
it('should generate dates when requested', function() {
var opts = {type:"binary", cellDates:true};
var cell = get_cell(X.read(b, opts).Sheets.Sheet1, "C3");
var cell = get_cell(X.read(b, opts).Sheets["Sheet1"], "C3");
assert.equal(cell.w, '2/19/14');
assert.equal(cell.t, 'd');
assert.ok(cell.v instanceof Date || typeof cell.v == "string");
@ -2070,29 +2096,29 @@ describe('CSV', function() {
it('should use US date code 14 by default', function() {
var opts = ({type:"binary"}/*:any*/);
var cell = get_cell(X.read(b, opts).Sheets.Sheet1, "C3");
var cell = get_cell(X.read(b, opts).Sheets["Sheet1"], "C3");
assert.equal(cell.w, '2/19/14');
opts.cellDates = true;
cell = get_cell(X.read(b, opts).Sheets.Sheet1, "C3");
cell = get_cell(X.read(b, opts).Sheets["Sheet1"], "C3");
assert.equal(cell.w, '2/19/14');
});
it('should honor dateNF override', function() {
var opts = ({type:"binary", dateNF:"YYYY-MM-DD"}/*:any*/);
var cell = get_cell(X.read(b, opts).Sheets.Sheet1, "C3");
var cell = get_cell(X.read(b, opts).Sheets["Sheet1"], "C3");
/* NOTE: IE interprets 2-digit years as 19xx */
assert.ok(cell.w == '2014-02-19' || cell.w == '1914-02-19');
opts.cellDates = true; opts.dateNF = "YY-MM-DD";
cell = get_cell(X.read(b, opts).Sheets.Sheet1, "C3");
cell = get_cell(X.read(b, opts).Sheets["Sheet1"], "C3");
assert.equal(cell.w, '14-02-19');
});
it('should interpret dateNF', function() {
var bb = "1,2,3,\nTRUE,FALSE,,sheetjs\nfoo,bar,2/3/14,0.3\n,,,\nbaz,,qux,\n";
var opts = {type:"binary", cellDates:true, dateNF:'m/d/yy'};
var cell = get_cell(X.read(bb, opts).Sheets.Sheet1, "C3");
var cell = get_cell(X.read(bb, opts).Sheets["Sheet1"], "C3");
assert.equal(cell.v.getMonth(), 1);
assert.equal(cell.w, "2/3/14");
opts = {type:"binary", cellDates:true, dateNF:'d/m/yy'};
cell = get_cell(X.read(bb, opts).Sheets.Sheet1, "C3");
cell = get_cell(X.read(bb, opts).Sheets["Sheet1"], "C3");
assert.equal(cell.v.getMonth(), 2);
assert.equal(cell.w, "2/3/14");
});
@ -2100,7 +2126,7 @@ describe('CSV', function() {
it('should generate strings if raw option is passed', function() { plaintext_test(X.read(csv_str, {type:"string", raw:true}), true); });
it('should handle formulae', function() {
var bb = '=,=1+1,="100"';
var sheet = X.read(bb, {type:"binary"}).Sheets.Sheet1;
var sheet = X.read(bb, {type:"binary"}).Sheets["Sheet1"];
assert.equal(get_cell(sheet, "A1").t, 's');
assert.equal(get_cell(sheet, "A1").v, '=');
assert.equal(get_cell(sheet, "B1").f, '1+1');
@ -2109,12 +2135,12 @@ describe('CSV', function() {
});
it('should interpret CRLF newlines', function() {
var wb = X.read("sep=&\r\n1&2&3\r\n4&5&6", {type: "string"});
assert.equal(wb.Sheets.Sheet1["!ref"], "A1:C2");
assert.equal(wb.Sheets["Sheet1"]["!ref"], "A1:C2");
});
if(!browser || typeof cptable !== 'undefined') it('should honor codepage for binary strings', function() {
var data = "abc,def\nghi,j\xD3l";
[[1251, 'ะฃ'],[1252, 'ร“'], [1253, 'ฮฃ'], [1254, 'ร“'], [1255, 'ืƒ'], [1256, 'ุณ'], [10000, 'โ€']].forEach(function(m) {
var ws = X.read(data, {type:"binary", codepage:m[0]}).Sheets.Sheet1;
var ws = X.read(data, {type:"binary", codepage:m[0]}).Sheets["Sheet1"];
assert.equal(get_cell(ws, "B2").v, "j" + m[1] + "l");
});
});
@ -2164,9 +2190,9 @@ describe('CSV', function() {
var data = ["1,a", "2,b", "3,c"];
[ "\r", "\n", "\r\n" ].forEach(function(RS) {
var wb = X.read(data.join(RS), {type:'binary'});
assert.equal(get_cell(wb.Sheets.Sheet1, "A1").v, 1);
assert.equal(get_cell(wb.Sheets.Sheet1, "B3").v, "c");
assert.equal(wb.Sheets.Sheet1['!ref'], "A1:B3");
assert.equal(get_cell(wb.Sheets["Sheet1"], "A1").v, 1);
assert.equal(get_cell(wb.Sheets["Sheet1"], "B3").v, "c");
assert.equal(wb.Sheets["Sheet1"]['!ref'], "A1:B3");
});
});
it('should handle skipHidden for rows if requested', function() {
@ -2210,32 +2236,33 @@ describe('sylk', function() {
describe('input', function(){
it('codepage', cpavail ? function() {
var str = "ID;PWXL;N;E\r\nC;X1;Y1;K\"a โ€“ b\"\r\nE", A1 = "a โ€“ b";
assert.equal(get_cell(X.read(str, {type:"string"}).Sheets.Sheet1, "A1").v, A1);
assert.equal(get_cell(X.read(str.replace(/โ€“/, "\x96"), {type:"binary", codepage:1252}).Sheets.Sheet1, "A1").v, A1);
assert.equal(get_cell(X.read(str, {type:"string"}).Sheets["Sheet1"], "A1").v, A1);
assert.equal(get_cell(X.read(str.replace(/โ€“/, "\x96"), {type:"binary", codepage:1252}).Sheets["Sheet1"], "A1").v, A1);
if(typeof Buffer !== 'undefined' && !browser) {
assert.equal(get_cell(X.read(Buffer_from(str), {type:"buffer", codepage:65001}).Sheets.Sheet1, "A1").v, A1);
assert.equal(get_cell(X.read(Buffer_from(str.replace(/โ€“/, "\x96"), "binary"), {type:"buffer", codepage:1252}).Sheets.Sheet1, "A1").v, A1);
assert.equal(get_cell(X.read(Buffer_from(str), {type:"buffer", codepage:65001}).Sheets["Sheet1"], "A1").v, A1);
assert.equal(get_cell(X.read(Buffer_from(str.replace(/โ€“/, "\x96"), "binary"), {type:"buffer", codepage:1252}).Sheets["Sheet1"], "A1").v, A1);
}
} : null);
});
describe('date system', function() {
function make_slk(d1904) { return "ID;PSheetJS\nP;Pd\\/m\\/yy\nP;Pd\\/m\\/yyyy\n" + (d1904 != null ? "O;D;V" + d1904 : "") + "\nF;P0;FG0G;X1;Y1\nC;K1\nE"; }