1
0
mirror of https://github.com/lana-k/sqliteviz.git synced 2025-12-06 18:18:53 +08:00

CSV import as a table and db connection rework

- Add csv to existing db #32
- [RFE] Simplify working with temporary tables #53
This commit is contained in:
lana-k
2021-05-24 19:40:47 +02:00
parent c96deb5766
commit 99a10225a3
37 changed files with 1362 additions and 1169 deletions

View File

@@ -1,172 +1,26 @@
import { expect } from 'chai'
import sinon from 'sinon'
import Vuex from 'vuex'
import { shallowMount, mount } from '@vue/test-utils'
import DbUploader from '@/components/DbUploader'
import fu from '@/lib/utils/fileIo'
import database from '@/lib/database'
import csv from '@/components/DbUploader/csv'
import { mount } from '@vue/test-utils'
import CsvImport from '@/components/CsvImport'
import csv from '@/components/CsvImport/csv'
describe('DbUploader.vue', () => {
describe('CsvImport.vue', () => {
let state = {}
let mutations = {}
let store = {}
let place
beforeEach(() => {
// mock store state and mutations
state = {}
mutations = {
saveSchema: sinon.stub(),
setDb: sinon.stub()
}
store = new Vuex.Store({ state, mutations })
place = document.createElement('div')
document.body.appendChild(place)
})
afterEach(() => {
sinon.restore()
place.remove()
})
it('loads db on click and redirects to /editor', async () => {
// mock getting a file from user
const file = { name: 'test.db' }
sinon.stub(fu, 'getFileFromUser').resolves(file)
// mock db loading
const schema = {}
const db = {
loadDb: sinon.stub().resolves(schema)
}
sinon.stub(database, 'getNewDatabase').returns(db)
// mock router
const $router = { push: sinon.stub() }
const $route = { path: '/' }
// mount the component
const wrapper = shallowMount(DbUploader, {
attachTo: place,
store,
mocks: { $router, $route },
propsData: {
type: 'illustrated'
}
})
await wrapper.find('.drop-area').trigger('click')
expect(db.loadDb.calledOnceWith(file)).to.equal(true)
await db.loadDb.returnValues[0]
await wrapper.vm.animationPromise
await wrapper.vm.$nextTick()
expect(mutations.saveSchema.calledOnceWith(state, schema)).to.equal(true)
expect($router.push.calledOnceWith('/editor')).to.equal(true)
wrapper.destroy()
})
it('loads db on drop and redirects to /editor', async () => {
// mock db loading
const schema = {}
const db = {
loadDb: sinon.stub().resolves(schema)
}
sinon.stub(database, 'getNewDatabase').returns(db)
// mock router
const $router = { push: sinon.stub() }
const $route = { path: '/' }
// mount the component
const wrapper = shallowMount(DbUploader, {
attachTo: place,
store,
mocks: { $router, $route },
propsData: {
type: 'illustrated'
}
})
// mock a file dropped by a user
const file = { name: 'test.db' }
const dropData = { dataTransfer: new DataTransfer() }
Object.defineProperty(dropData.dataTransfer, 'files', {
value: [file],
writable: false
})
await wrapper.find('.drop-area').trigger('drop', dropData)
expect(db.loadDb.calledOnceWith(file)).to.equal(true)
await db.loadDb.returnValues[0]
await wrapper.vm.animationPromise
await wrapper.vm.$nextTick()
expect(mutations.saveSchema.calledOnceWith(state, schema)).to.equal(true)
expect($router.push.calledOnceWith('/editor')).to.equal(true)
wrapper.destroy()
})
it("doesn't redirect if already on /editor", async () => {
// mock getting a file from user
const file = { name: 'test.db' }
sinon.stub(fu, 'getFileFromUser').resolves(file)
// mock db loading
const schema = {}
const db = {
loadDb: sinon.stub().resolves(schema)
}
sinon.stub(database, 'getNewDatabase').returns(db)
// mock router
const $router = { push: sinon.stub() }
const $route = { path: '/editor' }
// mount the component
const wrapper = shallowMount(DbUploader, {
attachTo: place,
store,
mocks: { $router, $route },
propsData: {
type: 'illustrated'
}
})
await wrapper.find('.drop-area').trigger('click')
await db.loadDb.returnValues[0]
await wrapper.vm.animationPromise
await wrapper.vm.$nextTick()
expect($router.push.called).to.equal(false)
wrapper.destroy()
})
})
describe('DbUploader.vue import CSV', () => {
let state = {}
let mutations = {}
let actions = {}
const newTabId = 1
let mutations = {}
let store = {}
let place
// mock router
const $router = { }
const $route = { path: '/' }
let clock
let wrapper
const newTabId = 1
const file = { name: 'my data.csv' }
beforeEach(() => {
// mock getting a file from user
sinon.stub(fu, 'getFileFromUser').resolves({ type: 'text/csv', name: 'foo.csv' })
clock = sinon.useFakeTimers()
// mock store state and mutations
state = {}
mutations = {
saveSchema: sinon.stub(),
setDb: sinon.stub(),
setCurrentTabId: sinon.stub()
}
@@ -175,29 +29,32 @@ describe('DbUploader.vue import CSV', () => {
}
store = new Vuex.Store({ state, mutations, actions })
$router.push = sinon.stub()
place = document.createElement('div')
document.body.appendChild(place)
const db = {
sanitizeTableName: sinon.stub().returns('my_data'),
addTableFromCsv: sinon.stub().resolves(),
createProgressCounter: sinon.stub().returns(1),
deleteProgressCounter: sinon.stub(),
validateTableName: sinon.stub().resolves(),
execute: sinon.stub().resolves(),
refreshSchema: sinon.stub().resolves()
}
// mount the component
wrapper = mount(DbUploader, {
attachTo: place,
wrapper = mount(CsvImport, {
store,
mocks: { $router, $route },
propsData: {
type: 'illustrated'
file,
dialogName: 'addCsv',
db
}
})
})
afterEach(() => {
sinon.restore()
wrapper.destroy()
place.remove()
})
it('shows parse dialog if gets csv file', async () => {
it('previews', async () => {
sinon.stub(csv, 'parse').resolves({
delimiter: '|',
data: {
@@ -216,12 +73,11 @@ describe('DbUploader.vue import CSV', () => {
}]
})
await wrapper.find('.drop-area').trigger('click')
await csv.parse.returnValues[0]
await wrapper.vm.animationPromise
wrapper.vm.previewCsv()
await wrapper.vm.open()
await wrapper.vm.$nextTick()
await wrapper.vm.$nextTick()
expect(wrapper.find('[data-modal="parse"]').exists()).to.equal(true)
expect(wrapper.find('[data-modal="addCsv"]').exists()).to.equal(true)
expect(wrapper.find('#csv-table-name input').element.value).to.equal('my_data')
expect(wrapper.findComponent({ name: 'delimiter-selector' }).vm.value).to.equal('|')
expect(wrapper.find('#quote-char input').element.value).to.equal('"')
expect(wrapper.find('#escape-char input').element.value).to.equal('"')
@@ -252,10 +108,9 @@ describe('DbUploader.vue import CSV', () => {
}
})
await wrapper.find('.drop-area').trigger('click')
wrapper.vm.previewCsv()
wrapper.vm.open()
await csv.parse.returnValues[0]
await wrapper.vm.animationPromise
await wrapper.vm.$nextTick()
await wrapper.vm.$nextTick()
parse.onCall(1).resolves({
@@ -362,17 +217,18 @@ describe('DbUploader.vue import CSV', () => {
}
})
await wrapper.find('.drop-area').trigger('click')
await csv.parse.returnValues[0]
await wrapper.vm.animationPromise
await wrapper.vm.$nextTick()
wrapper.vm.previewCsv()
wrapper.vm.open()
await wrapper.vm.$nextTick()
let resolveParsing
parse.onCall(1).returns(new Promise(resolve => {
resolveParsing = resolve
}))
await wrapper.find('#csv-table-name input').setValue('foo')
await wrapper.find('#csv-import').trigger('click')
await wrapper.vm.$nextTick()
// "Parsing CSV..." in the logs
expect(wrapper.findComponent({ name: 'logs' }).findAll('.msg').at(1).text())
@@ -430,12 +286,16 @@ describe('DbUploader.vue import CSV', () => {
messages: []
})
await wrapper.find('.drop-area').trigger('click')
await csv.parse.returnValues[0]
await wrapper.vm.animationPromise
await wrapper.vm.$nextTick()
wrapper.vm.previewCsv()
wrapper.vm.open()
await wrapper.vm.$nextTick()
let resolveImport
wrapper.vm.db.addTableFromCsv.onCall(0).returns(new Promise(resolve => {
resolveImport = resolve
}))
await wrapper.find('#csv-table-name input').setValue('foo')
await wrapper.find('#csv-import').trigger('click')
await csv.parse.returnValues[1]
await wrapper.vm.$nextTick()
@@ -454,6 +314,7 @@ describe('DbUploader.vue import CSV', () => {
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(true)
expect(wrapper.find('#csv-finish').isVisible()).to.equal(false)
expect(wrapper.find('#csv-import').isVisible()).to.equal(true)
await resolveImport()
})
it('parsing is completed with notes', async () => {
@@ -488,12 +349,16 @@ describe('DbUploader.vue import CSV', () => {
}]
})
await wrapper.find('.drop-area').trigger('click')
await csv.parse.returnValues[0]
await wrapper.vm.animationPromise
await wrapper.vm.$nextTick()
let resolveImport
wrapper.vm.db.addTableFromCsv.onCall(0).returns(new Promise(resolve => {
resolveImport = resolve
}))
wrapper.vm.previewCsv()
wrapper.vm.open()
await wrapper.vm.$nextTick()
await wrapper.find('#csv-table-name input').setValue('foo')
await wrapper.find('#csv-import').trigger('click')
await csv.parse.returnValues[1]
await wrapper.vm.$nextTick()
@@ -514,6 +379,7 @@ describe('DbUploader.vue import CSV', () => {
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(true)
expect(wrapper.find('#csv-finish').isVisible()).to.equal(false)
expect(wrapper.find('#csv-import').isVisible()).to.equal(true)
await resolveImport()
})
it('parsing is completed with errors', async () => {
@@ -548,12 +414,11 @@ describe('DbUploader.vue import CSV', () => {
}]
})
await wrapper.find('.drop-area').trigger('click')
await csv.parse.returnValues[0]
await wrapper.vm.animationPromise
await wrapper.vm.$nextTick()
wrapper.vm.previewCsv()
wrapper.vm.open()
await wrapper.vm.$nextTick()
await wrapper.find('#csv-table-name input').setValue('foo')
await wrapper.find('#csv-import').trigger('click')
await csv.parse.returnValues[1]
await wrapper.vm.$nextTick()
@@ -604,19 +469,14 @@ describe('DbUploader.vue import CSV', () => {
})
let resolveImport = sinon.stub()
const newDb = {
importDb: sinon.stub().resolves(new Promise(resolve => { resolveImport = resolve })),
createProgressCounter: sinon.stub().returns(1),
deleteProgressCounter: sinon.stub()
}
sinon.stub(database, 'getNewDatabase').returns(newDb)
wrapper.vm.db.addTableFromCsv = sinon.stub()
.resolves(new Promise(resolve => { resolveImport = resolve }))
await wrapper.find('.drop-area').trigger('click')
await csv.parse.returnValues[0]
await wrapper.vm.animationPromise
await wrapper.vm.$nextTick()
wrapper.vm.previewCsv()
wrapper.vm.open()
await wrapper.vm.$nextTick()
await wrapper.find('#csv-table-name input').setValue('foo')
await wrapper.find('#csv-import').trigger('click')
await csv.parse.returnValues[1]
await wrapper.vm.$nextTick()
@@ -641,11 +501,11 @@ describe('DbUploader.vue import CSV', () => {
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(true)
expect(wrapper.find('#csv-finish').isVisible()).to.equal(false)
expect(wrapper.find('#csv-import').isVisible()).to.equal(true)
expect(newDb.importDb.getCall(0).args[0]).to.equal('foo') // file name
expect(wrapper.vm.db.addTableFromCsv.getCall(0).args[0]).to.equal('foo') // table name
// After resolving - loading indicator is not shown
await resolveImport()
await newDb.importDb.returnValues[0]
await wrapper.vm.db.addTableFromCsv.returnValues[0]
expect(
wrapper.findComponent({ name: 'logs' }).findComponent({ name: 'LoadingIndicator' }).exists()
).to.equal(false)
@@ -678,20 +538,11 @@ describe('DbUploader.vue import CSV', () => {
messages: []
})
const schema = {}
const newDb = {
importDb: sinon.stub().resolves(schema),
createProgressCounter: sinon.stub().returns(1),
deleteProgressCounter: sinon.stub()
}
sinon.stub(database, 'getNewDatabase').returns(newDb)
await wrapper.find('.drop-area').trigger('click')
await csv.parse.returnValues[0]
await wrapper.vm.animationPromise
await wrapper.vm.$nextTick()
wrapper.vm.previewCsv()
wrapper.vm.open()
await wrapper.vm.$nextTick()
await wrapper.find('#csv-table-name input').setValue('foo')
await wrapper.find('#csv-import').trigger('click')
await csv.parse.returnValues[1]
await wrapper.vm.$nextTick()
@@ -739,19 +590,13 @@ describe('DbUploader.vue import CSV', () => {
messages: []
})
const newDb = {
importDb: sinon.stub().rejects(new Error('fail')),
createProgressCounter: sinon.stub().returns(1),
deleteProgressCounter: sinon.stub()
}
sinon.stub(database, 'getNewDatabase').returns(newDb)
wrapper.vm.db.addTableFromCsv = sinon.stub().rejects(new Error('fail'))
await wrapper.find('.drop-area').trigger('click')
await csv.parse.returnValues[0]
await wrapper.vm.animationPromise
await wrapper.vm.$nextTick()
wrapper.vm.previewCsv()
wrapper.vm.open()
await wrapper.vm.$nextTick()
await wrapper.find('#csv-table-name input').setValue('foo')
await wrapper.find('#csv-import').trigger('click')
await csv.parse.returnValues[1]
await wrapper.vm.$nextTick()
@@ -773,7 +618,7 @@ describe('DbUploader.vue import CSV', () => {
expect(wrapper.find('#csv-finish').isVisible()).to.equal(false)
})
it('import final', async () => {
it('import finish', async () => {
sinon.stub(csv, 'parse').resolves({
delimiter: '|',
data: {
@@ -786,33 +631,20 @@ describe('DbUploader.vue import CSV', () => {
messages: []
})
const schema = {}
const newDb = {
importDb: sinon.stub().resolves(schema),
createProgressCounter: sinon.stub().returns(1),
deleteProgressCounter: sinon.stub()
}
sinon.stub(database, 'getNewDatabase').returns(newDb)
await wrapper.find('.drop-area').trigger('click')
await csv.parse.returnValues[0]
await wrapper.vm.animationPromise
await wrapper.vm.$nextTick()
wrapper.vm.previewCsv()
wrapper.vm.open()
await wrapper.vm.$nextTick()
await wrapper.find('#csv-import').trigger('click')
await csv.parse.returnValues[1]
await wrapper.vm.$nextTick()
await wrapper.find('#csv-finish').trigger('click')
expect(mutations.setDb.calledOnceWith(state, newDb)).to.equal(true)
expect(mutations.saveSchema.calledOnceWith(state, schema)).to.equal(true)
expect(actions.addTab.calledOnce).to.equal(true)
await actions.addTab.returnValues[0]
expect(mutations.setCurrentTabId.calledOnceWith(state, newTabId)).to.equal(true)
expect($router.push.calledOnceWith('/editor')).to.equal(true)
expect(wrapper.find('[data-modal="parse"]').exists()).to.equal(false)
expect(wrapper.find('[data-modal="addCsv"]').exists()).to.equal(false)
expect(wrapper.emitted('finish')).to.have.lengthOf(1)
})
it('import cancel', async () => {
@@ -828,79 +660,47 @@ describe('DbUploader.vue import CSV', () => {
messages: []
})
const schema = {}
const newDb = {
importDb: sinon.stub().resolves(schema),
createProgressCounter: sinon.stub().returns(1),
deleteProgressCounter: sinon.stub(),
shutDown: sinon.stub()
}
sinon.stub(database, 'getNewDatabase').returns(newDb)
await wrapper.find('.drop-area').trigger('click')
await csv.parse.returnValues[0]
await wrapper.vm.animationPromise
await wrapper.vm.$nextTick()
await wrapper.vm.previewCsv()
await wrapper.vm.open()
await wrapper.vm.$nextTick()
await wrapper.find('#csv-import').trigger('click')
await csv.parse.returnValues[1]
await wrapper.vm.$nextTick()
await wrapper.find('#csv-cancel').trigger('click')
expect(mutations.setDb.called).to.equal(false)
expect(mutations.saveSchema.called).to.equal(false)
expect(actions.addTab.called).to.equal(false)
expect(mutations.setCurrentTabId.called).to.equal(false)
expect($router.push.called).to.equal(false)
expect(newDb.shutDown.calledOnce).to.equal(true)
expect(wrapper.find('[data-modal="parse"]').exists()).to.equal(false)
expect(wrapper.find('[data-modal="addCsv"]').exists()).to.equal(false)
expect(wrapper.vm.db.execute.calledOnceWith('DROP TABLE "my_data"')).to.equal(true)
expect(wrapper.vm.db.refreshSchema.calledOnce).to.equal(true)
expect(wrapper.emitted('cancel')).to.have.lengthOf(1)
})
it("doesn't open new tab when load db after importing CSV", async () => {
fu.getFileFromUser.onCall(0).resolves({ type: 'text/csv', name: 'foo.csv' })
fu.getFileFromUser.onCall(1).resolves({ type: 'application/x-sqlite3', name: 'bar.sqlite3' })
sinon.stub(csv, 'parse').resolves({
delimiter: '|',
data: {
columns: ['col1', 'col2'],
values: [
[1, 'foo']
]
},
hasErrors: false,
messages: []
})
const schema = {}
const newDb = {
importDb: sinon.stub().resolves(schema),
createProgressCounter: sinon.stub().returns(1),
deleteProgressCounter: sinon.stub(),
loadDb: sinon.stub().resolves()
}
sinon.stub(database, 'getNewDatabase').returns(newDb)
await wrapper.find('.drop-area').trigger('click')
await csv.parse.returnValues[0]
await wrapper.vm.animationPromise
it('checks table name', async () => {
sinon.stub(csv, 'parse').resolves()
await wrapper.vm.previewCsv()
await wrapper.vm.open()
await wrapper.vm.$nextTick()
await wrapper.find('#csv-table-name input').setValue('foo')
await clock.tick(400)
await wrapper.vm.$nextTick()
expect(wrapper.find('#csv-table-name .text-field-error').text()).to.equal('')
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 clock.tick(400)
await wrapper.vm.$nextTick()
expect(wrapper.find('#csv-table-name .text-field-error').text())
.to.equal('this is a bad table name. Try another table name.')
await wrapper.find('#csv-table-name input').setValue('')
await clock.tick(400)
await wrapper.vm.$nextTick()
expect(wrapper.find('#csv-table-name .text-field-error').text()).to.equal('')
await wrapper.find('#csv-import').trigger('click')
await csv.parse.returnValues[1]
await wrapper.vm.$nextTick()
await wrapper.find('#csv-finish').trigger('click')
expect(actions.addTab.calledOnce).to.equal(true)
await actions.addTab.returnValues[0]
expect(mutations.setCurrentTabId.calledOnceWith(state, newTabId)).to.equal(true)
await wrapper.find('.drop-area').trigger('click')
await newDb.loadDb.returnValues[0]
expect(actions.addTab.calledOnce).to.equal(true)
expect(mutations.setCurrentTabId.calledOnce).to.equal(true)
expect(wrapper.find('#csv-table-name .text-field-error').text())
.to.equal("Table name can't be empty")
expect(wrapper.vm.db.addTableFromCsv.called).to.equal(false)
})
})

View File

@@ -1,6 +1,6 @@
import { expect } from 'chai'
import { mount, shallowMount } from '@vue/test-utils'
import DelimiterSelector from '@/components/DbUploader/DelimiterSelector'
import DelimiterSelector from '@/components/CsvImport/DelimiterSelector'
describe('DelimiterSelector', async () => {
it('shows the name of value', async () => {

View File

@@ -1,6 +1,6 @@
import { expect } from 'chai'
import sinon from 'sinon'
import csv from '@/components/DbUploader/csv'
import csv from '@/components/CsvImport/csv'
import Papa from 'papaparse'
describe('csv.js', () => {

View File

@@ -0,0 +1,199 @@
import { expect } from 'chai'
import sinon from 'sinon'
import Vuex from 'vuex'
import { shallowMount, mount } from '@vue/test-utils'
import DbUploader from '@/components/DbUploader'
import fu from '@/lib/utils/fileIo'
import database from '@/lib/database'
describe('DbUploader.vue', () => {
let state = {}
let mutations = {}
let store = {}
let place
beforeEach(() => {
// mock store state and mutations
state = {}
mutations = {
setDb: sinon.stub()
}
store = new Vuex.Store({ state, mutations })
place = document.createElement('div')
document.body.appendChild(place)
})
afterEach(() => {
sinon.restore()
place.remove()
})
it('loads db on click and redirects to /editor', async () => {
// mock getting a file from user
const file = { name: 'test.db' }
sinon.stub(fu, 'getFileFromUser').resolves(file)
// mock db loading
const db = {
loadDb: sinon.stub().resolves()
}
sinon.stub(database, 'getNewDatabase').returns(db)
// mock router
const $router = { push: sinon.stub() }
const $route = { path: '/' }
// mount the component
const wrapper = shallowMount(DbUploader, {
attachTo: place,
store,
mocks: { $router, $route },
propsData: {
type: 'illustrated'
}
})
await wrapper.find('.drop-area').trigger('click')
expect(db.loadDb.calledOnceWith(file)).to.equal(true)
await db.loadDb.returnValues[0]
await wrapper.vm.animationPromise
await wrapper.vm.$nextTick()
expect($router.push.calledOnceWith('/editor')).to.equal(true)
wrapper.destroy()
})
it('loads db on drop and redirects to /editor', async () => {
// mock db loading
const db = {
loadDb: sinon.stub().resolves()
}
sinon.stub(database, 'getNewDatabase').returns(db)
// mock router
const $router = { push: sinon.stub() }
const $route = { path: '/' }
// mount the component
const wrapper = shallowMount(DbUploader, {
attachTo: place,
store,
mocks: { $router, $route },
propsData: {
type: 'illustrated'
}
})
// mock a file dropped by a user
const file = { name: 'test.db' }
const dropData = { dataTransfer: new DataTransfer() }
Object.defineProperty(dropData.dataTransfer, 'files', {
value: [file],
writable: false
})
await wrapper.find('.drop-area').trigger('drop', dropData)
expect(db.loadDb.calledOnceWith(file)).to.equal(true)
await db.loadDb.returnValues[0]
await wrapper.vm.animationPromise
await wrapper.vm.$nextTick()
expect($router.push.calledOnceWith('/editor')).to.equal(true)
wrapper.destroy()
})
it("doesn't redirect if already on /editor", async () => {
// mock getting a file from user
const file = { name: 'test.db' }
sinon.stub(fu, 'getFileFromUser').resolves(file)
// mock db loading
const db = {
loadDb: sinon.stub().resolves()
}
sinon.stub(database, 'getNewDatabase').returns(db)
// mock router
const $router = { push: sinon.stub() }
const $route = { path: '/editor' }
// mount the component
const wrapper = shallowMount(DbUploader, {
attachTo: place,
store,
mocks: { $router, $route },
propsData: {
type: 'illustrated'
}
})
await wrapper.find('.drop-area').trigger('click')
await db.loadDb.returnValues[0]
await wrapper.vm.animationPromise
await wrapper.vm.$nextTick()
expect($router.push.called).to.equal(false)
wrapper.destroy()
})
it('shows parse dialog if gets csv file', async () => {
// mock getting a file from user
const file = { name: 'test.csv' }
sinon.stub(fu, 'getFileFromUser').resolves(file)
// mock router
const $router = { push: sinon.stub() }
const $route = { path: '/editor' }
// mount the component
const wrapper = mount(DbUploader, {
attachTo: place,
store,
mocks: { $router, $route },
propsData: {
type: 'illustrated'
}
})
const CsvImport = wrapper.vm.$refs.addCsv
sinon.stub(CsvImport, 'reset')
sinon.stub(CsvImport, 'previewCsv').resolves()
sinon.stub(CsvImport, 'open')
await wrapper.find('.drop-area').trigger('click')
await wrapper.vm.$nextTick()
expect(CsvImport.reset.calledOnce).to.equal(true)
await wrapper.vm.animationPromise
expect(CsvImport.previewCsv.calledOnce).to.equal(true)
await wrapper.vm.$nextTick()
expect(CsvImport.open.calledOnce).to.equal(true)
wrapper.destroy()
})
it('deletes temporary db if CSV import is canceled', async () => {
// mock getting a file from user
const file = { name: 'test.csv' }
sinon.stub(fu, 'getFileFromUser').resolves(file)
// mock router
const $router = { push: sinon.stub() }
const $route = { path: '/editor' }
// mount the component
const wrapper = mount(DbUploader, {
store,
mocks: { $router, $route },
propsData: {
type: 'illustrated'
}
})
const CsvImport = wrapper.vm.$refs.addCsv
sinon.stub(CsvImport, 'reset')
sinon.stub(CsvImport, 'previewCsv').resolves()
sinon.stub(CsvImport, 'open')
await wrapper.find('.drop-area').trigger('click')
await wrapper.vm.$nextTick()
await CsvImport.$emit('cancel')
expect(wrapper.vm.newDb).to.equal(null)
})
})

View File

@@ -74,8 +74,8 @@ describe('_sql.js', () => {
const progressCallback = sinon.stub()
const progressCounterId = 1
const sql = await Sql.build()
sql.import(data.columns, data.values, progressCounterId, progressCallback, 2)
const result = sql.exec('SELECT * from csv_import')
sql.import('foo', data.columns, data.values, progressCounterId, progressCallback, 2)
const result = sql.exec('SELECT * from foo')
expect(result).to.have.lengthOf(1)
expect(result[0].columns).to.eql(['id', 'name'])
expect(result[0].values).to.have.lengthOf(4)
@@ -135,7 +135,7 @@ describe('_sql.js', () => {
expect(sql.db.db).to.equal(null)
})
it('overwrites', async () => {
it('adds', async () => {
const sql = await Sql.build()
sql.exec(`
CREATE TABLE test (
@@ -160,12 +160,11 @@ describe('_sql.js', () => {
[4, 'Ron Weasley']
]
}
// rewrite the database by import
sql.import(data.columns, data.values, 1, sinon.stub(), 2)
result = sql.exec('SELECT * from csv_import')
// import adds table
sql.import('foo', data.columns, data.values, 1, sinon.stub(), 2)
result = sql.exec('SELECT * from foo')
expect(result[0].values).to.have.lengthOf(4)
// test table oesn't exists anymore: the db was overwritten
expect(() => { sql.exec('SELECT * from test') }).to.throw('no such table: test')
result = sql.exec('SELECT * from test')
expect(result[0].values).to.have.lengthOf(2)
})
})

View File

@@ -1,11 +1,11 @@
import { expect } from 'chai'
import dbUtils from '@/lib/database/_statements'
import stmts from '@/lib/database/_statements'
describe('_statements.js', () => {
it('generateChunks', () => {
const arr = ['1', '2', '3', '4', '5']
const size = 2
const chunks = dbUtils.generateChunks(arr, size)
const chunks = stmts.generateChunks(arr, size)
const output = []
for (const chunk of chunks) {
output.push(chunk)
@@ -17,8 +17,8 @@ describe('_statements.js', () => {
it('getInsertStmt', () => {
const columns = ['id', 'name']
expect(dbUtils.getInsertStmt(columns))
.to.equal('INSERT INTO csv_import ("id", "name") VALUES (?, ?);')
expect(stmts.getInsertStmt('foo', columns))
.to.equal('INSERT INTO "foo" ("id", "name") VALUES (?, ?);')
})
it('getCreateStatement', () => {
@@ -27,8 +27,36 @@ describe('_statements.js', () => {
[1, 'foo', true, new Date()],
[2, 'bar', false, new Date()]
]
expect(dbUtils.getCreateStatement(columns, values)).to.equal(
'CREATE table csv_import("id" REAL, "name" TEXT, "isAdmin" INTEGER, "startDate" TEXT);'
expect(stmts.getCreateStatement('foo', columns, values)).to.equal(
'CREATE table "foo"("id" REAL, "name" TEXT, "isAdmin" INTEGER, "startDate" TEXT);'
)
})
it('getColumns', () => {
const sql = `CREATE TABLE test (
col1,
col2 integer,
col3 decimal(5,2),
col4 varchar(30)
)`
expect(stmts.getColumns(sql)).to.eql([
{ name: 'col1', type: 'N/A' },
{ name: 'col2', type: 'integer' },
{ name: 'col3', type: 'decimal(5, 2)' },
{ name: 'col4', type: 'varchar(30)' }
])
})
it('getColumns with virtual table', async () => {
const sql = `
CREATE VIRTUAL TABLE test_virtual USING fts4(
col1, col2,
notindexed=col1, notindexed=col2,
tokenize=unicode61 "tokenchars=.+#")
`
expect(stmts.getColumns(sql)).to.eql([
{ name: 'col1', type: 'N/A' },
{ name: 'col2', type: 'N/A' }
])
})
})

View File

@@ -27,58 +27,39 @@ describe('database.js', () => {
const tempDb = new SQL.Database()
tempDb.run(`CREATE TABLE test (
col1,
col2 integer,
col3 decimal(5,2),
col4 varchar(30)
col2 integer
)`)
const data = tempDb.export()
const buffer = new Blob([data])
buffer.name = 'foo.sqlite'
const { schema, dbName } = await db.loadDb(buffer)
expect(dbName).to.equal('foo')
sinon.spy(db, 'refreshSchema')
await db.loadDb(buffer)
await db.refreshSchema.returnValues[0]
const schema = db.schema
expect(db.dbName).to.equal('foo')
expect(schema).to.have.lengthOf(1)
expect(schema[0].name).to.equal('test')
expect(schema[0].columns[0].name).to.equal('col1')
expect(schema[0].columns[0].type).to.equal('N/A')
expect(schema[0].columns[1].name).to.equal('col2')
expect(schema[0].columns[1].type).to.equal('integer')
expect(schema[0].columns[2].name).to.equal('col3')
expect(schema[0].columns[2].type).to.equal('decimal(5, 2)')
expect(schema[0].columns[3].name).to.equal('col4')
expect(schema[0].columns[3].type).to.equal('varchar(30)')
})
it('creates schema with virtual table', async () => {
const SQL = await getSQL
const tempDb = new SQL.Database()
tempDb.run(`
CREATE VIRTUAL TABLE test_virtual USING fts4(
col1, col2,
notindexed=col1, notindexed=col2,
tokenize=unicode61 "tokenchars=.+#")
`)
it('creates empty db with name database', async () => {
sinon.spy(db, 'refreshSchema')
const data = tempDb.export()
const buffer = new Blob([data])
buffer.name = 'foo.sqlite'
const { schema } = await db.loadDb(buffer)
expect(schema[0].name).to.equal('test_virtual')
expect(schema[0].columns[0].name).to.equal('col1')
expect(schema[0].columns[0].type).to.equal('N/A')
expect(schema[0].columns[1].name).to.equal('col2')
expect(schema[0].columns[1].type).to.equal('N/A')
await db.loadDb()
await db.refreshSchema.returnValues[0]
expect(db.dbName).to.equal('database')
})
it('loadDb throws errors', async () => {
const SQL = await getSQL
const tempDb = new SQL.Database()
tempDb.run('CREATE TABLE test (col1, col2)')
const data = tempDb.export()
const buffer = new Blob([data])
const buffer = new Blob([])
buffer.name = 'foo.sqlite'
sinon.stub(db.pw, 'postMessage').resolves({ error: new Error('foo') })
@@ -136,7 +117,7 @@ describe('database.js', () => {
await expect(db.execute('SELECT * from foo')).to.be.rejectedWith(/^no such table: foo$/)
})
it('creates db', async () => {
it('adds table from csv', async () => {
const data = {
columns: ['id', 'name', 'faculty'],
values: [
@@ -146,16 +127,19 @@ describe('database.js', () => {
}
const progressHandler = sinon.spy()
const progressCounterId = db.createProgressCounter(progressHandler)
const { dbName, schema } = await db.importDb('foo', data, progressCounterId)
expect(dbName).to.equal('foo')
expect(schema).to.have.lengthOf(1)
expect(schema[0].name).to.equal('csv_import')
expect(schema[0].columns).to.have.lengthOf(3)
expect(schema[0].columns[0]).to.eql({ name: 'id', type: 'real' })
expect(schema[0].columns[1]).to.eql({ name: 'name', type: 'text' })
expect(schema[0].columns[2]).to.eql({ name: 'faculty', type: 'text' })
sinon.spy(db, 'refreshSchema')
const result = await db.execute('SELECT * from csv_import')
await db.addTableFromCsv('foo', data, progressCounterId)
await db.refreshSchema.returnValues[0]
expect(db.dbName).to.equal('database')
expect(db.schema).to.have.lengthOf(1)
expect(db.schema[0].name).to.equal('foo')
expect(db.schema[0].columns).to.have.lengthOf(3)
expect(db.schema[0].columns[0]).to.eql({ name: 'id', type: 'real' })
expect(db.schema[0].columns[1]).to.eql({ name: 'name', type: 'text' })
expect(db.schema[0].columns[2]).to.eql({ name: 'faculty', type: 'text' })
const result = await db.execute('SELECT * from foo')
expect(result.columns).to.eql(data.columns)
expect(result.values).to.eql(data.values)
@@ -164,7 +148,7 @@ describe('database.js', () => {
expect(progressHandler.secondCall.calledWith(100)).to.equal(true)
})
it('importDb throws errors', async () => {
it('addTableFromCsv throws errors', async () => {
const data = {
columns: ['id', 'name'],
values: [
@@ -174,7 +158,7 @@ describe('database.js', () => {
}
const progressHandler = sinon.stub()
const progressCounterId = db.createProgressCounter(progressHandler)
await expect(db.importDb('foo', data, progressCounterId))
await expect(db.addTableFromCsv('foo', data, progressCounterId))
.to.be.rejectedWith('column index out of range')
})
@@ -242,4 +226,23 @@ describe('database.js', () => {
expect(result.values).to.have.lengthOf(1)
expect(result.values[0]).to.eql([1, 'Harry Potter'])
})
it('sanitizeTableName', () => {
let name = 'foo[]bar'
expect(db.sanitizeTableName(name)).to.equal('foo_bar')
name = '1 foo(01.05.2020)'
expect(db.sanitizeTableName(name)).to.equal('_1_foo_01_05_2020_')
})
it('validateTableName', async () => {
await db.execute('CREATE TABLE foo(id)')
await expect(db.validateTableName('foo')).to.be.rejectedWith('table "foo" already exists')
await expect(db.validateTableName('1foo'))
.to.be.rejectedWith("Table name can't start with a digit")
await expect(db.validateTableName('foo(05.08.2020)'))
.to.be.rejectedWith('Table name can contain only letters, digits and underscores')
await expect(db.validateTableName('sqlite_foo'))
.to.be.rejectedWith("Table name can't start with sqlite_")
})
})

View File

@@ -1,5 +1,5 @@
import { expect } from 'chai'
import fu from '@/lib/utils/fileIo'
import fIo from '@/lib/utils/fileIo'
import sinon from 'sinon'
describe('fileIo.js', () => {
@@ -15,7 +15,7 @@ describe('fileIo.js', () => {
sinon.spy(URL, 'revokeObjectURL')
sinon.spy(window, 'Blob')
fu.exportToFile('foo', 'foo.txt')
fIo.exportToFile('foo', 'foo.txt')
expect(document.createElement.calledOnceWith('a')).to.equal(true)
@@ -40,7 +40,7 @@ describe('fileIo.js', () => {
sinon.spy(URL, 'revokeObjectURL')
sinon.spy(window, 'Blob')
fu.exportToFile('foo', 'foo.html', 'text/html')
fIo.exportToFile('foo', 'foo.html', 'text/html')
expect(document.createElement.calledOnceWith('a')).to.equal(true)
@@ -71,7 +71,7 @@ describe('fileIo.js', () => {
setTimeout(() => { spyInput.dispatchEvent(new Event('change')) })
const data = await fu.importFile()
const data = await fIo.importFile()
expect(data).to.equal('foo')
expect(document.createElement.calledOnceWith('input')).to.equal(true)
expect(spyInput.type).to.equal('file')
@@ -82,13 +82,13 @@ describe('fileIo.js', () => {
it('readFile', () => {
sinon.spy(window, 'fetch')
fu.readFile('./foo.bar')
fIo.readFile('./foo.bar')
expect(window.fetch.calledOnceWith('./foo.bar')).to.equal(true)
})
it('readAsArrayBuffer resolves', async () => {
const blob = new Blob(['foo'])
const buffer = await fu.readAsArrayBuffer(blob)
const buffer = await fIo.readAsArrayBuffer(blob)
const uint8Array = new Uint8Array(buffer)
const text = new TextDecoder().decode(uint8Array)
@@ -103,29 +103,34 @@ describe('fileIo.js', () => {
sinon.stub(window, 'FileReader').returns(r)
const blob = new Blob(['foo'])
await expect(fu.readAsArrayBuffer(blob)).to.be.rejectedWith('Problem parsing input file.')
await expect(fIo.readAsArrayBuffer(blob)).to.be.rejectedWith('Problem parsing input file.')
})
it('isDatabase', () => {
let file = { type: 'application/vnd.sqlite3' }
expect(fu.isDatabase(file)).to.equal(true)
expect(fIo.isDatabase(file)).to.equal(true)
file = { type: 'application/x-sqlite3' }
expect(fu.isDatabase(file)).to.equal(true)
expect(fIo.isDatabase(file)).to.equal(true)
file = { type: '', name: 'test.db' }
expect(fu.isDatabase(file)).to.equal(true)
expect(fIo.isDatabase(file)).to.equal(true)
file = { type: '', name: 'test.sqlite' }
expect(fu.isDatabase(file)).to.equal(true)
expect(fIo.isDatabase(file)).to.equal(true)
file = { type: '', name: 'test.sqlite3' }
expect(fu.isDatabase(file)).to.equal(true)
expect(fIo.isDatabase(file)).to.equal(true)
file = { type: '', name: 'test.csv' }
expect(fu.isDatabase(file)).to.equal(false)
expect(fIo.isDatabase(file)).to.equal(false)
file = { type: 'text', name: 'test.db' }
expect(fu.isDatabase(file)).to.equal(false)
expect(fIo.isDatabase(file)).to.equal(false)
})
it('getFileName', () => {
expect(fIo.getFileName({ name: 'foo.csv' })).to.equal('foo')
expect(fIo.getFileName({ name: 'foo.bar.db' })).to.equal('foo.bar')
})
})

View File

@@ -2,7 +2,6 @@ import { expect } from 'chai'
import sinon from 'sinon'
import mutations from '@/store/mutations'
const {
saveSchema,
updateTab,
deleteTab,
setCurrentTabId,
@@ -24,25 +23,6 @@ describe('mutations', () => {
expect(oldDb.shutDown.calledOnce).to.equal(true)
})
it('saveSchema', () => {
const state = {}
const schema = [
{
name: 'table1',
columns: [
{ name: 'id', type: 'INTEGER' }
]
}
]
saveSchema(state, {
dbName: 'test',
schema
})
expect(state.dbName).to.equal('test')
expect(state.schema).to.eql(schema)
})
it('updateTab (save)', () => {
const tab = {
id: 1,

View File

@@ -4,6 +4,9 @@ import { mount, createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
import Schema from '@/views/Main/Editor/Schema'
import TableDescription from '@/views/Main/Editor/Schema/TableDescription'
import database from '@/lib/database'
import fIo from '@/lib/utils/fileIo'
import csv from '@/components/CsvImport/csv'
const localVue = createLocalVue()
localVue.use(Vuex)
@@ -16,7 +19,9 @@ describe('Schema.vue', () => {
it('Renders DB name on initial', () => {
// mock store state
const state = {
dbName: 'fooDB'
db: {
dbName: 'fooDB'
}
}
const store = new Vuex.Store({ state })
@@ -31,7 +36,9 @@ describe('Schema.vue', () => {
it('Schema visibility is toggled when click on DB name', async () => {
// mock store state
const state = {
dbName: 'fooDB'
db: {
dbName: 'fooDB'
}
}
const store = new Vuex.Store({ state })
@@ -48,30 +55,32 @@ describe('Schema.vue', () => {
it('Schema filter', async () => {
// mock store state
const state = {
dbName: 'fooDB',
schema: [
{
name: 'foo',
columns: [
{ name: 'id', type: 'INTEGER' },
{ name: 'title', type: 'NVARCHAR(24)' }
]
},
{
name: 'bar',
columns: [
{ name: 'id', type: 'INTEGER' },
{ name: 'price', type: 'INTEGER' }
]
},
{
name: 'foobar',
columns: [
{ name: 'id', type: 'INTEGER' },
{ name: 'price', type: 'INTEGER' }
]
}
]
db: {
dbName: 'fooDB',
schema: [
{
name: 'foo',
columns: [
{ name: 'id', type: 'INTEGER' },
{ name: 'title', type: 'NVARCHAR(24)' }
]
},
{
name: 'bar',
columns: [
{ name: 'id', type: 'INTEGER' },
{ name: 'price', type: 'INTEGER' }
]
},
{
name: 'foobar',
columns: [
{ name: 'id', type: 'INTEGER' },
{ name: 'price', type: 'INTEGER' }
]
}
]
}
}
const store = new Vuex.Store({ state })
@@ -101,15 +110,69 @@ describe('Schema.vue', () => {
it('exports db', async () => {
const state = {
dbName: 'fooDB',
db: {
dbName: 'fooDB',
export: sinon.stub().resolves()
}
}
const store = new Vuex.Store({ state })
const wrapper = mount(Schema, { store, localVue })
await wrapper.findComponent({ name: 'export-icon' }).trigger('click')
await wrapper.findComponent({ name: 'export-icon' }).find('svg').trigger('click')
expect(state.db.export.calledOnceWith('fooDB'))
})
it('adds table', async () => {
const file = { name: 'test.csv' }
sinon.stub(fIo, 'getFileFromUser').resolves(file)
sinon.stub(csv, 'parse').resolves({
delimiter: '|',
data: {
columns: ['col1', 'col2'],
values: [
[1, 'foo']
]
},
hasErrors: false,
messages: []
})
const state = {
db: database.getNewDatabase()
}
state.db.dbName = 'db'
state.db.execute('CREATE TABLE foo(id)')
state.db.refreshSchema()
sinon.spy(state.db, 'refreshSchema')
const store = new Vuex.Store({ state })
const wrapper = mount(Schema, { store, localVue })
sinon.spy(wrapper.vm.$refs.addCsv, 'previewCsv')
sinon.spy(wrapper.vm, 'addCsv')
sinon.spy(wrapper.vm.$refs.addCsv, 'loadFromCsv')
await wrapper.findComponent({ name: 'add-table-icon' }).find('svg').trigger('click')
await wrapper.vm.$refs.addCsv.previewCsv.returnValues[0]
await wrapper.vm.addCsv.returnValues[0]
await wrapper.vm.$nextTick()
await wrapper.vm.$nextTick()
expect(wrapper.find('[data-modal="addCsv"]').exists()).to.equal(true)
await wrapper.find('#csv-import').trigger('click')
await wrapper.vm.$refs.addCsv.loadFromCsv.returnValues[0]
await wrapper.find('#csv-finish').trigger('click')
expect(wrapper.find('[data-modal="addCsv"]').exists()).to.equal(false)
await state.db.refreshSchema.returnValues[0]
expect(wrapper.vm.$store.state.db.schema).to.eql([
{ name: 'test', columns: [{ name: 'col1', type: 'real' }, { name: 'col2', type: 'text' }] },
{ name: 'foo', columns: [{ name: 'id', type: 'N/A' }] }
])
const res = await wrapper.vm.$store.state.db.execute('select * from test')
expect(res).to.eql({
columns: ['col1', 'col2'],
values: [[1, 'foo']]
})
})
})

View File

@@ -7,6 +7,5 @@ describe('SqlEditor.vue', () => {
const wrapper = mount(SqlEditor)
await wrapper.findComponent({ name: 'codemirror' }).vm.$emit('input', 'SELECT * FROM foo')
expect(wrapper.emitted('input')[0]).to.eql(['SELECT * FROM foo'])
// Take a pause to keep proper state in debounced '@/views/Main/Editor/Tabs/Tab/SqlEditor/hint'
})
})

View File

@@ -11,22 +11,24 @@ describe('hint.js', () => {
it('Calculates table list for hint', () => {
// mock store state
const schema = [
{
name: 'foo',
columns: [
{ name: 'fooId', type: 'INTEGER' },
{ name: 'name', type: 'NVARCHAR(20)' }
]
},
{
name: 'bar',
columns: [
{ name: 'barId', type: 'INTEGER' }
]
}
]
sinon.stub(state, 'schema').value(schema)
const db = {
schema: [
{
name: 'foo',
columns: [
{ name: 'fooId', type: 'INTEGER' },
{ name: 'name', type: 'NVARCHAR(20)' }
]
},
{
name: 'bar',
columns: [
{ name: 'barId', type: 'INTEGER' }
]
}
]
}
sinon.stub(state, 'db').value(db)
// mock showHint and editor
sinon.stub(CM, 'showHint')
@@ -52,16 +54,18 @@ describe('hint.js', () => {
it('Add default table if there is only one table in schema', () => {
// mock store state
const schema = [
{
name: 'foo',
columns: [
{ name: 'fooId', type: 'INTEGER' },
{ name: 'name', type: 'NVARCHAR(20)' }
]
}
]
sinon.stub(state, 'schema').value(schema)
const db = {
schema: [
{
name: 'foo',
columns: [
{ name: 'fooId', type: 'INTEGER' },
{ name: 'name', type: 'NVARCHAR(20)' }
]
}
]
}
sinon.stub(state, 'db').value(db)
// mock showHint and editor
sinon.stub(CM, 'showHint')
@@ -190,7 +194,7 @@ describe('hint.js', () => {
it('tables is empty object when schema is null', () => {
// mock store state
sinon.stub(state, 'schema').value(null)
sinon.stub(state, 'db').value({ schema: null })
// mock showHint and editor
sinon.stub(CM, 'showHint')

View File

@@ -182,7 +182,8 @@ describe('Tab.vue', () => {
const state = {
currentTabId: 1,
db: {
execute: sinon.stub().rejects(new Error('There is no table foo'))
execute: sinon.stub().rejects(new Error('There is no table foo')),
refreshSchema: sinon.stub().resolves()
}
}
@@ -221,7 +222,7 @@ describe('Tab.vue', () => {
currentTabId: 1,
db: {
execute: sinon.stub().resolves(result),
getSchema: sinon.stub().resolves({ dbName: '', schema: [] })
refreshSchema: sinon.stub().resolves()
}
}
@@ -253,36 +254,17 @@ describe('Tab.vue', () => {
columns: ['id', 'name'],
values: []
}
const newSchema = {
dbName: 'fooDb',
schema: [
{
name: 'foo',
columns: [
{ name: 'id', type: 'INTEGER' },
{ name: 'title', type: 'NVARCHAR(30)' }
]
},
{
name: 'bar',
columns: [
{ name: 'a', type: 'N/A' },
{ name: 'b', type: 'N/A' }
]
}
]
}
// mock store state
const state = {
currentTabId: 1,
dbName: 'fooDb',
db: {
execute: sinon.stub().resolves(result),
getSchema: sinon.stub().resolves(newSchema)
refreshSchema: sinon.stub().resolves()
}
}
sinon.spy(mutations, 'saveSchema')
const store = new Vuex.Store({ state, mutations })
// mount the component
@@ -300,7 +282,6 @@ describe('Tab.vue', () => {
})
await wrapper.vm.execute()
expect(state.db.getSchema.calledOnceWith('fooDb')).to.equal(true)
expect(mutations.saveSchema.calledOnceWith(state, newSchema)).to.equal(true)
expect(state.db.refreshSchema.calledOnce).to.equal(true)
})
})

View File

@@ -20,7 +20,7 @@ describe('MainMenu.vue', () => {
const state = {
currentTab: { query: '', execute: sinon.stub() },
tabs: [{}],
schema: []
db: {}
}
const store = new Vuex.Store({ state })
const $route = { path: '/editor' }
@@ -49,7 +49,7 @@ describe('MainMenu.vue', () => {
const state = {
currentTab: null,
tabs: [{}],
schema: []
db: {}
}
const store = new Vuex.Store({ state })
const $route = { path: '/editor' }
@@ -65,11 +65,11 @@ describe('MainMenu.vue', () => {
expect(wrapper.find('#create-btn').isVisible()).to.equal(true)
})
it('Run is disabled if there is no schema or no query', async () => {
it('Run is disabled if there is no db or no query', async () => {
const state = {
currentTab: { query: 'SELECT * FROM foo', execute: sinon.stub() },
tabs: [{}],
schema: null
db: null
}
const store = new Vuex.Store({ state })
const $route = { path: '/editor' }
@@ -82,7 +82,7 @@ describe('MainMenu.vue', () => {
const vm = wrapper.vm
expect(wrapper.find('#run-btn').element.disabled).to.equal(true)
await vm.$set(state, 'schema', [])
await vm.$set(state, 'db', {})
expect(wrapper.find('#run-btn').element.disabled).to.equal(false)
await vm.$set(state.currentTab, 'query', '')
@@ -97,7 +97,7 @@ describe('MainMenu.vue', () => {
tabIndex: 0
},
tabs: [{ isUnsaved: true }],
schema: null
db: {}
}
const store = new Vuex.Store({ state })
const $route = { path: '/editor' }
@@ -122,7 +122,7 @@ describe('MainMenu.vue', () => {
tabIndex: 0
},
tabs: [{ isUnsaved: true }],
schema: null
db: {}
}
const newQueryId = 1
const actions = {
@@ -156,7 +156,7 @@ describe('MainMenu.vue', () => {
tabIndex: 0
},
tabs: [{ isUnsaved: true }],
schema: null
db: {}
}
const newQueryId = 1
const actions = {
@@ -191,7 +191,7 @@ describe('MainMenu.vue', () => {
tabIndex: 0
},
tabs: [{ isUnsaved: true }],
schema: []
db: {}
}
const store = new Vuex.Store({ state })
const $route = { path: '/editor' }
@@ -212,14 +212,14 @@ describe('MainMenu.vue', () => {
expect(state.currentTab.execute.calledTwice).to.equal(true)
// Running is disabled and route path is editor
await wrapper.vm.$set(state, 'schema', null)
await wrapper.vm.$set(state, 'db', null)
document.dispatchEvent(ctrlR)
expect(state.currentTab.execute.calledTwice).to.equal(true)
document.dispatchEvent(metaR)
expect(state.currentTab.execute.calledTwice).to.equal(true)
// Running is enabled and route path is not editor
await wrapper.vm.$set(state, 'schema', [])
await wrapper.vm.$set(state, 'db', {})
await wrapper.vm.$set($route, 'path', '/my-queries')
document.dispatchEvent(ctrlR)
expect(state.currentTab.execute.calledTwice).to.equal(true)
@@ -236,7 +236,7 @@ describe('MainMenu.vue', () => {
tabIndex: 0
},
tabs: [{ isUnsaved: true }],
schema: []
db: {}
}
const store = new Vuex.Store({ state })
const $route = { path: '/editor' }
@@ -257,14 +257,14 @@ describe('MainMenu.vue', () => {
expect(state.currentTab.execute.calledTwice).to.equal(true)
// Running is disabled and route path is editor
await wrapper.vm.$set(state, 'schema', null)
await wrapper.vm.$set(state, 'db', null)
document.dispatchEvent(ctrlEnter)
expect(state.currentTab.execute.calledTwice).to.equal(true)
document.dispatchEvent(metaEnter)
expect(state.currentTab.execute.calledTwice).to.equal(true)
// Running is enabled and route path is not editor
await wrapper.vm.$set(state, 'schema', [])
await wrapper.vm.$set(state, 'db', {})
await wrapper.vm.$set($route, 'path', '/my-queries')
document.dispatchEvent(ctrlEnter)
expect(state.currentTab.execute.calledTwice).to.equal(true)
@@ -280,7 +280,7 @@ describe('MainMenu.vue', () => {
tabIndex: 0
},
tabs: [{ isUnsaved: true }],
schema: []
db: {}
}
const store = new Vuex.Store({ state })
const $route = { path: '/editor' }
@@ -315,7 +315,7 @@ describe('MainMenu.vue', () => {
tabIndex: 0
},
tabs: [{ isUnsaved: true }],
schema: []
db: {}
}
const store = new Vuex.Store({ state })
const $route = { path: '/editor' }
@@ -360,7 +360,7 @@ describe('MainMenu.vue', () => {
tabIndex: 0
},
tabs: [{ id: 1, name: 'foo', isUnsaved: true }],
schema: []
db: {}
}
const mutations = {
updateTab: sinon.stub()
@@ -411,7 +411,7 @@ describe('MainMenu.vue', () => {
tabIndex: 0
},
tabs: [{ id: 1, name: null, tempName: 'Untitled', isUnsaved: true }],
schema: []
db: {}
}
const mutations = {
updateTab: sinon.stub()
@@ -456,7 +456,7 @@ describe('MainMenu.vue', () => {
tabIndex: 0
},
tabs: [{ id: 1, name: null, tempName: 'Untitled', isUnsaved: true }],
schema: []
db: {}
}
const mutations = {
updateTab: sinon.stub()
@@ -528,7 +528,7 @@ describe('MainMenu.vue', () => {
view: 'chart'
},
tabs: [{ id: 1, name: 'foo', isUnsaved: true, isPredefined: true }],
schema: []
db: {}
}
const mutations = {
updateTab: sinon.stub()
@@ -607,7 +607,7 @@ describe('MainMenu.vue', () => {
tabIndex: 0
},
tabs: [{ id: 1, name: null, tempName: 'Untitled', isUnsaved: true }],
schema: []
db: {}
}
const mutations = {
updateTab: sinon.stub()