move tests to tests folder

rename util modules
rename DbUpload to DbUploader
add tests for DbUploader component #27
This commit is contained in:
lana-k committed 2021-04-21 11:05:56 +02:00
1 parent edd45b317a
commit 803622f18f
44 files changed
+333 -157

No files matched your search

+1 -1
View File
@@ -19,7 +19,7 @@ module.exports = {
{
files: [
'**/__tests__/*.{j,t}s?(x)',
'**/tests/unit/**/*.spec.{j,t}s?(x)'
'**/tests/**/*.spec.{j,t}s?(x)'
],
env: {
mocha: true
+1 -1
View File
@@ -27,4 +27,4 @@ jobs:
run: npm run lint
- name: Run karma tests
run: npm run test:unit
run: npm run test
+1 -1
View File
@@ -7,7 +7,7 @@ Vue.use(VModal)
Vue.config.productionTip = false
// require all test files (files that ends with .spec.js)
const testsContext = require.context('./tests/unit', true, /\.spec.js$/)
const testsContext = require.context('./tests', true, /\.spec.js$/)
// Read more about why we need to call testContext:
// https://www.npmjs.com/package/require-context#context-api
+1 -1
View File
@@ -6,7 +6,7 @@
"scripts": {
"serve": "vue-cli-service serve",
"build": "NODE_OPTIONS=--max_old_space_size=4096 vue-cli-service build",
"test:unit": "vue-cli-service karma",
"test": "vue-cli-service karma",
"lint": "vue-cli-service lint"
},
"dependencies": {
+1 -1
View File
@@ -20,7 +20,7 @@
<script>
export default {
name: 'checkBox',
name: 'CheckBox',
props: {
theme: {
type: String,
@@ -1,5 +1,5 @@
<template>
<div class="db-upload-container">
<div class="db-uploader-container">
<change-db-icon v-if="type === 'small'" @click.native="browse"/>
<div v-if="['regular', 'illustrated'].includes(type)" class="drop-area-container">
<div
@@ -62,6 +62,7 @@
width="93px"
:disabled="disableDialog"
class="char-input"
id="quote-char"
/>
<text-field
label="Escape char"
@@ -71,6 +72,7 @@
width="93px"
:disabled="disableDialog"
class="char-input"
id="escape-char"
/>
</div>
<check-box
@@ -122,7 +124,7 @@
</template>
<script>
import fu from '@/fileUtils'
import fu from '@/file.utils'
import csv from '@/csv'
import CloseIcon from '@/components/svg/close'
import TextField from '@/components/TextField'
@@ -135,7 +137,7 @@ import time from '@/time'
import database from '@/database'
export default {
name: 'DbUpload',
name: 'DbUploader',
props: {
type: {
type: String,
@@ -164,7 +166,7 @@ export default {
delimiter: '',
quoteChar: '"',
escapeChar: '"',
header: false,
header: true,
previewData: null,
importCsvMessages: [],
disableDialog: false,
@@ -237,9 +239,9 @@ export default {
this.delimiter = parseResult.delimiter
// In parseResult.messages we can get parse errors
this.importCsvMessages = parseResult.messages
this.importCsvMessages = parseResult.messages || []
if (parseResult.messages.length === 0) {
if (!parseResult.hasErrors) {
this.importCsvMessages.push({
message: `Preview parsing is completed in ${time.getPeriod(start, end)}.`,
type: 'success'
@@ -390,7 +392,7 @@ export default {
</script>
<style scoped>
.db-upload-container {
.db-uploader-container {
position: relative;
}
.drop-area-container {
+5 -1
View File
@@ -35,7 +35,11 @@ export default {
serializeMessage (msg) {
let result = ''
if (msg.row !== null && msg.row !== undefined) {
result += `Error in row ${msg.row}. `
if (msg.type === 'error') {
result += `Error in row ${msg.row}. `
} else {
result += `Information about row ${msg.row}. `
}
}
result += msg.message
+3 -3
View File
@@ -8,7 +8,7 @@
<tree-chevron :expanded="schemaVisible"/>
{{ dbName }}
</div>
<db-upload id="db-edit" type="small" />
<db-uploader id="db-edit" type="small" />
</div>
<div v-show="schemaVisible" class="schema">
<table-description
@@ -25,7 +25,7 @@
import TableDescription from '@/components/TableDescription'
import TextField from '@/components/TextField'
import TreeChevron from '@/components/svg/treeChevron'
import dbUpload from '@/components/DbUpload'
import DbUploader from '@/components/DbUploader'
export default {
name: 'Schema',
@@ -33,7 +33,7 @@ export default {
TableDescription,
TextField,
TreeChevron,
dbUpload
DbUploader
},
data () {
return {
+1 -1
View File
@@ -1,5 +1,5 @@
import sqliteParser from 'sqlite-parser'
import fu from '@/fileUtils'
import fu from '@/file.utils'
// We can import workers like so because of worker-loader:
// https://webpack.js.org/loaders/worker-loader/
import Worker from '@/db.worker.js'
View File
File renamed without changes.
File renamed without changes.
+1 -1
View File
@@ -1,5 +1,5 @@
import initSqlJs from 'sql.js/dist/sql-wasm.js'
import dbUtils from '@/dbUtils'
import dbUtils from '@/db.utils'
let SQL = null
const sqlModuleReady = initSqlJs().then(sqlModule => { SQL = sqlModule })
+1 -1
View File
@@ -1,5 +1,5 @@
import { nanoid } from 'nanoid'
import fu from '@/fileUtils'
import fu from '@/file.utils'
export default {
getStoredQueries () {
+4 -4
View File
@@ -11,7 +11,7 @@
<div class="warning">
Database is not loaded. Queries can’t be run without database.
</div>
<db-upload id="db-uploader"/>
<db-uploader id="db-uploader"/>
</div>
</template>
<template #right-pane>
@@ -25,7 +25,7 @@
import Splitpanes from '@/components/Splitpanes'
import Schema from '@/components/Schema'
import Tabs from '@/components/Tabs'
import dbUpload from '@/components/DbUpload'
import DbUploader from '@/components/DbUploader'
export default {
name: 'Editor',
@@ -33,7 +33,7 @@ export default {
Schema,
Splitpanes,
Tabs,
dbUpload
DbUploader
}
}
</script>
@@ -64,7 +64,7 @@ export default {
box-sizing: border-box;
}
>>> .db-upload-container {
>>> .db-uploader-container {
width: 100%;
}
+3 -3
View File
@@ -1,6 +1,6 @@
<template>
<div id="dbloader-container">
<db-upload type="illustrated" />
<db-uploader type="illustrated" />
<div id="note">
Sqliteviz is fully client-side. Your database never leaves your computer.
</div>
@@ -11,11 +11,11 @@
</template>
<script>
import dbUpload from '@/components/DbUpload'
import DbUploader from '@/components/DbUploader'
export default {
name: 'Home',
components: { dbUpload }
components: { DbUploader }
}
</script>
+1 -1
View File
@@ -147,7 +147,7 @@ import TextField from '@/components/TextField'
import CheckBox from '@/components/CheckBox'
import tooltipMixin from '@/mixins/tooltips'
import storedQueries from '@/storedQueries'
import fu from '@/fileUtils'
import fu from '@/file.utils'
export default {
name: 'MyQueries',
File renamed without changes.
File renamed without changes.
File renamed without changes.
+293
View File
@@ -0,0 +1,293 @@
import { expect } from 'chai'
import sinon from 'sinon'
import Vuex from 'vuex'
import { shallowMount, mount } from '@vue/test-utils'
import DbUploader from '@/components/DbUploader.vue'
import fu from '@/file.utils'
import database from '@/database.js'
import csv from '@/csv'
let state = {}
let mutations = {}
let store = {}
describe('DbUploader.vue', () => {
beforeEach(() => {
// mock store state and mutations
state = {}
mutations = {
saveSchema: sinon.stub()
}
store = new Vuex.Store({ state, mutations })
})
afterEach(() => {
sinon.restore()
})
it('loads db on click and redirects to /editor', async () => {
// mock getting a file from user
const file = {}
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, {
store,
mocks: { $router, $route }
})
await wrapper.find('.drop-area').trigger('click')
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)
})
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, {
store,
mocks: { $router, $route }
})
// mock a file dropped by a user
const file = {}
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]
expect(mutations.saveSchema.calledOnceWith(state, schema)).to.equal(true)
expect($router.push.calledOnceWith('/editor')).to.equal(true)
})
it("doesn't redirect if already on /editor", async () => {
// mock getting a file from user
const file = {}
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, {
store,
mocks: { $router, $route }
})
await wrapper.find('.drop-area').trigger('click')
await db.loadDb.returnValues[0]
expect($router.push.called).to.equal(false)
})
it('shows parse dialog if gets csv file', async () => {
// mock getting a file from user
const file = { type: 'text/csv' }
sinon.stub(fu, 'getFileFromUser').resolves(file)
// mock router
const $router = { push: sinon.stub() }
const $route = { path: '/editor' }
sinon.stub(csv, 'parse').resolves({
delimiter: '|',
data: {
columns: ['col1', 'col2'],
values: [
[1, 'foo'],
[2, 'bar']
]
},
messages: [{
code: 'UndetectableDelimiter',
message: 'Comma was used as a standart delimiter',
row: 0,
type: 'info',
hint: undefined
}]
})
// mount the component
const wrapper = mount(DbUploader, {
store,
mocks: { $router, $route }
})
await wrapper.find('.drop-area').trigger('click')
await csv.parse.returnValues[0]
await wrapper.vm.animationPromise
await wrapper.vm.$nextTick()
expect(wrapper.find('[data-modal="parse"]').exists()).to.equal(true)
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('"')
expect(wrapper.findComponent({ name: 'check-box' }).vm.checked).to.equal(true)
const rows = wrapper.findAll('tbody tr')
expect(rows).to.have.lengthOf(2)
expect(rows.at(0).findAll('td').at(0).text()).to.equal('1')
expect(rows.at(0).findAll('td').at(1).text()).to.equal('foo')
expect(rows.at(1).findAll('td').at(0).text()).to.equal('2')
expect(rows.at(1).findAll('td').at(1).text()).to.equal('bar')
expect(wrapper.findComponent({ name: 'logs' }).text())
.to.include('Information about row 0. Comma was used as a standart delimiter.')
expect(wrapper.findComponent({ name: 'logs' }).text())
.to.include('Preview parsing is completed in')
})
it('reparses when parameters changes', async () => {
// mock getting a file from user
const file = { type: 'text/csv' }
sinon.stub(fu, 'getFileFromUser').resolves(file)
// mock router
const $router = { push: sinon.stub() }
const $route = { path: '/editor' }
const parse = sinon.stub(csv, 'parse')
parse.onCall(0).resolves({
delimiter: '|',
data: {
columns: ['col1', 'col2'],
values: [
[1, 'foo']
]
}
})
// mount the component
const wrapper = mount(DbUploader, {
store,
mocks: { $router, $route }
})
await wrapper.find('.drop-area').trigger('click')
await csv.parse.returnValues[0]
await wrapper.vm.animationPromise
await wrapper.vm.$nextTick()
parse.onCall(1).resolves({
delimiter: ',',
data: {
columns: ['col1', 'col2'],
values: [
[2, 'bar']
]
},
hasErrors: false
})
await wrapper.find('.delimiter-selector-container input').setValue(',')
expect(parse.callCount).to.equal(2)
await csv.parse.returnValues[1]
let rows = wrapper.findAll('tbody tr')
expect(rows).to.have.lengthOf(1)
expect(rows.at(0).findAll('td').at(0).text()).to.equal('2')
expect(rows.at(0).findAll('td').at(1).text()).to.equal('bar')
expect(wrapper.findComponent({ name: 'logs' }).text())
.to.include('Preview parsing is completed in')
parse.onCall(2).resolves({
delimiter: ',',
data: {
columns: ['col1', 'col2'],
values: [
[3, 'baz']
]
},
hasErrors: true,
messages: [{
code: 'MissingQuotes',
message: 'Quote is missed',
row: 0,
type: 'error',
hint: 'Edit your CSV so that the field has a closing quote char.'
}]
})
await wrapper.find('#quote-char input').setValue("'")
expect(parse.callCount).to.equal(3)
await csv.parse.returnValues[2]
rows = wrapper.findAll('tbody tr')
expect(rows).to.have.lengthOf(1)
expect(rows.at(0).findAll('td').at(0).text()).to.equal('3')
expect(rows.at(0).findAll('td').at(1).text()).to.equal('baz')
expect(wrapper.findComponent({ name: 'logs' }).text())
.to.contain('Error in row 0. Quote is missed. Edit your CSV so that the field has a closing quote char.')
expect(wrapper.findComponent({ name: 'logs' }).text())
.to.not.contain('Preview parsing is completed in')
parse.onCall(3).resolves({
delimiter: ',',
data: {
columns: ['col1', 'col2'],
values: [
[4, 'qux']
]
},
hasErrors: false
})
await wrapper.find('#escape-char input').setValue("'")
expect(parse.callCount).to.equal(4)
await csv.parse.returnValues[3]
rows = wrapper.findAll('tbody tr')
expect(rows).to.have.lengthOf(1)
expect(rows.at(0).findAll('td').at(0).text()).to.equal('4')
expect(rows.at(0).findAll('td').at(1).text()).to.equal('qux')
expect(wrapper.findComponent({ name: 'logs' }).text())
.to.contain('Preview parsing is completed in')
parse.onCall(4).resolves({
delimiter: ',',
data: {
columns: ['col1', 'col2'],
values: [
[5, 'corge']
]
},
hasErrors: false
})
await wrapper.findComponent({ name: 'check-box' }).trigger('click')
expect(parse.callCount).to.equal(5)
await csv.parse.returnValues[4]
rows = wrapper.findAll('tbody tr')
expect(rows).to.have.lengthOf(1)
expect(rows.at(0).findAll('td').at(0).text()).to.equal('5')
expect(rows.at(0).findAll('td').at(1).text()).to.equal('corge')
expect(wrapper.findComponent({ name: 'logs' }).text())
.to.include('Preview parsing is completed in')
})
})
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,7 +1,7 @@
import { expect } from 'chai'
import dbUtils from '@/dbUtils'
import dbUtils from '@/db.utils'
describe('dbUtils.js', () => {
describe('db.utils.js', () => {
it('generateChunks', () => {
const arr = ['1', '2', '3', '4', '5']
const size = 2
@@ -1,8 +1,8 @@
import { expect } from 'chai'
import fu from '@/fileUtils.js'
import fu from '@/file.utils'
import sinon from 'sinon'
describe('fileUtils.js', () => {
describe('file.utils.js', () => {
afterEach(() => {
sinon.restore()
})
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,7 +1,7 @@
import { expect } from 'chai'
import sinon from 'sinon'
import storedQueries from '@/storedQueries.js'
import fu from '@/fileUtils'
import fu from '@/file.utils'
describe('storedQueries.js', () => {
beforeEach(() => {
File renamed without changes.
File renamed without changes.
-123
View File
@@ -1,123 +0,0 @@
import { expect } from 'chai'
import sinon from 'sinon'
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(() => {
sinon.restore()
})
it('loads db on click and redirects to /editor', async () => {
// mock store state and mutations
const state = {}
const mutations = {
saveSchema: sinon.stub()
}
const store = new Vuex.Store({ state, mutations })
// mock getting a file from user
const file = {}
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(DbUpload, {
store,
mocks: { $router, $route }
})
await wrapper.find('.drop-area').trigger('click')
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)
})
it('loads db on drop and redirects to /editor', async () => {
// mock store state and mutations
const state = {}
const mutations = {
saveSchema: sinon.stub()
}
const store = new Vuex.Store({ state, mutations })
// 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(DbUpload, {
store,
mocks: { $router, $route }
})
// mock a file dropped by a user
const file = {}
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]
expect(mutations.saveSchema.calledOnceWith(state, schema)).to.equal(true)
expect($router.push.calledOnceWith('/editor')).to.equal(true)
})
it("doesn't redirect if already on /editor", async () => {
// mock store state and mutations
const state = {}
const mutations = {
saveSchema: sinon.stub()
}
const store = new Vuex.Store({ state, mutations })
// mock getting a file from user
const file = {}
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(DbUpload, {
store,
mocks: { $router, $route }
})
await wrapper.find('.drop-area').trigger('click')
await db.loadDb.returnValues[0]
expect($router.push.called).to.equal(false)
})
})
@@ -3,9 +3,9 @@ import sinon from 'sinon'
import { mount, shallowMount } from '@vue/test-utils'
import Vuex from 'vuex'
import MyQueries from '@/views/MyQueries.vue'
import storedQueries from '@/storedQueries.js'
import storedQueries from '@/storedQueries'
import { mutations } from '@/store'
import fu from '@/fileUtils.js'
import fu from '@/file.utils'
describe('MyQueries.vue', () => {
afterEach(() => {