mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
(core) updates from grist-core
This commit is contained in:
commit
b29ce996b6
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "grist-core",
|
"name": "grist-core",
|
||||||
"version": "1.0.3",
|
"version": "1.0.4",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"description": "Grist is the evolution of spreadsheets",
|
"description": "Grist is the evolution of spreadsheets",
|
||||||
"homepage": "https://github.com/gristlabs/grist-core",
|
"homepage": "https://github.com/gristlabs/grist-core",
|
||||||
|
BIN
sandbox/grist/imports/fixtures/test_headers_with_none_cell.xlsx
Normal file
BIN
sandbox/grist/imports/fixtures/test_headers_with_none_cell.xlsx
Normal file
Binary file not shown.
@ -52,7 +52,7 @@ def _is_numeric(text):
|
|||||||
try:
|
try:
|
||||||
t(text)
|
t(text)
|
||||||
return True
|
return True
|
||||||
except (ValueError, OverflowError):
|
except (ValueError, OverflowError, TypeError):
|
||||||
pass
|
pass
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -63,7 +63,7 @@ def _is_header(header, data_rows):
|
|||||||
"""
|
"""
|
||||||
# See if the row has any non-text values.
|
# See if the row has any non-text values.
|
||||||
for cell in header:
|
for cell in header:
|
||||||
if not isinstance(cell, six.string_types) or _is_numeric(cell):
|
if not (isinstance(cell, six.string_types) or cell is None) or _is_numeric(cell):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
@ -109,7 +109,7 @@ def expand_headers(headers, data_offset, rows):
|
|||||||
row_length = max(itertools.chain([len(headers)],
|
row_length = max(itertools.chain([len(headers)],
|
||||||
(_count_nonempty(r) for r in itertools.islice(rows, data_offset,
|
(_count_nonempty(r) for r in itertools.islice(rows, data_offset,
|
||||||
None))))
|
None))))
|
||||||
header_values = [h.strip() for h in headers] + [u''] * (row_length - len(headers))
|
header_values = [h.strip() if h else '' for h in headers] + [u''] * (row_length - len(headers))
|
||||||
return header_values
|
return header_values
|
||||||
|
|
||||||
|
|
||||||
|
@ -105,17 +105,17 @@ class TestImportXLS(unittest.TestCase):
|
|||||||
'table_name': u'Transaction Report',
|
'table_name': u'Transaction Report',
|
||||||
'column_metadata': [
|
'column_metadata': [
|
||||||
{'type': 'Any', 'id': u''},
|
{'type': 'Any', 'id': u''},
|
||||||
{'type': 'Any', 'id': u''},
|
{'type': 'Numeric', 'id': u'Start'},
|
||||||
{'type': 'Numeric', 'id': u''},
|
{'type': 'Numeric', 'id': u''},
|
||||||
{'type': 'Numeric', 'id': u''},
|
{'type': 'Numeric', 'id': u''},
|
||||||
{'type': 'Any', 'id': u''},
|
{'type': 'Any', 'id': u'Seek no easy ways'},
|
||||||
],
|
],
|
||||||
'table_data': [
|
'table_data': [
|
||||||
['', u'SINGLE MERGED', u'The End'],
|
[u'SINGLE MERGED', u'The End'],
|
||||||
['Start', '1637384.52', ''],
|
[1637384.52, None],
|
||||||
[None, 2444344.06, None],
|
[2444344.06, None],
|
||||||
[None, 2444344.06, None],
|
[2444344.06, None],
|
||||||
['Seek no easy ways', u'', u''],
|
[u'', u''],
|
||||||
],
|
],
|
||||||
}])
|
}])
|
||||||
|
|
||||||
@ -225,6 +225,25 @@ class TestImportXLS(unittest.TestCase):
|
|||||||
],
|
],
|
||||||
}])
|
}])
|
||||||
|
|
||||||
|
def test_header_with_none_cell(self):
|
||||||
|
parsed_file = import_xls.parse_file(*_get_fixture('test_headers_with_none_cell.xlsx'))
|
||||||
|
tables = parsed_file[1]
|
||||||
|
self.assertEqual(tables, [{
|
||||||
|
'table_name': 'Sheet1',
|
||||||
|
'column_metadata': [
|
||||||
|
{'id': u'header1', 'type': 'Any'},
|
||||||
|
{'id': u'header2', 'type': 'Any'},
|
||||||
|
{'id': u'header3', 'type': 'Any'},
|
||||||
|
{'id': u'header4', 'type': 'Any'},
|
||||||
|
],
|
||||||
|
'table_data': [
|
||||||
|
['foo1', 'foo2'],
|
||||||
|
['bar1', 'bar2'],
|
||||||
|
['baz1', 'baz2'],
|
||||||
|
['boz1', 'boz2'],
|
||||||
|
],
|
||||||
|
}])
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
@ -128,14 +128,14 @@ describe("Localization", function() {
|
|||||||
try {
|
try {
|
||||||
// Wrong path to locales.
|
// Wrong path to locales.
|
||||||
process.env.GRIST_LOCALES_DIR = __filename;
|
process.env.GRIST_LOCALES_DIR = __filename;
|
||||||
await assert.isRejected(server.restart());
|
await assert.isRejected(server.restart(false, true));
|
||||||
// Empty folder.
|
// Empty folder.
|
||||||
const tempDirectory = fs.mkdtempSync(path.join(os.tmpdir(), 'grist_test_'));
|
const tempDirectory = fs.mkdtempSync(path.join(os.tmpdir(), 'grist_test_'));
|
||||||
process.env.GRIST_LOCALES_DIR = tempDirectory;
|
process.env.GRIST_LOCALES_DIR = tempDirectory;
|
||||||
await assert.isRejected(server.restart());
|
await assert.isRejected(server.restart(false, true));
|
||||||
// Wrong file format.
|
// Wrong file format.
|
||||||
fs.writeFileSync(path.join(tempDirectory, 'dummy.json'), 'invalid json');
|
fs.writeFileSync(path.join(tempDirectory, 'dummy.json'), 'invalid json');
|
||||||
await assert.isRejected(server.restart());
|
await assert.isRejected(server.restart(false, true));
|
||||||
} finally {
|
} finally {
|
||||||
oldEnv.restore();
|
oldEnv.restore();
|
||||||
await server.restart();
|
await server.restart();
|
||||||
|
@ -877,11 +877,13 @@ async function testChoices(colA: string = 'Left', colB: string = 'Right') {
|
|||||||
if (await getColumnType() === 'Choice List') {
|
if (await getColumnType() === 'Choice List') {
|
||||||
await gu.sendKeys(Key.ENTER);
|
await gu.sendKeys(Key.ENTER);
|
||||||
}
|
}
|
||||||
|
await gu.waitForServer();
|
||||||
await gu.getCell(colB, 1).click();
|
await gu.getCell(colB, 1).click();
|
||||||
await gu.sendKeys("one", Key.ENTER);
|
await gu.sendKeys("one", Key.ENTER);
|
||||||
if (await getColumnType() === 'Choice List') {
|
if (await getColumnType() === 'Choice List') {
|
||||||
await gu.sendKeys(Key.ENTER);
|
await gu.sendKeys(Key.ENTER);
|
||||||
}
|
}
|
||||||
|
await gu.waitForServer();
|
||||||
// Rename one of the choices.
|
// Rename one of the choices.
|
||||||
await selectColumns(colA, colB);
|
await selectColumns(colA, colB);
|
||||||
const undo = await gu.begin();
|
const undo = await gu.begin();
|
||||||
@ -941,6 +943,7 @@ const choiceEditor = {
|
|||||||
},
|
},
|
||||||
async save() {
|
async save() {
|
||||||
await driver.find(".test-choice-list-entry-save").click();
|
await driver.find(".test-choice-list-entry-save").click();
|
||||||
|
await gu.waitForServer();
|
||||||
},
|
},
|
||||||
async cancel() {
|
async cancel() {
|
||||||
await driver.find(".test-choice-list-entry-cancel").click();
|
await driver.find(".test-choice-list-entry-cancel").click();
|
||||||
@ -1023,12 +1026,14 @@ async function wrap(state?: boolean) {
|
|||||||
if (await buttons[0].matches('[class*=-selected]')) {
|
if (await buttons[0].matches('[class*=-selected]')) {
|
||||||
if (state === false) {
|
if (state === false) {
|
||||||
await buttons[0].click();
|
await buttons[0].click();
|
||||||
|
await gu.waitForServer();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (state === true) {
|
if (state === true) {
|
||||||
await buttons[0].click();
|
await buttons[0].click();
|
||||||
|
await gu.waitForServer();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -1106,6 +1111,7 @@ async function alignment(value?: 'left' | 'right' | 'center') {
|
|||||||
if (value === 'right') {
|
if (value === 'right') {
|
||||||
await buttons[2].click();
|
await buttons[2].click();
|
||||||
}
|
}
|
||||||
|
await gu.waitForServer();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (await buttons[0].matches('[class*=-selected]')) {
|
if (await buttons[0].matches('[class*=-selected]')) {
|
||||||
@ -1293,6 +1299,7 @@ async function numMode(value?: 'currency' | 'percent' | 'exp' | 'decimal') {
|
|||||||
await driver.findContent('.test-numeric-mode .test-select-button', /Exp/).click();
|
await driver.findContent('.test-numeric-mode .test-select-button', /Exp/).click();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
await gu.waitForServer();
|
||||||
}
|
}
|
||||||
if (mode.length === 0) {
|
if (mode.length === 0) {
|
||||||
return undefined;
|
return undefined;
|
||||||
|
@ -55,7 +55,7 @@ export class TestServerMerged implements IMochaServer {
|
|||||||
* Restart the server. If reset is set, the database is cleared. If reset is not set,
|
* Restart the server. If reset is set, the database is cleared. If reset is not set,
|
||||||
* the database is preserved, and the temporary directory is unchanged.
|
* the database is preserved, and the temporary directory is unchanged.
|
||||||
*/
|
*/
|
||||||
public async restart(reset: boolean = false) {
|
public async restart(reset: boolean = false, quiet = false) {
|
||||||
if (this.isExternalServer()) { return; }
|
if (this.isExternalServer()) { return; }
|
||||||
if (this._starts > 0) {
|
if (this._starts > 0) {
|
||||||
this.resume();
|
this.resume();
|
||||||
@ -97,7 +97,6 @@ export class TestServerMerged implements IMochaServer {
|
|||||||
const serverLog = process.env.VERBOSE ? 'inherit' : nodeLogFd;
|
const serverLog = process.env.VERBOSE ? 'inherit' : nodeLogFd;
|
||||||
const env: Record<string, string> = {
|
const env: Record<string, string> = {
|
||||||
TYPEORM_DATABASE: this._getDatabaseFile(),
|
TYPEORM_DATABASE: this._getDatabaseFile(),
|
||||||
TEST_CLEAN_DATABASE: reset ? 'true' : '',
|
|
||||||
GRIST_DATA_DIR: this.testDocDir,
|
GRIST_DATA_DIR: this.testDocDir,
|
||||||
GRIST_INST_DIR: this.testDir,
|
GRIST_INST_DIR: this.testDir,
|
||||||
// uses the test installed plugins folder as the user installed plugins.
|
// uses the test installed plugins folder as the user installed plugins.
|
||||||
@ -129,6 +128,7 @@ export class TestServerMerged implements IMochaServer {
|
|||||||
// This skips type-checking when running server, but reduces startup time a lot.
|
// This skips type-checking when running server, but reduces startup time a lot.
|
||||||
TS_NODE_TRANSPILE_ONLY: 'true',
|
TS_NODE_TRANSPILE_ONLY: 'true',
|
||||||
...process.env,
|
...process.env,
|
||||||
|
TEST_CLEAN_DATABASE: reset ? 'true' : '',
|
||||||
};
|
};
|
||||||
if (!process.env.REDIS_URL) {
|
if (!process.env.REDIS_URL) {
|
||||||
// Multiple doc workers only possible when redis is available.
|
// Multiple doc workers only possible when redis is available.
|
||||||
@ -137,7 +137,7 @@ export class TestServerMerged implements IMochaServer {
|
|||||||
}
|
}
|
||||||
this._server = spawn('node', [cmd], {
|
this._server = spawn('node', [cmd], {
|
||||||
env,
|
env,
|
||||||
stdio: ['inherit', serverLog, serverLog],
|
stdio: quiet ? 'ignore' : ['inherit', serverLog, serverLog],
|
||||||
});
|
});
|
||||||
this._exitPromise = exitPromise(this._server);
|
this._exitPromise = exitPromise(this._server);
|
||||||
|
|
||||||
@ -147,7 +147,7 @@ export class TestServerMerged implements IMochaServer {
|
|||||||
|
|
||||||
// Try to be more helpful when server exits by printing out the tail of its log.
|
// Try to be more helpful when server exits by printing out the tail of its log.
|
||||||
this._exitPromise.then((code) => {
|
this._exitPromise.then((code) => {
|
||||||
if (this._server.killed) { return; }
|
if (this._server.killed || quiet) { return; }
|
||||||
log.error("Server died unexpectedly, with code", code);
|
log.error("Server died unexpectedly, with code", code);
|
||||||
const output = execFileSync('tail', ['-30', nodeLogPath]);
|
const output = execFileSync('tail', ['-30', nodeLogPath]);
|
||||||
log.info(`\n===== BEGIN SERVER OUTPUT ====\n${output}\n===== END SERVER OUTPUT =====`);
|
log.info(`\n===== BEGIN SERVER OUTPUT ====\n${output}\n===== END SERVER OUTPUT =====`);
|
||||||
|
Loading…
Reference in New Issue
Block a user