mirror of
https://github.com/lana-k/sqliteviz.git
synced 2025-12-07 02:28:54 +08:00
Compare commits
2 Commits
3f6427ff0e
...
3893a66f4e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3893a66f4e | ||
|
|
1b6b7c71e9 |
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "sqliteviz",
|
"name": "sqliteviz",
|
||||||
"version": "0.24.1",
|
"version": "0.25.0",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -8,8 +8,8 @@
|
|||||||
:clickToClose="false"
|
:clickToClose="false"
|
||||||
>
|
>
|
||||||
<div class="dialog-header">
|
<div class="dialog-header">
|
||||||
CSV import
|
{{ typeName }} import
|
||||||
<close-icon @click="cancelCsvImport" :disabled="disableDialog"/>
|
<close-icon @click="cancelImport" :disabled="disableDialog"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="dialog-body">
|
<div class="dialog-body">
|
||||||
<text-field
|
<text-field
|
||||||
@@ -18,15 +18,15 @@
|
|||||||
width="484px"
|
width="484px"
|
||||||
:disabled="disableDialog"
|
:disabled="disableDialog"
|
||||||
:error-msg="tableNameError"
|
:error-msg="tableNameError"
|
||||||
id="csv-table-name"
|
id="csv-json-table-name"
|
||||||
/>
|
/>
|
||||||
<div class="chars">
|
<div v-if="!isJson && !isNdJson" class="chars">
|
||||||
<delimiter-selector
|
<delimiter-selector
|
||||||
v-model="delimiter"
|
v-model="delimiter"
|
||||||
width="210px"
|
width="210px"
|
||||||
class="char-input"
|
class="char-input"
|
||||||
@input="previewCsv"
|
|
||||||
:disabled="disableDialog"
|
:disabled="disableDialog"
|
||||||
|
@input="preview"
|
||||||
/>
|
/>
|
||||||
<text-field
|
<text-field
|
||||||
label="Quote char"
|
label="Quote char"
|
||||||
@@ -36,6 +36,7 @@
|
|||||||
:disabled="disableDialog"
|
:disabled="disableDialog"
|
||||||
class="char-input"
|
class="char-input"
|
||||||
id="quote-char"
|
id="quote-char"
|
||||||
|
@input="preview"
|
||||||
/>
|
/>
|
||||||
<text-field
|
<text-field
|
||||||
label="Escape char"
|
label="Escape char"
|
||||||
@@ -49,52 +50,52 @@
|
|||||||
:disabled="disableDialog"
|
:disabled="disableDialog"
|
||||||
class="char-input"
|
class="char-input"
|
||||||
id="escape-char"
|
id="escape-char"
|
||||||
|
@input="preview"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<check-box
|
<check-box
|
||||||
@click="header = $event"
|
v-if="!isJson && !isNdJson"
|
||||||
:init="true"
|
:init="header"
|
||||||
label="Use first row as column headers"
|
label="Use first row as column headers"
|
||||||
:disabled="disableDialog"
|
:disabled="disableDialog"
|
||||||
|
@click="changeHeaderDisplaying"
|
||||||
/>
|
/>
|
||||||
<sql-table
|
<sql-table
|
||||||
v-if="previewData
|
v-if="previewData && previewData.rowCount > 0"
|
||||||
&& (previewData.rowCount > 0 || Object.keys(previewData).length > 0)
|
|
||||||
"
|
|
||||||
:data-set="previewData"
|
:data-set="previewData"
|
||||||
class="preview-table"
|
|
||||||
:preview="true"
|
:preview="true"
|
||||||
|
class="preview-table"
|
||||||
/>
|
/>
|
||||||
<div v-else class="no-data">No data</div>
|
<div v-else class="no-data">No data</div>
|
||||||
<logs
|
<logs
|
||||||
class="import-csv-errors"
|
class="import-errors"
|
||||||
:messages="importCsvMessages"
|
:messages="importMessages"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="dialog-buttons-container">
|
<div class="dialog-buttons-container">
|
||||||
<button
|
<button
|
||||||
class="secondary"
|
class="secondary"
|
||||||
:disabled="disableDialog"
|
:disabled="disableDialog"
|
||||||
@click="cancelCsvImport"
|
@click="cancelImport"
|
||||||
id="csv-cancel"
|
id="import-cancel"
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
v-show="!importCsvCompleted"
|
v-show="!importCompleted"
|
||||||
class="primary"
|
class="primary"
|
||||||
:disabled="disableDialog"
|
:disabled="disableDialog || disableImport"
|
||||||
@click="loadFromCsv(file)"
|
@click="loadToDb(file)"
|
||||||
id="csv-import"
|
id="import-start"
|
||||||
>
|
>
|
||||||
Import
|
Import
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
v-show="importCsvCompleted"
|
v-show="importCompleted"
|
||||||
class="primary"
|
class="primary"
|
||||||
:disabled="disableDialog"
|
:disabled="disableDialog"
|
||||||
@click="finish"
|
@click="finish"
|
||||||
id="csv-finish"
|
id="import-finish"
|
||||||
>
|
>
|
||||||
Finish
|
Finish
|
||||||
</button>
|
</button>
|
||||||
@@ -115,7 +116,7 @@ import fIo from '@/lib/utils/fileIo'
|
|||||||
import events from '@/lib/utils/events'
|
import events from '@/lib/utils/events'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'CsvImport',
|
name: 'CsvJsonImport',
|
||||||
components: {
|
components: {
|
||||||
CloseIcon,
|
CloseIcon,
|
||||||
TextField,
|
TextField,
|
||||||
@@ -124,33 +125,50 @@ export default {
|
|||||||
SqlTable,
|
SqlTable,
|
||||||
Logs
|
Logs
|
||||||
},
|
},
|
||||||
props: ['file', 'db', 'dialogName'],
|
props: {
|
||||||
|
file: File,
|
||||||
|
db: Object,
|
||||||
|
dialogName: String
|
||||||
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
disableDialog: false,
|
disableDialog: false,
|
||||||
|
disableImport: false,
|
||||||
tableName: '',
|
tableName: '',
|
||||||
delimiter: '',
|
delimiter: '',
|
||||||
quoteChar: '"',
|
quoteChar: '"',
|
||||||
escapeChar: '"',
|
escapeChar: '"',
|
||||||
header: true,
|
header: true,
|
||||||
importCsvCompleted: false,
|
importCompleted: false,
|
||||||
importCsvMessages: [],
|
importMessages: [],
|
||||||
previewData: null,
|
previewData: null,
|
||||||
addedTable: null,
|
addedTable: null,
|
||||||
tableNameError: ''
|
tableNameError: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
isJson () {
|
||||||
|
return fIo.isJSON(this.file)
|
||||||
|
},
|
||||||
|
isNdJson () {
|
||||||
|
return fIo.isNDJSON(this.file)
|
||||||
|
},
|
||||||
|
typeName () {
|
||||||
|
return this.isJson || this.isNdJson ? 'JSON' : 'CSV'
|
||||||
|
}
|
||||||
|
},
|
||||||
watch: {
|
watch: {
|
||||||
quoteChar () {
|
isJson () {
|
||||||
this.previewCsv()
|
if (this.isJson) {
|
||||||
|
this.delimiter = '\u001E'
|
||||||
|
this.header = false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
isNdJson () {
|
||||||
escapeChar () {
|
if (this.isNdJson) {
|
||||||
this.previewCsv()
|
this.delimiter = '\u001E'
|
||||||
},
|
this.header = false
|
||||||
|
}
|
||||||
header () {
|
|
||||||
this.previewCsv()
|
|
||||||
},
|
},
|
||||||
tableName: time.debounce(function () {
|
tableName: time.debounce(function () {
|
||||||
this.tableNameError = ''
|
this.tableNameError = ''
|
||||||
@@ -164,7 +182,11 @@ export default {
|
|||||||
}, 400)
|
}, 400)
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
cancelCsvImport () {
|
changeHeaderDisplaying (e) {
|
||||||
|
this.header = e
|
||||||
|
this.preview()
|
||||||
|
},
|
||||||
|
cancelImport () {
|
||||||
if (!this.disableDialog) {
|
if (!this.disableDialog) {
|
||||||
if (this.addedTable) {
|
if (this.addedTable) {
|
||||||
this.db.execute(`DROP TABLE "${this.addedTable}"`)
|
this.db.execute(`DROP TABLE "${this.addedTable}"`)
|
||||||
@@ -175,14 +197,15 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
reset () {
|
reset () {
|
||||||
this.header = true
|
this.header = !this.isJson && !this.isNdJson
|
||||||
this.quoteChar = '"'
|
this.quoteChar = '"'
|
||||||
this.escapeChar = '"'
|
this.escapeChar = '"'
|
||||||
this.delimiter = ''
|
this.delimiter = !this.isJson && !this.isNdJson ? '' : '\u001E'
|
||||||
this.tableName = ''
|
this.tableName = ''
|
||||||
this.disableDialog = false
|
this.disableDialog = false
|
||||||
this.importCsvCompleted = false
|
this.disableImport = false
|
||||||
this.importCsvMessages = []
|
this.importCompleted = false
|
||||||
|
this.importMessages = []
|
||||||
this.previewData = null
|
this.previewData = null
|
||||||
this.addedTable = null
|
this.addedTable = null
|
||||||
this.tableNameError = ''
|
this.tableNameError = ''
|
||||||
@@ -191,39 +214,69 @@ export default {
|
|||||||
this.tableName = this.db.sanitizeTableName(fIo.getFileName(this.file))
|
this.tableName = this.db.sanitizeTableName(fIo.getFileName(this.file))
|
||||||
this.$modal.show(this.dialogName)
|
this.$modal.show(this.dialogName)
|
||||||
},
|
},
|
||||||
async previewCsv () {
|
async preview () {
|
||||||
this.importCsvCompleted = false
|
this.disableImport = false
|
||||||
|
if (!this.file) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.importCompleted = false
|
||||||
const config = {
|
const config = {
|
||||||
preview: 3,
|
preview: 3,
|
||||||
quoteChar: this.quoteChar || '"',
|
quoteChar: this.quoteChar || '"',
|
||||||
escapeChar: this.escapeChar,
|
escapeChar: this.escapeChar,
|
||||||
header: this.header,
|
header: this.header,
|
||||||
delimiter: this.delimiter
|
delimiter: this.delimiter,
|
||||||
|
columns: !this.isJson && !this.isNdJson ? null : ['doc']
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const start = new Date()
|
const start = new Date()
|
||||||
const parseResult = await csv.parse(this.file, config)
|
const parseResult = this.isJson
|
||||||
|
? await this.getJsonParseResult(this.file)
|
||||||
|
: await csv.parse(this.file, config)
|
||||||
const end = new Date()
|
const end = new Date()
|
||||||
this.previewData = parseResult.data
|
this.previewData = parseResult.data
|
||||||
|
this.previewData.rowCount = parseResult.rowCount
|
||||||
this.delimiter = parseResult.delimiter
|
this.delimiter = parseResult.delimiter
|
||||||
|
|
||||||
// In parseResult.messages we can get parse errors
|
// In parseResult.messages we can get parse errors
|
||||||
this.importCsvMessages = parseResult.messages || []
|
this.importMessages = parseResult.messages || []
|
||||||
|
|
||||||
|
if (this.previewData.rowCount === 0) {
|
||||||
|
this.disableImport = true
|
||||||
|
this.importMessages.push({
|
||||||
|
type: 'info',
|
||||||
|
message: 'No rows to import.'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if (!parseResult.hasErrors) {
|
if (!parseResult.hasErrors) {
|
||||||
this.importCsvMessages.push({
|
this.importMessages.push({
|
||||||
message: `Preview parsing is completed in ${time.getPeriod(start, end)}.`,
|
message: `Preview parsing is completed in ${time.getPeriod(start, end)}.`,
|
||||||
type: 'success'
|
type: 'success'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.importCsvMessages = [{
|
console.error(err)
|
||||||
|
this.importMessages = [{
|
||||||
message: err,
|
message: err,
|
||||||
type: 'error'
|
type: 'error'
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async loadFromCsv (file) {
|
async getJsonParseResult (file) {
|
||||||
|
const jsonContent = await fIo.getFileContent(file)
|
||||||
|
const isEmpty = !jsonContent.trim()
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
columns: ['doc'],
|
||||||
|
values: { doc: !isEmpty ? [jsonContent] : [] }
|
||||||
|
},
|
||||||
|
hasErrors: false,
|
||||||
|
messages: [],
|
||||||
|
rowCount: +(!isEmpty)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async loadToDb (file) {
|
||||||
if (!this.tableName) {
|
if (!this.tableName) {
|
||||||
this.tableNameError = "Table name can't be empty"
|
this.tableNameError = "Table name can't be empty"
|
||||||
return
|
return
|
||||||
@@ -234,17 +287,18 @@ export default {
|
|||||||
quoteChar: this.quoteChar || '"',
|
quoteChar: this.quoteChar || '"',
|
||||||
escapeChar: this.escapeChar,
|
escapeChar: this.escapeChar,
|
||||||
header: this.header,
|
header: this.header,
|
||||||
delimiter: this.delimiter
|
delimiter: this.delimiter,
|
||||||
|
columns: !this.isJson && !this.isNdJson ? null : ['doc']
|
||||||
}
|
}
|
||||||
const parseCsvMsg = {
|
const parsingMsg = {
|
||||||
message: 'Parsing CSV...',
|
message: `Parsing ${this.typeName}...`,
|
||||||
type: 'info'
|
type: 'info'
|
||||||
}
|
}
|
||||||
this.importCsvMessages.push(parseCsvMsg)
|
this.importMessages.push(parsingMsg)
|
||||||
const parseCsvLoadingIndicator = setTimeout(() => { parseCsvMsg.type = 'loading' }, 1000)
|
const parsingLoadingIndicator = setTimeout(() => { parsingMsg.type = 'loading' }, 1000)
|
||||||
|
|
||||||
const importMsg = {
|
const importMsg = {
|
||||||
message: 'Importing CSV into a SQLite database...',
|
message: `Importing ${this.typeName} into a SQLite database...`,
|
||||||
type: 'info'
|
type: 'info'
|
||||||
}
|
}
|
||||||
let importLoadingIndicator = null
|
let importLoadingIndicator = null
|
||||||
@@ -256,27 +310,30 @@ export default {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
let start = new Date()
|
let start = new Date()
|
||||||
const parseResult = await csv.parse(this.file, config)
|
const parseResult = this.isJson
|
||||||
|
? await this.getJsonParseResult(file)
|
||||||
|
: await csv.parse(this.file, config)
|
||||||
|
|
||||||
let end = new Date()
|
let end = new Date()
|
||||||
|
|
||||||
if (!parseResult.hasErrors) {
|
if (!parseResult.hasErrors) {
|
||||||
const rowCount = parseResult.rowCount
|
const rowCount = parseResult.rowCount
|
||||||
let period = time.getPeriod(start, end)
|
let period = time.getPeriod(start, end)
|
||||||
parseCsvMsg.type = 'success'
|
parsingMsg.type = 'success'
|
||||||
|
|
||||||
if (parseResult.messages.length > 0) {
|
if (parseResult.messages.length > 0) {
|
||||||
this.importCsvMessages = this.importCsvMessages.concat(parseResult.messages)
|
this.importMessages = this.importMessages.concat(parseResult.messages)
|
||||||
parseCsvMsg.message = `${rowCount} rows are parsed in ${period}.`
|
parsingMsg.message = `${rowCount} rows are parsed in ${period}.`
|
||||||
} else {
|
} else {
|
||||||
// Inform about csv parsing success
|
// Inform about parsing success
|
||||||
parseCsvMsg.message = `${rowCount} rows are parsed successfully in ${period}.`
|
parsingMsg.message = `${rowCount} rows are parsed successfully in ${period}.`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loading indicator for csv parsing is not needed anymore
|
// Loading indicator for parsing is not needed anymore
|
||||||
clearTimeout(parseCsvLoadingIndicator)
|
clearTimeout(parsingLoadingIndicator)
|
||||||
|
|
||||||
// Add info about import start
|
// Add info about import start
|
||||||
this.importCsvMessages.push(importMsg)
|
this.importMessages.push(importMsg)
|
||||||
|
|
||||||
// Show import progress after 1 second
|
// Show import progress after 1 second
|
||||||
importLoadingIndicator = setTimeout(() => {
|
importLoadingIndicator = setTimeout(() => {
|
||||||
@@ -291,52 +348,105 @@ export default {
|
|||||||
this.addedTable = this.tableName
|
this.addedTable = this.tableName
|
||||||
// Inform about import success
|
// Inform about import success
|
||||||
period = time.getPeriod(start, end)
|
period = time.getPeriod(start, end)
|
||||||
importMsg.message = `Importing CSV into a SQLite database is completed in ${period}.`
|
importMsg.message = `Importing ${this.typeName} into a SQLite database is completed in ${period}.`
|
||||||
importMsg.type = 'success'
|
importMsg.type = 'success'
|
||||||
|
|
||||||
// Loading indicator for import is not needed anymore
|
// Loading indicator for import is not needed anymore
|
||||||
clearTimeout(importLoadingIndicator)
|
clearTimeout(importLoadingIndicator)
|
||||||
|
|
||||||
this.importCsvCompleted = true
|
this.importCompleted = true
|
||||||
} else {
|
} else {
|
||||||
parseCsvMsg.message = 'Parsing ended with errors.'
|
parsingMsg.message = 'Parsing ended with errors.'
|
||||||
parseCsvMsg.type = 'info'
|
parsingMsg.type = 'info'
|
||||||
this.importCsvMessages = this.importCsvMessages.concat(parseResult.messages)
|
this.importMessages = this.importMessages.concat(parseResult.messages)
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (parseCsvMsg.type === 'loading') {
|
console.error(err)
|
||||||
parseCsvMsg.type = 'info'
|
if (parsingMsg.type === 'loading') {
|
||||||
|
parsingMsg.type = 'info'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (importMsg.type === 'loading') {
|
if (importMsg.type === 'loading') {
|
||||||
importMsg.type = 'info'
|
importMsg.type = 'info'
|
||||||
}
|
}
|
||||||
|
|
||||||
this.importCsvMessages.push({
|
this.importMessages.push({
|
||||||
message: err,
|
message: err,
|
||||||
type: 'error'
|
type: 'error'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
clearTimeout(parseCsvLoadingIndicator)
|
clearTimeout(parsingLoadingIndicator)
|
||||||
clearTimeout(importLoadingIndicator)
|
clearTimeout(importLoadingIndicator)
|
||||||
this.db.deleteProgressCounter(progressCounterId)
|
this.db.deleteProgressCounter(progressCounterId)
|
||||||
this.disableDialog = false
|
this.disableDialog = false
|
||||||
},
|
},
|
||||||
async finish () {
|
async finish () {
|
||||||
this.$modal.hide(this.dialogName)
|
this.$modal.hide(this.dialogName)
|
||||||
const stmt = [
|
const stmt = this.getQueryExample()
|
||||||
|
const tabId = await this.$store.dispatch('addTab', { query: stmt })
|
||||||
|
this.$store.commit('setCurrentTabId', tabId)
|
||||||
|
this.importCompleted = false
|
||||||
|
this.$emit('finish')
|
||||||
|
events.send('inquiry.create', null, { auto: true })
|
||||||
|
},
|
||||||
|
getQueryExample () {
|
||||||
|
return this.isNdJson ? this.getNdJsonQueryExample()
|
||||||
|
: this.isJson ? this.getJsonQueryExample()
|
||||||
|
: [
|
||||||
'/*',
|
'/*',
|
||||||
` * Your CSV file has been imported into ${this.addedTable} table.`,
|
` * Your CSV file has been imported into ${this.addedTable} table.`,
|
||||||
' * You can run this SQL query to make all CSV records available for charting.',
|
' * You can run this SQL query to make all CSV records available for charting.',
|
||||||
' */',
|
' */',
|
||||||
`SELECT * FROM "${this.addedTable}"`
|
`SELECT * FROM "${this.addedTable}"`
|
||||||
].join('\n')
|
].join('\n')
|
||||||
const tabId = await this.$store.dispatch('addTab', { query: stmt })
|
},
|
||||||
this.$store.commit('setCurrentTabId', tabId)
|
getNdJsonQueryExample () {
|
||||||
this.importCsvCompleted = false
|
try {
|
||||||
this.$emit('finish')
|
const firstRowJson = JSON.parse(this.previewData.values.doc[0])
|
||||||
events.send('inquiry.create', null, { auto: true })
|
const firstKey = Object.keys(firstRowJson)[0]
|
||||||
|
return [
|
||||||
|
'/*',
|
||||||
|
` * Your NDJSON file has been imported into ${this.addedTable} table.`,
|
||||||
|
` * Run this SQL query to get values of property ${firstKey} and make them available for charting.`,
|
||||||
|
' */',
|
||||||
|
`SELECT doc->>'${firstKey}'`,
|
||||||
|
`FROM "${this.addedTable}"`
|
||||||
|
].join('\n')
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
return [
|
||||||
|
'/*',
|
||||||
|
` * Your NDJSON file has been imported into ${this.addedTable} table.`,
|
||||||
|
' */',
|
||||||
|
'SELECT *',
|
||||||
|
`FROM "${this.addedTable}"`
|
||||||
|
].join('\n')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getJsonQueryExample () {
|
||||||
|
try {
|
||||||
|
const firstRowJson = JSON.parse(this.previewData.values.doc[0])
|
||||||
|
const firstKey = Object.keys(firstRowJson)[0]
|
||||||
|
return [
|
||||||
|
'/*',
|
||||||
|
` * Your JSON file has been imported into ${this.addedTable} table.`,
|
||||||
|
` * Run this SQL query to get values of property ${firstKey} and make them available for charting.`,
|
||||||
|
' */',
|
||||||
|
'SELECT *',
|
||||||
|
`FROM "${this.addedTable}"`,
|
||||||
|
`JOIN json_each(doc, '$.${firstKey}')`
|
||||||
|
].join('\n')
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
return [
|
||||||
|
'/*',
|
||||||
|
` * Your NDJSON file has been imported into ${this.addedTable} table.`,
|
||||||
|
' */',
|
||||||
|
'SELECT *',
|
||||||
|
`FROM "${this.addedTable}"`
|
||||||
|
].join('\n')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -347,10 +457,14 @@ export default {
|
|||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#csv-json-table-name {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
.chars {
|
.chars {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
margin: 24px 0 20px;
|
margin: 0 0 20px;
|
||||||
}
|
}
|
||||||
.char-input {
|
.char-input {
|
||||||
margin-right: 44px;
|
margin-right: 44px;
|
||||||
@@ -359,7 +473,7 @@ export default {
|
|||||||
margin-top: 18px;
|
margin-top: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.import-csv-errors {
|
.import-errors {
|
||||||
height: 136px;
|
height: 136px;
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
@@ -41,13 +41,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="error" class="error"></div>
|
<div id="error" class="error"></div>
|
||||||
|
|
||||||
<!--Parse csv dialog -->
|
<!--Parse csv or json dialog -->
|
||||||
<csv-import
|
<csv-json-import
|
||||||
ref="addCsv"
|
ref="addCsvJson"
|
||||||
:file="file"
|
:file="file"
|
||||||
:db="newDb"
|
:db="newDb"
|
||||||
dialog-name="importFromCsv"
|
dialog-name="importFromCsvJson"
|
||||||
@cancel="cancelCsvImport"
|
@cancel="cancelImport"
|
||||||
@finish="finish"
|
@finish="finish"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
import fIo from '@/lib/utils/fileIo'
|
import fIo from '@/lib/utils/fileIo'
|
||||||
import ChangeDbIcon from '@/components/svg/changeDb'
|
import ChangeDbIcon from '@/components/svg/changeDb'
|
||||||
import database from '@/lib/database'
|
import database from '@/lib/database'
|
||||||
import CsvImport from '@/components/CsvImport'
|
import CsvJsonImport from '@/components/CsvJsonImport'
|
||||||
import events from '@/lib/utils/events'
|
import events from '@/lib/utils/events'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -79,7 +79,7 @@ export default {
|
|||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
ChangeDbIcon,
|
ChangeDbIcon,
|
||||||
CsvImport
|
CsvJsonImport
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
@@ -102,7 +102,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
cancelCsvImport () {
|
cancelImport () {
|
||||||
if (this.newDb) {
|
if (this.newDb) {
|
||||||
this.newDb.shutDown()
|
this.newDb.shutDown()
|
||||||
this.newDb = null
|
this.newDb = null
|
||||||
@@ -128,21 +128,22 @@ export default {
|
|||||||
if (fIo.isDatabase(file)) {
|
if (fIo.isDatabase(file)) {
|
||||||
this.loadDb(file)
|
this.loadDb(file)
|
||||||
} else {
|
} else {
|
||||||
|
const isJson = fIo.isJSON(file) || fIo.isNDJSON(file)
|
||||||
events.send('database.import', file.size, {
|
events.send('database.import', file.size, {
|
||||||
from: 'csv',
|
from: isJson ? 'json' : 'csv',
|
||||||
new_db: true
|
new_db: true
|
||||||
})
|
})
|
||||||
|
|
||||||
this.file = file
|
this.file = file
|
||||||
await this.$nextTick()
|
await this.$nextTick()
|
||||||
const csvImport = this.$refs.addCsv
|
const csvJsonImportModal = this.$refs.addCsvJson
|
||||||
csvImport.reset()
|
csvJsonImportModal.reset()
|
||||||
return Promise.all([csvImport.previewCsv(), this.animationPromise])
|
return Promise.all([csvJsonImportModal.preview(), this.animationPromise])
|
||||||
.then(csvImport.open)
|
.then(csvJsonImportModal.open)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
browse () {
|
browse () {
|
||||||
fIo.getFileFromUser('.db,.sqlite,.sqlite3,.csv')
|
fIo.getFileFromUser('.db,.sqlite,.sqlite3,.csv,.json,.ndjson')
|
||||||
.then(this.checkFile)
|
.then(this.checkFile)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ const hintsByCode = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
getResult (source) {
|
getResult (source, columns) {
|
||||||
const result = {
|
const result = {
|
||||||
columns: []
|
columns: columns || []
|
||||||
}
|
}
|
||||||
const values = {}
|
const values = {}
|
||||||
if (source.meta.fields) {
|
if (source.meta.fields) {
|
||||||
@@ -24,8 +24,18 @@ export default {
|
|||||||
return value
|
return value
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
} else if (columns) {
|
||||||
|
columns.forEach((col, i) => {
|
||||||
|
values[col] = source.data.map(row => {
|
||||||
|
let value = row[i]
|
||||||
|
if (value instanceof Date) {
|
||||||
|
value = value.toISOString()
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
})
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
for (let i = 0; i <= source.data[0].length - 1; i++) {
|
for (let i = 0; source.data[0] && i <= source.data[0].length - 1; i++) {
|
||||||
const colName = `col${i + 1}`
|
const colName = `col${i + 1}`
|
||||||
result.columns.push(colName)
|
result.columns.push(colName)
|
||||||
values[colName] = source.data.map(row => {
|
values[colName] = source.data.map(row => {
|
||||||
@@ -76,7 +86,7 @@ export default {
|
|||||||
let res
|
let res
|
||||||
try {
|
try {
|
||||||
res = {
|
res = {
|
||||||
data: this.getResult(results),
|
data: this.getResult(results, config.columns),
|
||||||
delimiter: results.meta.delimiter,
|
delimiter: results.meta.delimiter,
|
||||||
hasErrors: false,
|
hasErrors: false,
|
||||||
rowCount: results.data.length
|
rowCount: results.data.length
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
export default {
|
export default {
|
||||||
|
isJSON (file) {
|
||||||
|
return file && file.type === 'application/json'
|
||||||
|
},
|
||||||
|
isNDJSON (file) {
|
||||||
|
return file && file.name.endsWith('.ndjson')
|
||||||
|
},
|
||||||
isDatabase (file) {
|
isDatabase (file) {
|
||||||
const dbTypes = ['application/vnd.sqlite3', 'application/x-sqlite3']
|
const dbTypes = ['application/vnd.sqlite3', 'application/x-sqlite3']
|
||||||
return file.type
|
return file.type
|
||||||
@@ -51,16 +57,17 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
importFile () {
|
importFile () {
|
||||||
const reader = new FileReader()
|
|
||||||
|
|
||||||
return this.getFileFromUser('.json')
|
return this.getFileFromUser('.json')
|
||||||
.then(file => {
|
.then(file => {
|
||||||
return new Promise((resolve, reject) => {
|
return this.getFileContent(file)
|
||||||
reader.onload = e => {
|
|
||||||
resolve(e.target.result)
|
|
||||||
}
|
|
||||||
reader.readAsText(file)
|
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
getFileContent (file) {
|
||||||
|
const reader = new FileReader()
|
||||||
|
return new Promise(resolve => {
|
||||||
|
reader.onload = e => resolve(e.target.result)
|
||||||
|
reader.readAsText(file)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<db-uploader id="db-edit" type="small" />
|
<db-uploader id="db-edit" type="small" />
|
||||||
<export-icon tooltip="Export database" @click="exportToFile"/>
|
<export-icon tooltip="Export database" @click="exportToFile"/>
|
||||||
<add-table-icon @click="addCsv"/>
|
<add-table-icon @click="addCsvJson"/>
|
||||||
</div>
|
</div>
|
||||||
<div v-show="schemaVisible" class="schema">
|
<div v-show="schemaVisible" class="schema">
|
||||||
<table-description
|
<table-description
|
||||||
@@ -21,12 +21,12 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!--Parse csv dialog -->
|
<!--Parse csv or json dialog -->
|
||||||
<csv-import
|
<csv-json-import
|
||||||
ref="addCsv"
|
ref="addCsvJson"
|
||||||
:file="file"
|
:file="file"
|
||||||
:db="$store.state.db"
|
:db="$store.state.db"
|
||||||
dialog-name="addCsv"
|
dialog-name="addCsvJson"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -40,7 +40,7 @@ import TreeChevron from '@/components/svg/treeChevron'
|
|||||||
import DbUploader from '@/components/DbUploader'
|
import DbUploader from '@/components/DbUploader'
|
||||||
import ExportIcon from '@/components/svg/export'
|
import ExportIcon from '@/components/svg/export'
|
||||||
import AddTableIcon from '@/components/svg/addTable'
|
import AddTableIcon from '@/components/svg/addTable'
|
||||||
import CsvImport from '@/components/CsvImport'
|
import CsvJsonImport from '@/components/CsvJsonImport'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Schema',
|
name: 'Schema',
|
||||||
@@ -51,7 +51,7 @@ export default {
|
|||||||
DbUploader,
|
DbUploader,
|
||||||
ExportIcon,
|
ExportIcon,
|
||||||
AddTableIcon,
|
AddTableIcon,
|
||||||
CsvImport
|
CsvJsonImport
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
@@ -80,16 +80,17 @@ export default {
|
|||||||
exportToFile () {
|
exportToFile () {
|
||||||
this.$store.state.db.export(`${this.dbName}.sqlite`)
|
this.$store.state.db.export(`${this.dbName}.sqlite`)
|
||||||
},
|
},
|
||||||
async addCsv () {
|
async addCsvJson () {
|
||||||
this.file = await fIo.getFileFromUser('.csv')
|
this.file = await fIo.getFileFromUser('.csv,.json,.ndjson')
|
||||||
await this.$nextTick()
|
await this.$nextTick()
|
||||||
const csvImport = this.$refs.addCsv
|
const csvJsonImportModal = this.$refs.addCsvJson
|
||||||
csvImport.reset()
|
csvJsonImportModal.reset()
|
||||||
await csvImport.previewCsv()
|
await csvJsonImportModal.preview()
|
||||||
csvImport.open()
|
csvJsonImportModal.open()
|
||||||
|
|
||||||
|
const isJson = fIo.isJSON(this.file) || fIo.isNDJSON(this.file)
|
||||||
events.send('database.import', this.file.size, {
|
events.send('database.import', this.file.size, {
|
||||||
from: 'csv',
|
from: isJson ? 'json' : 'csv',
|
||||||
new_db: false
|
new_db: false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<main-menu />
|
<main-menu />
|
||||||
<keep-alive include="Workspace">
|
<keep-alive include="Workspace,Inquiries">
|
||||||
<router-view id="main-view" />
|
<router-view id="main-view" />
|
||||||
</keep-alive>
|
</keep-alive>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ import { expect } from 'chai'
|
|||||||
import sinon from 'sinon'
|
import sinon from 'sinon'
|
||||||
import Vuex from 'vuex'
|
import Vuex from 'vuex'
|
||||||
import { mount } from '@vue/test-utils'
|
import { mount } from '@vue/test-utils'
|
||||||
import CsvImport from '@/components/CsvImport'
|
import CsvJsonImport from '@/components/CsvJsonImport'
|
||||||
import csv from '@/lib/csv'
|
import csv from '@/lib/csv'
|
||||||
|
|
||||||
describe('CsvImport.vue', () => {
|
describe('CsvJsonImport.vue', () => {
|
||||||
let state = {}
|
let state = {}
|
||||||
let actions = {}
|
let actions = {}
|
||||||
let mutations = {}
|
let mutations = {}
|
||||||
@@ -40,11 +40,11 @@ describe('CsvImport.vue', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// mount the component
|
// mount the component
|
||||||
wrapper = mount(CsvImport, {
|
wrapper = mount(CsvJsonImport, {
|
||||||
store,
|
store,
|
||||||
propsData: {
|
propsData: {
|
||||||
file,
|
file,
|
||||||
dialogName: 'addCsv',
|
dialogName: 'addCsvJson',
|
||||||
db
|
db
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -74,11 +74,11 @@ describe('CsvImport.vue', () => {
|
|||||||
}]
|
}]
|
||||||
})
|
})
|
||||||
|
|
||||||
wrapper.vm.previewCsv()
|
wrapper.vm.preview()
|
||||||
await wrapper.vm.open()
|
await wrapper.vm.open()
|
||||||
await wrapper.vm.$nextTick()
|
await wrapper.vm.$nextTick()
|
||||||
expect(wrapper.find('[data-modal="addCsv"]').exists()).to.equal(true)
|
expect(wrapper.find('[data-modal="addCsvJson"]').exists()).to.equal(true)
|
||||||
expect(wrapper.find('#csv-table-name input').element.value).to.equal('my_data')
|
expect(wrapper.find('#csv-json-table-name input').element.value).to.equal('my_data')
|
||||||
expect(wrapper.findComponent({ name: 'delimiter-selector' }).vm.value).to.equal('|')
|
expect(wrapper.findComponent({ name: 'delimiter-selector' }).vm.value).to.equal('|')
|
||||||
expect(wrapper.find('#quote-char input').element.value).to.equal('"')
|
expect(wrapper.find('#quote-char input').element.value).to.equal('"')
|
||||||
expect(wrapper.find('#escape-char input').element.value).to.equal('"')
|
expect(wrapper.find('#escape-char input').element.value).to.equal('"')
|
||||||
@@ -93,8 +93,8 @@ describe('CsvImport.vue', () => {
|
|||||||
.to.include('Information about row 0. Comma was used as a standart delimiter.')
|
.to.include('Information about row 0. Comma was used as a standart delimiter.')
|
||||||
expect(wrapper.findComponent({ name: 'logs' }).text())
|
expect(wrapper.findComponent({ name: 'logs' }).text())
|
||||||
.to.include('Preview parsing is completed in')
|
.to.include('Preview parsing is completed in')
|
||||||
expect(wrapper.find('#csv-finish').isVisible()).to.equal(false)
|
expect(wrapper.find('#import-finish').isVisible()).to.equal(false)
|
||||||
expect(wrapper.find('#csv-import').isVisible()).to.equal(true)
|
expect(wrapper.find('#import-start').isVisible()).to.equal(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('reparses when parameters changes', async () => {
|
it('reparses when parameters changes', async () => {
|
||||||
@@ -111,7 +111,7 @@ describe('CsvImport.vue', () => {
|
|||||||
rowCount: 1
|
rowCount: 1
|
||||||
})
|
})
|
||||||
|
|
||||||
wrapper.vm.previewCsv()
|
wrapper.vm.preview()
|
||||||
wrapper.vm.open()
|
wrapper.vm.open()
|
||||||
await csv.parse.returnValues[0]
|
await csv.parse.returnValues[0]
|
||||||
await wrapper.vm.$nextTick()
|
await wrapper.vm.$nextTick()
|
||||||
@@ -234,7 +234,7 @@ describe('CsvImport.vue', () => {
|
|||||||
rowCount: 1
|
rowCount: 1
|
||||||
})
|
})
|
||||||
|
|
||||||
wrapper.vm.previewCsv()
|
wrapper.vm.preview()
|
||||||
wrapper.vm.open()
|
wrapper.vm.open()
|
||||||
await wrapper.vm.$nextTick()
|
await wrapper.vm.$nextTick()
|
||||||
|
|
||||||
@@ -243,8 +243,8 @@ describe('CsvImport.vue', () => {
|
|||||||
resolveParsing = resolve
|
resolveParsing = resolve
|
||||||
}))
|
}))
|
||||||
|
|
||||||
await wrapper.find('#csv-table-name input').setValue('foo')
|
await wrapper.find('#csv-json-table-name input').setValue('foo')
|
||||||
await wrapper.find('#csv-import').trigger('click')
|
await wrapper.find('#import-start').trigger('click')
|
||||||
await wrapper.vm.$nextTick()
|
await wrapper.vm.$nextTick()
|
||||||
|
|
||||||
// "Parsing CSV..." in the logs
|
// "Parsing CSV..." in the logs
|
||||||
@@ -262,11 +262,11 @@ describe('CsvImport.vue', () => {
|
|||||||
expect(wrapper.find('#quote-char input').element.disabled).to.equal(true)
|
expect(wrapper.find('#quote-char input').element.disabled).to.equal(true)
|
||||||
expect(wrapper.find('#escape-char input').element.disabled).to.equal(true)
|
expect(wrapper.find('#escape-char input').element.disabled).to.equal(true)
|
||||||
expect(wrapper.findComponent({ name: 'check-box' }).vm.disabled).to.equal(true)
|
expect(wrapper.findComponent({ name: 'check-box' }).vm.disabled).to.equal(true)
|
||||||
expect(wrapper.find('#csv-cancel').element.disabled).to.equal(true)
|
expect(wrapper.find('#import-cancel').element.disabled).to.equal(true)
|
||||||
expect(wrapper.find('#csv-finish').element.disabled).to.equal(true)
|
expect(wrapper.find('#import-finish').element.disabled).to.equal(true)
|
||||||
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(true)
|
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(true)
|
||||||
expect(wrapper.find('#csv-finish').isVisible()).to.equal(false)
|
expect(wrapper.find('#import-finish').isVisible()).to.equal(false)
|
||||||
expect(wrapper.find('#csv-import').isVisible()).to.equal(true)
|
expect(wrapper.find('#import-start').isVisible()).to.equal(true)
|
||||||
await resolveParsing()
|
await resolveParsing()
|
||||||
await parse.returnValues[1]
|
await parse.returnValues[1]
|
||||||
|
|
||||||
@@ -306,7 +306,7 @@ describe('CsvImport.vue', () => {
|
|||||||
messages: []
|
messages: []
|
||||||
})
|
})
|
||||||
|
|
||||||
wrapper.vm.previewCsv()
|
wrapper.vm.preview()
|
||||||
wrapper.vm.open()
|
wrapper.vm.open()
|
||||||
await wrapper.vm.$nextTick()
|
await wrapper.vm.$nextTick()
|
||||||
|
|
||||||
@@ -315,8 +315,8 @@ describe('CsvImport.vue', () => {
|
|||||||
resolveImport = resolve
|
resolveImport = resolve
|
||||||
}))
|
}))
|
||||||
|
|
||||||
await wrapper.find('#csv-table-name input').setValue('foo')
|
await wrapper.find('#csv-json-table-name input').setValue('foo')
|
||||||
await wrapper.find('#csv-import').trigger('click')
|
await wrapper.find('#import-start').trigger('click')
|
||||||
await csv.parse.returnValues[1]
|
await csv.parse.returnValues[1]
|
||||||
await wrapper.vm.$nextTick()
|
await wrapper.vm.$nextTick()
|
||||||
|
|
||||||
@@ -329,11 +329,11 @@ describe('CsvImport.vue', () => {
|
|||||||
expect(wrapper.find('#quote-char input').element.disabled).to.equal(true)
|
expect(wrapper.find('#quote-char input').element.disabled).to.equal(true)
|
||||||
expect(wrapper.find('#escape-char input').element.disabled).to.equal(true)
|
expect(wrapper.find('#escape-char input').element.disabled).to.equal(true)
|
||||||
expect(wrapper.findComponent({ name: 'check-box' }).vm.disabled).to.equal(true)
|
expect(wrapper.findComponent({ name: 'check-box' }).vm.disabled).to.equal(true)
|
||||||
expect(wrapper.find('#csv-cancel').element.disabled).to.equal(true)
|
expect(wrapper.find('#import-cancel').element.disabled).to.equal(true)
|
||||||
expect(wrapper.find('#csv-finish').element.disabled).to.equal(true)
|
expect(wrapper.find('#import-finish').element.disabled).to.equal(true)
|
||||||
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(true)
|
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(true)
|
||||||
expect(wrapper.find('#csv-finish').isVisible()).to.equal(false)
|
expect(wrapper.find('#import-finish').isVisible()).to.equal(false)
|
||||||
expect(wrapper.find('#csv-import').isVisible()).to.equal(true)
|
expect(wrapper.find('#import-start').isVisible()).to.equal(true)
|
||||||
await resolveImport()
|
await resolveImport()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -377,12 +377,12 @@ describe('CsvImport.vue', () => {
|
|||||||
resolveImport = resolve
|
resolveImport = resolve
|
||||||
}))
|
}))
|
||||||
|
|
||||||
wrapper.vm.previewCsv()
|
wrapper.vm.preview()
|
||||||
wrapper.vm.open()
|
wrapper.vm.open()
|
||||||
await wrapper.vm.$nextTick()
|
await wrapper.vm.$nextTick()
|
||||||
|
|
||||||
await wrapper.find('#csv-table-name input').setValue('foo')
|
await wrapper.find('#csv-json-table-name input').setValue('foo')
|
||||||
await wrapper.find('#csv-import').trigger('click')
|
await wrapper.find('#import-start').trigger('click')
|
||||||
await csv.parse.returnValues[1]
|
await csv.parse.returnValues[1]
|
||||||
await wrapper.vm.$nextTick()
|
await wrapper.vm.$nextTick()
|
||||||
|
|
||||||
@@ -397,11 +397,11 @@ describe('CsvImport.vue', () => {
|
|||||||
expect(wrapper.find('#quote-char input').element.disabled).to.equal(true)
|
expect(wrapper.find('#quote-char input').element.disabled).to.equal(true)
|
||||||
expect(wrapper.find('#escape-char input').element.disabled).to.equal(true)
|
expect(wrapper.find('#escape-char input').element.disabled).to.equal(true)
|
||||||
expect(wrapper.findComponent({ name: 'check-box' }).vm.disabled).to.equal(true)
|
expect(wrapper.findComponent({ name: 'check-box' }).vm.disabled).to.equal(true)
|
||||||
expect(wrapper.find('#csv-cancel').element.disabled).to.equal(true)
|
expect(wrapper.find('#import-cancel').element.disabled).to.equal(true)
|
||||||
expect(wrapper.find('#csv-finish').element.disabled).to.equal(true)
|
expect(wrapper.find('#import-finish').element.disabled).to.equal(true)
|
||||||
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(true)
|
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(true)
|
||||||
expect(wrapper.find('#csv-finish').isVisible()).to.equal(false)
|
expect(wrapper.find('#import-finish').isVisible()).to.equal(false)
|
||||||
expect(wrapper.find('#csv-import').isVisible()).to.equal(true)
|
expect(wrapper.find('#import-start').isVisible()).to.equal(true)
|
||||||
await resolveImport()
|
await resolveImport()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -440,12 +440,12 @@ describe('CsvImport.vue', () => {
|
|||||||
}]
|
}]
|
||||||
})
|
})
|
||||||
|
|
||||||
wrapper.vm.previewCsv()
|
wrapper.vm.preview()
|
||||||
wrapper.vm.open()
|
wrapper.vm.open()
|
||||||
await wrapper.vm.$nextTick()
|
await wrapper.vm.$nextTick()
|
||||||
|
|
||||||
await wrapper.find('#csv-table-name input').setValue('foo')
|
await wrapper.find('#csv-json-table-name input').setValue('foo')
|
||||||
await wrapper.find('#csv-import').trigger('click')
|
await wrapper.find('#import-start').trigger('click')
|
||||||
await csv.parse.returnValues[1]
|
await csv.parse.returnValues[1]
|
||||||
await wrapper.vm.$nextTick()
|
await wrapper.vm.$nextTick()
|
||||||
|
|
||||||
@@ -460,11 +460,11 @@ describe('CsvImport.vue', () => {
|
|||||||
expect(wrapper.find('#quote-char input').element.disabled).to.equal(false)
|
expect(wrapper.find('#quote-char input').element.disabled).to.equal(false)
|
||||||
expect(wrapper.find('#escape-char input').element.disabled).to.equal(false)
|
expect(wrapper.find('#escape-char input').element.disabled).to.equal(false)
|
||||||
expect(wrapper.findComponent({ name: 'check-box' }).vm.disabled).to.equal(false)
|
expect(wrapper.findComponent({ name: 'check-box' }).vm.disabled).to.equal(false)
|
||||||
expect(wrapper.find('#csv-cancel').element.disabled).to.equal(false)
|
expect(wrapper.find('#import-cancel').element.disabled).to.equal(false)
|
||||||
expect(wrapper.find('#csv-finish').element.disabled).to.equal(false)
|
expect(wrapper.find('#import-finish').element.disabled).to.equal(false)
|
||||||
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(false)
|
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(false)
|
||||||
expect(wrapper.find('#csv-finish').isVisible()).to.equal(false)
|
expect(wrapper.find('#import-finish').isVisible()).to.equal(false)
|
||||||
expect(wrapper.find('#csv-import').isVisible()).to.equal(true)
|
expect(wrapper.find('#import-start').isVisible()).to.equal(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has proper state before import is completed', async () => {
|
it('has proper state before import is completed', async () => {
|
||||||
@@ -501,12 +501,12 @@ describe('CsvImport.vue', () => {
|
|||||||
wrapper.vm.db.addTableFromCsv = sinon.stub()
|
wrapper.vm.db.addTableFromCsv = sinon.stub()
|
||||||
.resolves(new Promise(resolve => { resolveImport = resolve }))
|
.resolves(new Promise(resolve => { resolveImport = resolve }))
|
||||||
|
|
||||||
wrapper.vm.previewCsv()
|
wrapper.vm.preview()
|
||||||
wrapper.vm.open()
|
wrapper.vm.open()
|
||||||
await wrapper.vm.$nextTick()
|
await wrapper.vm.$nextTick()
|
||||||
|
|
||||||
await wrapper.find('#csv-table-name input').setValue('foo')
|
await wrapper.find('#csv-json-table-name input').setValue('foo')
|
||||||
await wrapper.find('#csv-import').trigger('click')
|
await wrapper.find('#import-start').trigger('click')
|
||||||
await csv.parse.returnValues[1]
|
await csv.parse.returnValues[1]
|
||||||
await wrapper.vm.$nextTick()
|
await wrapper.vm.$nextTick()
|
||||||
|
|
||||||
@@ -525,11 +525,11 @@ describe('CsvImport.vue', () => {
|
|||||||
expect(wrapper.find('#quote-char input').element.disabled).to.equal(true)
|
expect(wrapper.find('#quote-char input').element.disabled).to.equal(true)
|
||||||
expect(wrapper.find('#escape-char input').element.disabled).to.equal(true)
|
expect(wrapper.find('#escape-char input').element.disabled).to.equal(true)
|
||||||
expect(wrapper.findComponent({ name: 'check-box' }).vm.disabled).to.equal(true)
|
expect(wrapper.findComponent({ name: 'check-box' }).vm.disabled).to.equal(true)
|
||||||
expect(wrapper.find('#csv-cancel').element.disabled).to.equal(true)
|
expect(wrapper.find('#import-cancel').element.disabled).to.equal(true)
|
||||||
expect(wrapper.find('#csv-finish').element.disabled).to.equal(true)
|
expect(wrapper.find('#import-finish').element.disabled).to.equal(true)
|
||||||
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(true)
|
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(true)
|
||||||
expect(wrapper.find('#csv-finish').isVisible()).to.equal(false)
|
expect(wrapper.find('#import-finish').isVisible()).to.equal(false)
|
||||||
expect(wrapper.find('#csv-import').isVisible()).to.equal(true)
|
expect(wrapper.find('#import-start').isVisible()).to.equal(true)
|
||||||
expect(wrapper.vm.db.addTableFromCsv.getCall(0).args[0]).to.equal('foo') // table name
|
expect(wrapper.vm.db.addTableFromCsv.getCall(0).args[0]).to.equal('foo') // table name
|
||||||
|
|
||||||
// After resolving - loading indicator is not shown
|
// After resolving - loading indicator is not shown
|
||||||
@@ -570,12 +570,12 @@ describe('CsvImport.vue', () => {
|
|||||||
messages: []
|
messages: []
|
||||||
})
|
})
|
||||||
|
|
||||||
wrapper.vm.previewCsv()
|
wrapper.vm.preview()
|
||||||
wrapper.vm.open()
|
wrapper.vm.open()
|
||||||
await wrapper.vm.$nextTick()
|
await wrapper.vm.$nextTick()
|
||||||
|
|
||||||
await wrapper.find('#csv-table-name input').setValue('foo')
|
await wrapper.find('#csv-json-table-name input').setValue('foo')
|
||||||
await wrapper.find('#csv-import').trigger('click')
|
await wrapper.find('#import-start').trigger('click')
|
||||||
await csv.parse.returnValues[1]
|
await csv.parse.returnValues[1]
|
||||||
await wrapper.vm.$nextTick()
|
await wrapper.vm.$nextTick()
|
||||||
|
|
||||||
@@ -589,10 +589,10 @@ describe('CsvImport.vue', () => {
|
|||||||
expect(wrapper.find('#quote-char input').element.disabled).to.equal(false)
|
expect(wrapper.find('#quote-char input').element.disabled).to.equal(false)
|
||||||
expect(wrapper.find('#escape-char input').element.disabled).to.equal(false)
|
expect(wrapper.find('#escape-char input').element.disabled).to.equal(false)
|
||||||
expect(wrapper.findComponent({ name: 'check-box' }).vm.disabled).to.equal(false)
|
expect(wrapper.findComponent({ name: 'check-box' }).vm.disabled).to.equal(false)
|
||||||
expect(wrapper.find('#csv-cancel').element.disabled).to.equal(false)
|
expect(wrapper.find('#import-cancel').element.disabled).to.equal(false)
|
||||||
expect(wrapper.find('#csv-finish').element.disabled).to.equal(false)
|
expect(wrapper.find('#import-finish').element.disabled).to.equal(false)
|
||||||
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(false)
|
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(false)
|
||||||
expect(wrapper.find('#csv-finish').isVisible()).to.equal(true)
|
expect(wrapper.find('#import-finish').isVisible()).to.equal(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('import fails', async () => {
|
it('import fails', async () => {
|
||||||
@@ -627,12 +627,12 @@ describe('CsvImport.vue', () => {
|
|||||||
|
|
||||||
wrapper.vm.db.addTableFromCsv = sinon.stub().rejects(new Error('fail'))
|
wrapper.vm.db.addTableFromCsv = sinon.stub().rejects(new Error('fail'))
|
||||||
|
|
||||||
wrapper.vm.previewCsv()
|
wrapper.vm.preview()
|
||||||
wrapper.vm.open()
|
wrapper.vm.open()
|
||||||
await wrapper.vm.$nextTick()
|
await wrapper.vm.$nextTick()
|
||||||
|
|
||||||
await wrapper.find('#csv-table-name input').setValue('foo')
|
await wrapper.find('#csv-json-table-name input').setValue('foo')
|
||||||
await wrapper.find('#csv-import').trigger('click')
|
await wrapper.find('#import-start').trigger('click')
|
||||||
await csv.parse.returnValues[1]
|
await csv.parse.returnValues[1]
|
||||||
await wrapper.vm.$nextTick()
|
await wrapper.vm.$nextTick()
|
||||||
|
|
||||||
@@ -647,10 +647,10 @@ describe('CsvImport.vue', () => {
|
|||||||
expect(wrapper.find('#quote-char input').element.disabled).to.equal(false)
|
expect(wrapper.find('#quote-char input').element.disabled).to.equal(false)
|
||||||
expect(wrapper.find('#escape-char input').element.disabled).to.equal(false)
|
expect(wrapper.find('#escape-char input').element.disabled).to.equal(false)
|
||||||
expect(wrapper.findComponent({ name: 'check-box' }).vm.disabled).to.equal(false)
|
expect(wrapper.findComponent({ name: 'check-box' }).vm.disabled).to.equal(false)
|
||||||
expect(wrapper.find('#csv-cancel').element.disabled).to.equal(false)
|
expect(wrapper.find('#import-cancel').element.disabled).to.equal(false)
|
||||||
expect(wrapper.find('#csv-finish').element.disabled).to.equal(false)
|
expect(wrapper.find('#import-finish').element.disabled).to.equal(false)
|
||||||
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(false)
|
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(false)
|
||||||
expect(wrapper.find('#csv-finish').isVisible()).to.equal(false)
|
expect(wrapper.find('#import-finish').isVisible()).to.equal(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('import finish', async () => {
|
it('import finish', async () => {
|
||||||
@@ -668,19 +668,19 @@ describe('CsvImport.vue', () => {
|
|||||||
messages: []
|
messages: []
|
||||||
})
|
})
|
||||||
|
|
||||||
wrapper.vm.previewCsv()
|
wrapper.vm.preview()
|
||||||
wrapper.vm.open()
|
wrapper.vm.open()
|
||||||
await wrapper.vm.$nextTick()
|
await wrapper.vm.$nextTick()
|
||||||
|
|
||||||
await wrapper.find('#csv-import').trigger('click')
|
await wrapper.find('#import-start').trigger('click')
|
||||||
await wrapper.vm.$nextTick()
|
await wrapper.vm.$nextTick()
|
||||||
|
|
||||||
await wrapper.find('#csv-finish').trigger('click')
|
await wrapper.find('#import-finish').trigger('click')
|
||||||
|
|
||||||
expect(actions.addTab.calledOnce).to.equal(true)
|
expect(actions.addTab.calledOnce).to.equal(true)
|
||||||
await actions.addTab.returnValues[0]
|
await actions.addTab.returnValues[0]
|
||||||
expect(mutations.setCurrentTabId.calledOnceWith(state, newTabId)).to.equal(true)
|
expect(mutations.setCurrentTabId.calledOnceWith(state, newTabId)).to.equal(true)
|
||||||
expect(wrapper.find('[data-modal="addCsv"]').exists()).to.equal(false)
|
expect(wrapper.find('[data-modal="addCsvJson"]').exists()).to.equal(false)
|
||||||
expect(wrapper.emitted('finish')).to.have.lengthOf(1)
|
expect(wrapper.emitted('finish')).to.have.lengthOf(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -699,16 +699,16 @@ describe('CsvImport.vue', () => {
|
|||||||
messages: []
|
messages: []
|
||||||
})
|
})
|
||||||
|
|
||||||
await wrapper.vm.previewCsv()
|
await wrapper.vm.preview()
|
||||||
await wrapper.vm.open()
|
await wrapper.vm.open()
|
||||||
await wrapper.vm.$nextTick()
|
await wrapper.vm.$nextTick()
|
||||||
|
|
||||||
await wrapper.find('#csv-import').trigger('click')
|
await wrapper.find('#import-start').trigger('click')
|
||||||
await wrapper.vm.$nextTick()
|
await wrapper.vm.$nextTick()
|
||||||
|
|
||||||
await wrapper.find('#csv-cancel').trigger('click')
|
await wrapper.find('#import-cancel').trigger('click')
|
||||||
|
|
||||||
expect(wrapper.find('[data-modal="addCsv"]').exists()).to.equal(false)
|
expect(wrapper.find('[data-modal="addCsvJson"]').exists()).to.equal(false)
|
||||||
expect(wrapper.vm.db.execute.calledOnceWith('DROP TABLE "my_data"')).to.equal(true)
|
expect(wrapper.vm.db.execute.calledOnceWith('DROP TABLE "my_data"')).to.equal(true)
|
||||||
expect(wrapper.vm.db.refreshSchema.calledOnce).to.equal(true)
|
expect(wrapper.vm.db.refreshSchema.calledOnce).to.equal(true)
|
||||||
expect(wrapper.emitted('cancel')).to.have.lengthOf(1)
|
expect(wrapper.emitted('cancel')).to.have.lengthOf(1)
|
||||||
@@ -716,29 +716,29 @@ describe('CsvImport.vue', () => {
|
|||||||
|
|
||||||
it('checks table name', async () => {
|
it('checks table name', async () => {
|
||||||
sinon.stub(csv, 'parse').resolves()
|
sinon.stub(csv, 'parse').resolves()
|
||||||
await wrapper.vm.previewCsv()
|
await wrapper.vm.preview()
|
||||||
await wrapper.vm.open()
|
await wrapper.vm.open()
|
||||||
await wrapper.vm.$nextTick()
|
await wrapper.vm.$nextTick()
|
||||||
|
|
||||||
await wrapper.find('#csv-table-name input').setValue('foo')
|
await wrapper.find('#csv-json-table-name input').setValue('foo')
|
||||||
await clock.tick(400)
|
await clock.tick(400)
|
||||||
await wrapper.vm.$nextTick()
|
await wrapper.vm.$nextTick()
|
||||||
expect(wrapper.find('#csv-table-name .text-field-error').text()).to.equal('')
|
expect(wrapper.find('#csv-json-table-name .text-field-error').text()).to.equal('')
|
||||||
|
|
||||||
wrapper.vm.db.validateTableName = sinon.stub().rejects(new Error('this is a bad table name'))
|
wrapper.vm.db.validateTableName = sinon.stub().rejects(new Error('this is a bad table name'))
|
||||||
await wrapper.find('#csv-table-name input').setValue('bar')
|
await wrapper.find('#csv-json-table-name input').setValue('bar')
|
||||||
await clock.tick(400)
|
await clock.tick(400)
|
||||||
await wrapper.vm.$nextTick()
|
await wrapper.vm.$nextTick()
|
||||||
expect(wrapper.find('#csv-table-name .text-field-error').text())
|
expect(wrapper.find('#csv-json-table-name .text-field-error').text())
|
||||||
.to.equal('this is a bad table name. Try another table name.')
|
.to.equal('this is a bad table name. Try another table name.')
|
||||||
|
|
||||||
await wrapper.find('#csv-table-name input').setValue('')
|
await wrapper.find('#csv-json-table-name input').setValue('')
|
||||||
await clock.tick(400)
|
await clock.tick(400)
|
||||||
await wrapper.vm.$nextTick()
|
await wrapper.vm.$nextTick()
|
||||||
expect(wrapper.find('#csv-table-name .text-field-error').text()).to.equal('')
|
expect(wrapper.find('#csv-json-table-name .text-field-error').text()).to.equal('')
|
||||||
|
|
||||||
await wrapper.find('#csv-import').trigger('click')
|
await wrapper.find('#import-start').trigger('click')
|
||||||
expect(wrapper.find('#csv-table-name .text-field-error').text())
|
expect(wrapper.find('#csv-json-table-name .text-field-error').text())
|
||||||
.to.equal("Table name can't be empty")
|
.to.equal("Table name can't be empty")
|
||||||
expect(wrapper.vm.db.addTableFromCsv.called).to.equal(false)
|
expect(wrapper.vm.db.addTableFromCsv.called).to.equal(false)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { expect } from 'chai'
|
import { expect } from 'chai'
|
||||||
import { mount, shallowMount } from '@vue/test-utils'
|
import { mount, shallowMount } from '@vue/test-utils'
|
||||||
import DelimiterSelector from '@/components/CsvImport/DelimiterSelector'
|
import DelimiterSelector from '@/components/CsvJsonImport/DelimiterSelector'
|
||||||
|
|
||||||
describe('DelimiterSelector', async () => {
|
describe('DelimiterSelector', async () => {
|
||||||
it('shows the name of value', async () => {
|
it('shows the name of value', async () => {
|
||||||
|
|||||||
@@ -153,16 +153,16 @@ describe('DbUploader.vue', () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const CsvImport = wrapper.vm.$refs.addCsv
|
const CsvImport = wrapper.vm.$refs.addCsvJson
|
||||||
sinon.stub(CsvImport, 'reset')
|
sinon.stub(CsvImport, 'reset')
|
||||||
sinon.stub(CsvImport, 'previewCsv').resolves()
|
sinon.stub(CsvImport, 'preview').resolves()
|
||||||
sinon.stub(CsvImport, 'open')
|
sinon.stub(CsvImport, 'open')
|
||||||
|
|
||||||
await wrapper.find('.drop-area').trigger('click')
|
await wrapper.find('.drop-area').trigger('click')
|
||||||
await wrapper.vm.$nextTick()
|
await wrapper.vm.$nextTick()
|
||||||
expect(CsvImport.reset.calledOnce).to.equal(true)
|
expect(CsvImport.reset.calledOnce).to.equal(true)
|
||||||
await wrapper.vm.animationPromise
|
await wrapper.vm.animationPromise
|
||||||
expect(CsvImport.previewCsv.calledOnce).to.equal(true)
|
expect(CsvImport.preview.calledOnce).to.equal(true)
|
||||||
await wrapper.vm.$nextTick()
|
await wrapper.vm.$nextTick()
|
||||||
expect(CsvImport.open.calledOnce).to.equal(true)
|
expect(CsvImport.open.calledOnce).to.equal(true)
|
||||||
wrapper.destroy()
|
wrapper.destroy()
|
||||||
@@ -186,9 +186,9 @@ describe('DbUploader.vue', () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const CsvImport = wrapper.vm.$refs.addCsv
|
const CsvImport = wrapper.vm.$refs.addCsvJson
|
||||||
sinon.stub(CsvImport, 'reset')
|
sinon.stub(CsvImport, 'reset')
|
||||||
sinon.stub(CsvImport, 'previewCsv').resolves()
|
sinon.stub(CsvImport, 'preview').resolves()
|
||||||
sinon.stub(CsvImport, 'open')
|
sinon.stub(CsvImport, 'open')
|
||||||
|
|
||||||
await wrapper.find('.drop-area').trigger('click')
|
await wrapper.find('.drop-area').trigger('click')
|
||||||
|
|||||||
@@ -152,20 +152,20 @@ describe('Schema.vue', () => {
|
|||||||
|
|
||||||
const store = new Vuex.Store({ state, actions, mutations })
|
const store = new Vuex.Store({ state, actions, mutations })
|
||||||
const wrapper = mount(Schema, { store, localVue })
|
const wrapper = mount(Schema, { store, localVue })
|
||||||
sinon.spy(wrapper.vm.$refs.addCsv, 'previewCsv')
|
sinon.spy(wrapper.vm.$refs.addCsvJson, 'preview')
|
||||||
sinon.spy(wrapper.vm, 'addCsv')
|
sinon.spy(wrapper.vm, 'addCsvJson')
|
||||||
sinon.spy(wrapper.vm.$refs.addCsv, 'loadFromCsv')
|
sinon.spy(wrapper.vm.$refs.addCsvJson, 'loadToDb')
|
||||||
|
|
||||||
await wrapper.findComponent({ name: 'add-table-icon' }).find('svg').trigger('click')
|
await wrapper.findComponent({ name: 'add-table-icon' }).find('svg').trigger('click')
|
||||||
await wrapper.vm.$refs.addCsv.previewCsv.returnValues[0]
|
await wrapper.vm.$refs.addCsvJson.preview.returnValues[0]
|
||||||
await wrapper.vm.addCsv.returnValues[0]
|
await wrapper.vm.addCsvJson.returnValues[0]
|
||||||
await wrapper.vm.$nextTick()
|
await wrapper.vm.$nextTick()
|
||||||
await wrapper.vm.$nextTick()
|
await wrapper.vm.$nextTick()
|
||||||
expect(wrapper.find('[data-modal="addCsv"]').exists()).to.equal(true)
|
expect(wrapper.find('[data-modal="addCsvJson"]').exists()).to.equal(true)
|
||||||
await wrapper.find('#csv-import').trigger('click')
|
await wrapper.find('#import-start').trigger('click')
|
||||||
await wrapper.vm.$refs.addCsv.loadFromCsv.returnValues[0]
|
await wrapper.vm.$refs.addCsvJson.loadToDb.returnValues[0]
|
||||||
await wrapper.find('#csv-finish').trigger('click')
|
await wrapper.find('#import-finish').trigger('click')
|
||||||
expect(wrapper.find('[data-modal="addCsv"]').exists()).to.equal(false)
|
expect(wrapper.find('[data-modal="addCsvJson"]').exists()).to.equal(false)
|
||||||
await state.db.refreshSchema.returnValues[0]
|
await state.db.refreshSchema.returnValues[0]
|
||||||
|
|
||||||
expect(wrapper.vm.$store.state.db.schema).to.eql([
|
expect(wrapper.vm.$store.state.db.schema).to.eql([
|
||||||
|
|||||||
Reference in New Issue
Block a user