(core) updates from grist-core

This commit is contained in:
Paul Fitzpatrick 2022-11-07 10:26:26 -05:00
commit b29ce996b6
7 changed files with 44 additions and 18 deletions

View File

@ -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",

View File

@ -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

View File

@ -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()

View File

@ -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();

View File

@ -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;

View File

@ -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 =====`);