1
0
mirror of https://github.com/lana-k/sqliteviz.git synced 2025-12-07 02:28:54 +08:00

add CSV support #27

This commit is contained in:
lana-k
2021-04-09 16:43:20 +02:00
parent c2864b4308
commit b30eeb6788
23 changed files with 1084 additions and 357 deletions

View File

@@ -4,6 +4,7 @@ import Vuex from 'vuex'
import { shallowMount } from '@vue/test-utils'
import DbUpload from '@/components/DbUpload.vue'
import fu from '@/fileUtils'
import database from '@/database.js'
describe('DbUploader.vue', () => {
afterEach(() => {
@@ -24,7 +25,10 @@ describe('DbUploader.vue', () => {
// mock db loading
const schema = {}
const $db = { loadDb: sinon.stub().resolves(schema) }
const db = {
loadDb: sinon.stub().resolves(schema)
}
database.getNewDatabase = sinon.stub().returns(db)
// mock router
const $router = { push: sinon.stub() }
@@ -33,12 +37,12 @@ describe('DbUploader.vue', () => {
// mount the component
const wrapper = shallowMount(DbUpload, {
store,
mocks: { $db, $router, $route }
mocks: { $router, $route }
})
await wrapper.find('.drop-area').trigger('click')
expect($db.loadDb.calledOnceWith(file)).to.equal(true)
await $db.loadDb.returnValues[0]
expect(db.loadDb.calledOnceWith(file)).to.equal(true)
await db.loadDb.returnValues[0]
expect(mutations.saveSchema.calledOnceWith(state, schema)).to.equal(true)
expect($router.push.calledOnceWith('/editor')).to.equal(true)
})
@@ -53,7 +57,10 @@ describe('DbUploader.vue', () => {
// mock db loading
const schema = {}
const $db = { loadDb: sinon.stub().resolves(schema) }
const db = {
loadDb: sinon.stub().resolves(schema)
}
database.getNewDatabase = sinon.stub().returns(db)
// mock router
const $router = { push: sinon.stub() }
@@ -62,7 +69,7 @@ describe('DbUploader.vue', () => {
// mount the component
const wrapper = shallowMount(DbUpload, {
store,
mocks: { $db, $router, $route }
mocks: { $router, $route }
})
// mock a file dropped by a user
@@ -74,8 +81,8 @@ describe('DbUploader.vue', () => {
})
await wrapper.find('.drop-area').trigger('drop', dropData)
expect($db.loadDb.calledOnceWith(file)).to.equal(true)
await $db.loadDb.returnValues[0]
expect(db.loadDb.calledOnceWith(file)).to.equal(true)
await db.loadDb.returnValues[0]
expect(mutations.saveSchema.calledOnceWith(state, schema)).to.equal(true)
expect($router.push.calledOnceWith('/editor')).to.equal(true)
})
@@ -94,7 +101,10 @@ describe('DbUploader.vue', () => {
// mock db loading
const schema = {}
const $db = { loadDb: sinon.stub().resolves(schema) }
const db = {
loadDb: sinon.stub().resolves(schema)
}
database.getNewDatabase = sinon.stub().returns(db)
// mock router
const $router = { push: sinon.stub() }
@@ -103,11 +113,11 @@ describe('DbUploader.vue', () => {
// mount the component
const wrapper = shallowMount(DbUpload, {
store,
mocks: { $db, $router, $route }
mocks: { $router, $route }
})
await wrapper.find('.drop-area').trigger('click')
await $db.loadDb.returnValues[0]
await db.loadDb.returnValues[0]
expect($router.push.called).to.equal(false)
})
})

View File

@@ -184,7 +184,6 @@ describe('MainMenu.vue', () => {
it('Ctrl R calls currentTab.execute if running is enabled and route.path is "/editor"',
async () => {
console.log('ctrl r')
const state = {
currentTab: {
query: 'SELECT * FROM foo',

View File

@@ -4,7 +4,6 @@ import { mount, createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
import Schema from '@/components/Schema.vue'
import TableDescription from '@/components/TableDescription.vue'
import fu from '@/fileUtils.js'
const localVue = createLocalVue()
localVue.use(Vuex)
@@ -99,75 +98,4 @@ describe('Schema.vue', () => {
expect(tables.at(1).vm.name).to.equal('bar')
expect(tables.at(2).vm.name).to.equal('foobar')
})
it('Change DB', async () => {
// mock store state and mutations
const mutations = {
saveSchema: sinon.stub()
}
const state = {
dbName: 'fooDB',
schema: [
{
name: 'foo',
columns: [
{ name: 'foo_id', type: 'INTEGER' },
{ name: 'foo_title', type: 'NVARCHAR(24)' }
]
},
{
name: 'foo_prices',
columns: [
{ name: 'foo_id', type: 'INTEGER' },
{ name: 'foo_price', type: 'INTEGER' }
]
}
]
}
const store = new Vuex.Store({ state, mutations })
// stub getFileFromUser
const file = { file: 'hello' }
sinon.stub(fu, 'getFileFromUser').resolves(file)
// mock $db.loadDb()
const newSchema = {
dbName: 'barDB',
schema: [
{
name: 'bar',
columns: [
{ name: 'bar_id', type: 'INTEGER' },
{ name: 'bar_title', type: 'NVARCHAR(24)' }
]
},
{
name: 'bar_prices',
columns: [
{ name: 'bar_id', type: 'INTEGER' },
{ name: 'bar_price', type: 'INTEGER' }
]
}
]
}
const $db = {
loadDb: sinon.stub().resolves(newSchema)
}
// mount the component
const wrapper = mount(Schema, { store, localVue, mocks: { $db } })
// trigger the event
await wrapper.find('#db-edit').trigger('click')
expect(fu.getFileFromUser.calledOnceWith('.db,.sqlite,.sqlite3')).to.equal(true)
await fu.getFileFromUser.returnValues[0]
expect($db.loadDb.calledOnceWith(file)).to.equal(true)
await $db.loadDb.returnValues[0]
expect(mutations.saveSchema.calledOnceWith(state, newSchema)).to.equal(true)
})
})

View File

@@ -145,18 +145,17 @@ describe('Tab.vue', () => {
it('Shows .result-in-progress message when executing query', (done) => {
// mock store state
const state = {
currentTabId: 1
currentTabId: 1,
db: {
execute () { return new Promise(() => {}) }
}
}
const store = new Vuex.Store({ state, mutations })
const $db = {
execute () { return new Promise(() => {}) }
}
// mount the component
const wrapper = mount(Tab, {
store,
stubs: ['chart'],
mocks: { $db },
propsData: {
id: 1,
initName: 'foo',
@@ -178,18 +177,17 @@ describe('Tab.vue', () => {
it('Shows error when executing query ends with error', async () => {
// mock store state
const state = {
currentTabId: 1
currentTabId: 1,
db: {
execute () { return Promise.reject(new Error('There is no table foo')) }
}
}
const store = new Vuex.Store({ state, mutations })
const $db = {
execute () { return Promise.reject(new Error('There is no table foo')) }
}
// mount the component
const wrapper = mount(Tab, {
store,
stubs: ['chart'],
mocks: { $db },
propsData: {
id: 1,
initName: 'foo',
@@ -210,7 +208,10 @@ describe('Tab.vue', () => {
it('Passes result to sql-table component', async () => {
// mock store state
const state = {
currentTabId: 1
currentTabId: 1,
db: {
execute () { return Promise.resolve(result) }
}
}
const store = new Vuex.Store({ state, mutations })
@@ -221,14 +222,11 @@ describe('Tab.vue', () => {
[2, 'bar']
]
}
const $db = {
execute () { return Promise.resolve(result) }
}
// mount the component
const wrapper = mount(Tab, {
store,
stubs: ['chart'],
mocks: { $db },
propsData: {
id: 1,
initName: 'foo',

View File

@@ -1,129 +1,113 @@
import { expect } from 'chai'
import chai from 'chai'
import chaiAsPromised from 'chai-as-promised'
import initSqlJs from 'sql.js'
import db from '@/database.js'
const config = {
locateFile: filename => 'js/sql-wasm.wasm'
}
import database from '@/database.js'
chai.use(chaiAsPromised)
const expect = chai.expect
chai.should()
const db = database.getNewDatabase()
const getSQL = initSqlJs()
describe('database.js', () => {
it('creates schema', () => {
return initSqlJs(config)
.then(SQL => {
const database = new SQL.Database()
database.run(`
CREATE TABLE test (
col1,
col2 integer,
col3 decimal(5,2),
col4 varchar(30)
)
`)
it('creates schema', async () => {
const SQL = await getSQL
const tempDb = new SQL.Database()
tempDb.run(`CREATE TABLE test (
col1,
col2 integer,
col3 decimal(5,2),
col4 varchar(30)
)`)
const data = database.export()
const buffer = new Blob([data])
return db.loadDb(buffer)
})
.then(({ dbName, schema }) => {
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)')
})
const data = tempDb.export()
const buffer = new Blob([data])
const { schema } = await db.loadDb(buffer)
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', () => {
return initSqlJs(config)
.then(SQL => {
const database = new SQL.Database()
database.run(`
CREATE VIRTUAL TABLE test_virtual USING fts4(
col1, col2,
notindexed=col1, notindexed=col2,
tokenize=unicode61 "tokenchars=.+#")
`)
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=.+#")
`)
const data = database.export()
const buffer = new Blob([data])
return db.loadDb(buffer)
})
.then(({ dbName, schema }) => {
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')
})
const data = tempDb.export()
const buffer = new Blob([data])
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')
})
it('returns a query result', () => {
return initSqlJs(config)
.then(SQL => {
const database = new SQL.Database()
database.run(`
CREATE TABLE test (
id integer,
name varchar(100),
faculty varchar(100)
);
INSERT INTO test (id, name, faculty)
VALUES
( 1, 'Harry Potter', 'Griffindor'),
( 2, 'Draco Malfoy', 'Slytherin');
`)
it('returns a query result', async () => {
const SQL = await getSQL
const tempDb = new SQL.Database()
tempDb.run(`
CREATE TABLE test (
id integer,
name varchar(100),
faculty varchar(100)
);
INSERT INTO test (id, name, faculty)
VALUES
( 1, 'Harry Potter', 'Griffindor'),
( 2, 'Draco Malfoy', 'Slytherin');
`)
const data = database.export()
const buffer = new Blob([data])
return db.loadDb(buffer)
})
.then(({ dbName, schema }) => {
return db.execute('SELECT * from test')
})
.then(result => {
expect(result.columns).to.have.lengthOf(3)
expect(result.columns[0]).to.equal('id')
expect(result.columns[1]).to.equal('name')
expect(result.columns[2]).to.equal('faculty')
expect(result.values).to.have.lengthOf(2)
expect(result.values[0][0]).to.equal(1)
expect(result.values[0][1]).to.equal('Harry Potter')
expect(result.values[0][2]).to.equal('Griffindor')
expect(result.values[1][0]).to.equal(2)
expect(result.values[1][1]).to.equal('Draco Malfoy')
expect(result.values[1][2]).to.equal('Slytherin')
})
const data = tempDb.export()
const buffer = new Blob([data])
await db.loadDb(buffer)
const result = await db.execute('SELECT * from test')
expect(result.columns).to.have.lengthOf(3)
expect(result.columns[0]).to.equal('id')
expect(result.columns[1]).to.equal('name')
expect(result.columns[2]).to.equal('faculty')
expect(result.values).to.have.lengthOf(2)
expect(result.values[0][0]).to.equal(1)
expect(result.values[0][1]).to.equal('Harry Potter')
expect(result.values[0][2]).to.equal('Griffindor')
expect(result.values[1][0]).to.equal(2)
expect(result.values[1][1]).to.equal('Draco Malfoy')
expect(result.values[1][2]).to.equal('Slytherin')
})
it('returns an error', () => {
return initSqlJs(config)
.then(SQL => {
const database = new SQL.Database()
database.run(`
CREATE TABLE test (
id integer,
name varchar(100),
faculty varchar(100)
);
INSERT INTO test (id, name, faculty)
VALUES
( 1, 'Harry Potter', 'Griffindor'),
( 2, 'Draco Malfoy', 'Slytherin');
`)
it('returns an error', async () => {
const SQL = await getSQL
const tempDb = new SQL.Database()
tempDb.run(`
CREATE TABLE test (
id integer,
name varchar(100),
faculty varchar(100)
);
INSERT INTO test (id, name, faculty)
VALUES
( 1, 'Harry Potter', 'Griffindor'),
( 2, 'Draco Malfoy', 'Slytherin');
`)
const data = database.export()
const buffer = new Blob([data])
return db.loadDb(buffer)
})
.then(() => {
return db.execute('SELECT * from foo')
})
.catch(result => {
expect(result).to.equal('no such table: foo')
})
const data = tempDb.export()
const buffer = new Blob([data])
await db.loadDb(buffer)
await expect(db.execute('SELECT * from foo')).to.be.rejectedWith(/^no such table: foo$/)
})
})

View File

@@ -0,0 +1,17 @@
import { expect } from 'chai'
import dbUtils from '@/dbUtils.js'
describe('dbUtils.js', () => {
it('generator', () => {
const arr = ['1', '2', '3', '4', '5']
const size = 2
const chunks = dbUtils.generateChunks(arr, size)
const output = []
for (const chunk of chunks) {
output.push(chunk)
}
expect(output[0]).to.eql(['1', '2'])
expect(output[1]).to.eql(['3', '4'])
expect(output[2]).to.eql(['5'])
})
})