mirror of
https://github.com/lana-k/sqliteviz.git
synced 2025-12-06 18:18:53 +08:00
Loading remote database and inquiries #109
This commit is contained in:
13
package-lock.json
generated
13
package-lock.json
generated
@@ -50,6 +50,7 @@
|
|||||||
"eslint-plugin-promise": "^4.2.1",
|
"eslint-plugin-promise": "^4.2.1",
|
||||||
"eslint-plugin-standard": "^4.0.0",
|
"eslint-plugin-standard": "^4.0.0",
|
||||||
"eslint-plugin-vue": "^6.2.2",
|
"eslint-plugin-vue": "^6.2.2",
|
||||||
|
"flush-promises": "^1.0.2",
|
||||||
"karma": "^3.1.4",
|
"karma": "^3.1.4",
|
||||||
"karma-firefox-launcher": "^2.1.0",
|
"karma-firefox-launcher": "^2.1.0",
|
||||||
"karma-webpack": "^4.0.2",
|
"karma-webpack": "^4.0.2",
|
||||||
@@ -10090,6 +10091,12 @@
|
|||||||
"resolved": "https://registry.npmjs.org/flip-pixels/-/flip-pixels-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/flip-pixels/-/flip-pixels-1.0.2.tgz",
|
||||||
"integrity": "sha512-oXbJGbjDnfJRWPC7Va38EFhd+A8JWE5/hCiKcK8qjCdbLj9DTpsq6MEudwpRTH+V4qq+Jw7d3pUgQdSr3x3mTA=="
|
"integrity": "sha512-oXbJGbjDnfJRWPC7Va38EFhd+A8JWE5/hCiKcK8qjCdbLj9DTpsq6MEudwpRTH+V4qq+Jw7d3pUgQdSr3x3mTA=="
|
||||||
},
|
},
|
||||||
|
"node_modules/flush-promises": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/flush-promises/-/flush-promises-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-G0sYfLQERwKz4+4iOZYQEZVpOt9zQrlItIxQAAYAWpfby3gbHrx0osCHz5RLl/XoXevXk0xoN4hDFky/VV9TrA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/flush-write-stream": {
|
"node_modules/flush-write-stream": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz",
|
||||||
@@ -33658,6 +33665,12 @@
|
|||||||
"resolved": "https://registry.npmjs.org/flip-pixels/-/flip-pixels-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/flip-pixels/-/flip-pixels-1.0.2.tgz",
|
||||||
"integrity": "sha512-oXbJGbjDnfJRWPC7Va38EFhd+A8JWE5/hCiKcK8qjCdbLj9DTpsq6MEudwpRTH+V4qq+Jw7d3pUgQdSr3x3mTA=="
|
"integrity": "sha512-oXbJGbjDnfJRWPC7Va38EFhd+A8JWE5/hCiKcK8qjCdbLj9DTpsq6MEudwpRTH+V4qq+Jw7d3pUgQdSr3x3mTA=="
|
||||||
},
|
},
|
||||||
|
"flush-promises": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/flush-promises/-/flush-promises-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-G0sYfLQERwKz4+4iOZYQEZVpOt9zQrlItIxQAAYAWpfby3gbHrx0osCHz5RLl/XoXevXk0xoN4hDFky/VV9TrA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"flush-write-stream": {
|
"flush-write-stream": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz",
|
||||||
|
|||||||
@@ -51,6 +51,7 @@
|
|||||||
"eslint-plugin-promise": "^4.2.1",
|
"eslint-plugin-promise": "^4.2.1",
|
||||||
"eslint-plugin-standard": "^4.0.0",
|
"eslint-plugin-standard": "^4.0.0",
|
||||||
"eslint-plugin-vue": "^6.2.2",
|
"eslint-plugin-vue": "^6.2.2",
|
||||||
|
"flush-promises": "^1.0.2",
|
||||||
"karma": "^3.1.4",
|
"karma": "^3.1.4",
|
||||||
"karma-firefox-launcher": "^2.1.0",
|
"karma-firefox-launcher": "^2.1.0",
|
||||||
"karma-webpack": "^4.0.2",
|
"karma-webpack": "^4.0.2",
|
||||||
|
|||||||
@@ -75,14 +75,23 @@ export default {
|
|||||||
props: {
|
props: {
|
||||||
horizontal: { type: Boolean, default: false },
|
horizontal: { type: Boolean, default: false },
|
||||||
before: { type: Object },
|
before: { type: Object },
|
||||||
after: { type: Object }
|
after: { type: Object },
|
||||||
|
default: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {
|
||||||
|
return {
|
||||||
|
before: 50,
|
||||||
|
after: 50
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
container: null,
|
container: null,
|
||||||
paneBefore: this.before,
|
paneBefore: this.before,
|
||||||
paneAfter: this.after,
|
paneAfter: this.after,
|
||||||
beforeMinimising: {
|
beforeMinimising: !this.after.size || !this.before.size ? this.default : {
|
||||||
before: this.before.size,
|
before: this.before.size,
|
||||||
after: this.after.size
|
after: this.after.size
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ class Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.dbName = file ? fu.getFileName(file) : 'database'
|
this.dbName = file ? fu.getFileName(file) : 'database'
|
||||||
this.refreshSchema()
|
await this.refreshSchema()
|
||||||
|
|
||||||
events.send('database.import', file ? file.size : 0, {
|
events.send('database.import', file ? file.size : 0, {
|
||||||
from: file ? 'sqlite' : 'none',
|
from: file ? 'sqlite' : 'none',
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ export default class Tab {
|
|||||||
table: 'bottom',
|
table: 'bottom',
|
||||||
dataView: 'hidden'
|
dataView: 'hidden'
|
||||||
}
|
}
|
||||||
|
this.maximize = inquiry.maximize
|
||||||
|
|
||||||
this.isSaved = !!inquiry.id
|
this.isSaved = !!inquiry.id
|
||||||
this.state = state
|
this.state = state
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import Workspace from '@/views/Main/Workspace'
|
|||||||
import Inquiries from '@/views/Main/Inquiries'
|
import Inquiries from '@/views/Main/Inquiries'
|
||||||
import Welcome from '@/views/Welcome'
|
import Welcome from '@/views/Welcome'
|
||||||
import Main from '@/views/Main'
|
import Main from '@/views/Main'
|
||||||
|
import LoadView from '@/views/LoadView'
|
||||||
import store from '@/store'
|
import store from '@/store'
|
||||||
import database from '@/lib/database'
|
import database from '@/lib/database'
|
||||||
|
|
||||||
@@ -31,6 +32,11 @@ const routes = [
|
|||||||
component: Inquiries
|
component: Inquiries
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/load',
|
||||||
|
name: 'Load',
|
||||||
|
component: LoadView
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -39,7 +45,7 @@ const router = new VueRouter({
|
|||||||
})
|
})
|
||||||
|
|
||||||
router.beforeEach(async (to, from, next) => {
|
router.beforeEach(async (to, from, next) => {
|
||||||
if (!store.state.db) {
|
if (!store.state.db && to.name !== 'Load') {
|
||||||
const newDb = database.getNewDatabase()
|
const newDb = database.getNewDatabase()
|
||||||
await newDb.loadDb()
|
await newDb.loadDb()
|
||||||
store.commit('setDb', newDb)
|
store.commit('setDb', newDb)
|
||||||
|
|||||||
188
src/views/LoadView.vue
Normal file
188
src/views/LoadView.vue
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<logs
|
||||||
|
id="logs"
|
||||||
|
:messages="messages"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
v-if="hasErrors"
|
||||||
|
id="open-workspace-btn"
|
||||||
|
class="secondary"
|
||||||
|
@click="$router.push('/workspace?hide_schema=1')">
|
||||||
|
Open workspace
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import fu from '@/lib/utils/fileIo'
|
||||||
|
import database from '@/lib/database'
|
||||||
|
import Logs from '@/components/Logs'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'LoadView',
|
||||||
|
components: {
|
||||||
|
Logs
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
newDb: null,
|
||||||
|
messages: [],
|
||||||
|
dataMsg: {},
|
||||||
|
inquiryMsg: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
hasErrors () {
|
||||||
|
return this.dataMsg.type === 'error' || this.inquiryMsg.type === 'error'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async created () {
|
||||||
|
const {
|
||||||
|
data_url: dataUrl,
|
||||||
|
data_format: dataFormat,
|
||||||
|
inquiry_url: inquiryUrl,
|
||||||
|
inquiry_id: inquiryIds,
|
||||||
|
maximize
|
||||||
|
} = this.$route.query
|
||||||
|
|
||||||
|
await this.loadData(dataUrl, dataFormat)
|
||||||
|
const inquiries = await this.loadInquiries(inquiryUrl, inquiryIds)
|
||||||
|
if (inquiries && inquiries.length > 0) {
|
||||||
|
await this.openInquiries(inquiries, maximize)
|
||||||
|
}
|
||||||
|
if (!this.hasErrors) {
|
||||||
|
this.$router.push('/workspace?hide_schema=1')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async loadData (dataUrl, dataFormat) {
|
||||||
|
this.newDb = database.getNewDatabase()
|
||||||
|
if (dataUrl) {
|
||||||
|
this.dataMsg = {
|
||||||
|
message: 'Preparing data...',
|
||||||
|
type: 'info'
|
||||||
|
}
|
||||||
|
this.messages.push(this.dataMsg)
|
||||||
|
|
||||||
|
// Show loading indicator after 1 second
|
||||||
|
const loadingDataIndicator = setTimeout(() => {
|
||||||
|
if (this.dataMsg.type === 'info') {
|
||||||
|
this.dataMsg.type = 'loading'
|
||||||
|
}
|
||||||
|
}, 1000)
|
||||||
|
|
||||||
|
if (dataFormat === 'sqlite') {
|
||||||
|
await this.getSqliteDb(dataUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loading indicator is not needed anymore
|
||||||
|
clearTimeout(loadingDataIndicator)
|
||||||
|
} else {
|
||||||
|
await this.newDb.loadDb()
|
||||||
|
}
|
||||||
|
this.$store.commit('setDb', this.newDb)
|
||||||
|
},
|
||||||
|
async getSqliteDb (dataUrl) {
|
||||||
|
try {
|
||||||
|
const filename = new URL(dataUrl).pathname.split('/').pop()
|
||||||
|
const res = await fu.readFile(dataUrl)
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error('Fetching DB failed')
|
||||||
|
}
|
||||||
|
const file = await res.blob()
|
||||||
|
file.name = filename
|
||||||
|
|
||||||
|
await this.newDb.loadDb(file)
|
||||||
|
this.dataMsg.message = 'Data is ready'
|
||||||
|
this.dataMsg.type = 'success'
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
this.dataMsg.message = error
|
||||||
|
this.dataMsg.type = 'error'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async loadInquiries (inquiryUrl, inquiryIds = []) {
|
||||||
|
if (!inquiryUrl) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
// Show loading indicator after 1 second
|
||||||
|
const loadingInquiriesIndicator = setTimeout(() => {
|
||||||
|
if (this.inquiryMsg.type === 'info') {
|
||||||
|
this.inquiryMsg.type = 'loading'
|
||||||
|
}
|
||||||
|
}, 1000)
|
||||||
|
try {
|
||||||
|
this.inquiryMsg = {
|
||||||
|
message: 'Preparing inquiries...',
|
||||||
|
type: 'info'
|
||||||
|
}
|
||||||
|
this.messages.push(this.inquiryMsg)
|
||||||
|
|
||||||
|
const res = await fu.readFile(inquiryUrl)
|
||||||
|
const file = await res.json()
|
||||||
|
|
||||||
|
this.inquiryMsg.message = 'Inquiries are ready'
|
||||||
|
this.inquiryMsg.type = 'success'
|
||||||
|
|
||||||
|
return inquiryIds.length > 0
|
||||||
|
? file.inquiries.filter(inquiry => inquiryIds.includes(inquiry.id))
|
||||||
|
: file.inquiries
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
this.inquiryMsg.message = error
|
||||||
|
this.inquiryMsg.type = 'error'
|
||||||
|
}
|
||||||
|
// Loading indicator is not needed anymore
|
||||||
|
clearTimeout(loadingInquiriesIndicator)
|
||||||
|
},
|
||||||
|
async openInquiries (inquiries, maximize) {
|
||||||
|
let tabToOpen = null
|
||||||
|
const layout = maximize ? this.getLayout(maximize) : undefined
|
||||||
|
for (const inquiry of inquiries) {
|
||||||
|
const tabId = await this.$store.dispatch('addTab', {
|
||||||
|
...inquiry,
|
||||||
|
id: undefined,
|
||||||
|
layout,
|
||||||
|
maximize
|
||||||
|
})
|
||||||
|
if (!tabToOpen) {
|
||||||
|
tabToOpen = tabId
|
||||||
|
this.$store.commit('setCurrentTabId', tabToOpen)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$store.state.currentTab.execute()
|
||||||
|
},
|
||||||
|
|
||||||
|
getLayout (panelToMaximize) {
|
||||||
|
if (panelToMaximize === 'dataView') {
|
||||||
|
return {
|
||||||
|
sqlEditor: 'hidden',
|
||||||
|
table: 'above',
|
||||||
|
dataView: 'bottom'
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
sqlEditor: 'above',
|
||||||
|
table: 'bottom',
|
||||||
|
dataView: 'hidden'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
#logs {
|
||||||
|
margin: 8px auto;
|
||||||
|
max-width: 800px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#open-workspace-btn {
|
||||||
|
margin: 16px auto;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -3,8 +3,9 @@
|
|||||||
<splitpanes
|
<splitpanes
|
||||||
class="query-results-splitter"
|
class="query-results-splitter"
|
||||||
horizontal
|
horizontal
|
||||||
:before="{ size: 50, max: 100 }"
|
:before="{ size: topPaneSize, max: 100 }"
|
||||||
:after="{ size: 50, max: 100 }"
|
:after="{ size: 100 - topPaneSize, max: 100 }"
|
||||||
|
:default="{ before: 50, after: 50 }"
|
||||||
>
|
>
|
||||||
<template #left-pane>
|
<template #left-pane>
|
||||||
<div :id="'above-' + tab.id" class="above" />
|
<div :id="'above-' + tab.id" class="above" />
|
||||||
@@ -70,6 +71,13 @@ export default {
|
|||||||
Splitpanes,
|
Splitpanes,
|
||||||
Teleport
|
Teleport
|
||||||
},
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
topPaneSize: this.tab.maximize
|
||||||
|
? this.tab.layout[this.tab.maximize] === 'above' ? 100 : 0
|
||||||
|
: 50
|
||||||
|
}
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
isActive () {
|
isActive () {
|
||||||
return this.tab.id === this.$store.state.currentTabId
|
return this.tab.id === this.$store.state.currentTabId
|
||||||
|
|||||||
@@ -2,8 +2,9 @@
|
|||||||
<div>
|
<div>
|
||||||
<splitpanes
|
<splitpanes
|
||||||
class="schema-tabs-splitter"
|
class="schema-tabs-splitter"
|
||||||
:before="{ size: 20, max: 30 }"
|
:before="{ size: schemaWidth, max: 30 }"
|
||||||
:after="{ size: 80, max: 100 }"
|
:after="{ size: 100 - schemaWidth, max: 100 }"
|
||||||
|
:default="{ before: 20, after: 80 }"
|
||||||
>
|
>
|
||||||
<template #left-pane>
|
<template #left-pane>
|
||||||
<schema/>
|
<schema/>
|
||||||
@@ -28,6 +29,11 @@ export default {
|
|||||||
Splitpanes,
|
Splitpanes,
|
||||||
Tabs
|
Tabs
|
||||||
},
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
schemaWidth: this.$route.query.hide_schema === '1' ? 0 : 20
|
||||||
|
}
|
||||||
|
},
|
||||||
async beforeCreate () {
|
async beforeCreate () {
|
||||||
const schema = this.$store.state.db.schema
|
const schema = this.$store.state.db.schema
|
||||||
if (!schema || schema.length === 0) {
|
if (!schema || schema.length === 0) {
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ describe('Splitpanes.vue', () => {
|
|||||||
expect(wrapper.findAll('.splitpanes-pane').at(1).element.style.height).to.equal('40%')
|
expect(wrapper.findAll('.splitpanes-pane').at(1).element.style.height).to.equal('40%')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('toggles correctly', async () => {
|
it('toggles correctly - no maximized initially', async () => {
|
||||||
// mount the component
|
// mount the component
|
||||||
const wrapper = shallowMount(Splitpanes, {
|
const wrapper = shallowMount(Splitpanes, {
|
||||||
slots: {
|
slots: {
|
||||||
@@ -70,6 +70,64 @@ describe('Splitpanes.vue', () => {
|
|||||||
expect(wrapper.findAll('.splitpanes-pane').at(1).element.style.width).to.equal('40%')
|
expect(wrapper.findAll('.splitpanes-pane').at(1).element.style.width).to.equal('40%')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('toggles correctly - with maximized initially', async () => {
|
||||||
|
// mount the component
|
||||||
|
let wrapper = shallowMount(Splitpanes, {
|
||||||
|
slots: {
|
||||||
|
leftPane: '<div />',
|
||||||
|
rightPane: '<div />'
|
||||||
|
},
|
||||||
|
propsData: {
|
||||||
|
before: { size: 0, max: 100 },
|
||||||
|
after: { size: 100, max: 100 },
|
||||||
|
default: { before: 20, after: 80 }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
await wrapper.find('.toggle-btn').trigger('click')
|
||||||
|
expect(wrapper.findAll('.splitpanes-pane').at(0).element.style.width).to.equal('20%')
|
||||||
|
expect(wrapper.findAll('.splitpanes-pane').at(1).element.style.width).to.equal('80%')
|
||||||
|
|
||||||
|
await wrapper.findAll('.toggle-btn').at(0).trigger('click')
|
||||||
|
expect(wrapper.findAll('.splitpanes-pane').at(0).element.style.width).to.equal('0%')
|
||||||
|
expect(wrapper.findAll('.splitpanes-pane').at(1).element.style.width).to.equal('100%')
|
||||||
|
|
||||||
|
await wrapper.find('.toggle-btn').trigger('click')
|
||||||
|
expect(wrapper.findAll('.splitpanes-pane').at(0).element.style.width).to.equal('20%')
|
||||||
|
expect(wrapper.findAll('.splitpanes-pane').at(1).element.style.width).to.equal('80%')
|
||||||
|
|
||||||
|
await wrapper.findAll('.toggle-btn').at(1).trigger('click')
|
||||||
|
expect(wrapper.findAll('.splitpanes-pane').at(0).element.style.width).to.equal('100%')
|
||||||
|
expect(wrapper.findAll('.splitpanes-pane').at(1).element.style.width).to.equal('0%')
|
||||||
|
|
||||||
|
wrapper = shallowMount(Splitpanes, {
|
||||||
|
slots: {
|
||||||
|
leftPane: '<div />',
|
||||||
|
rightPane: '<div />'
|
||||||
|
},
|
||||||
|
propsData: {
|
||||||
|
before: { size: 100, max: 100 },
|
||||||
|
after: { size: 0, max: 100 }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
await wrapper.find('.toggle-btn').trigger('click')
|
||||||
|
expect(wrapper.findAll('.splitpanes-pane').at(0).element.style.width).to.equal('50%')
|
||||||
|
expect(wrapper.findAll('.splitpanes-pane').at(1).element.style.width).to.equal('50%')
|
||||||
|
|
||||||
|
await wrapper.findAll('.toggle-btn').at(0).trigger('click')
|
||||||
|
expect(wrapper.findAll('.splitpanes-pane').at(0).element.style.width).to.equal('0%')
|
||||||
|
expect(wrapper.findAll('.splitpanes-pane').at(1).element.style.width).to.equal('100%')
|
||||||
|
|
||||||
|
await wrapper.find('.toggle-btn').trigger('click')
|
||||||
|
expect(wrapper.findAll('.splitpanes-pane').at(0).element.style.width).to.equal('50%')
|
||||||
|
expect(wrapper.findAll('.splitpanes-pane').at(1).element.style.width).to.equal('50%')
|
||||||
|
|
||||||
|
await wrapper.findAll('.toggle-btn').at(1).trigger('click')
|
||||||
|
expect(wrapper.findAll('.splitpanes-pane').at(0).element.style.width).to.equal('100%')
|
||||||
|
expect(wrapper.findAll('.splitpanes-pane').at(1).element.style.width).to.equal('0%')
|
||||||
|
})
|
||||||
|
|
||||||
it('drag - vertical', async () => {
|
it('drag - vertical', async () => {
|
||||||
const root = document.createElement('div')
|
const root = document.createElement('div')
|
||||||
const place = document.createElement('div')
|
const place = document.createElement('div')
|
||||||
|
|||||||
@@ -283,6 +283,7 @@ describe('mutations', () => {
|
|||||||
|
|
||||||
it('setCurrentTabId', () => {
|
it('setCurrentTabId', () => {
|
||||||
const state = {
|
const state = {
|
||||||
|
tabs: [{ id: 1 }, { id: 2 }],
|
||||||
currentTabId: 1
|
currentTabId: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
147
tests/views/LoadView.spec.js
Normal file
147
tests/views/LoadView.spec.js
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
import { expect } from 'chai'
|
||||||
|
import sinon from 'sinon'
|
||||||
|
import { mount } from '@vue/test-utils'
|
||||||
|
import Vuex from 'vuex'
|
||||||
|
import LoadView from '@/views/LoadView'
|
||||||
|
import fu from '@/lib/utils/fileIo'
|
||||||
|
import database from '@/lib/database'
|
||||||
|
import realMutations from '@/store/mutations'
|
||||||
|
import realActions from '@/store/actions'
|
||||||
|
import flushPromises from 'flush-promises'
|
||||||
|
import Tab from '@/lib/tab'
|
||||||
|
|
||||||
|
describe('LoadView.vue', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
sinon.restore()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Loads db and inquiries and redirects to workspace if no errors', async () => {
|
||||||
|
const state = {
|
||||||
|
tabs: []
|
||||||
|
}
|
||||||
|
const mutations = {
|
||||||
|
setCurrentTabId: sinon.stub().callsFake(realMutations.setCurrentTabId),
|
||||||
|
setDb: sinon.stub().callsFake(realMutations.setDb)
|
||||||
|
}
|
||||||
|
const actions = {
|
||||||
|
addTab: sinon.stub().callsFake(realActions.addTab)
|
||||||
|
}
|
||||||
|
const store = new Vuex.Store({ state, mutations, actions })
|
||||||
|
const $route = {
|
||||||
|
path: '/workspace',
|
||||||
|
query: {
|
||||||
|
data_url: 'https://my-url/test.db',
|
||||||
|
data_format: 'sqlite',
|
||||||
|
inquiry_url: 'https://my-url/test_inquiries.json',
|
||||||
|
inquiry_id: [1],
|
||||||
|
maximize: 'dataView'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const $router = { push: sinon.stub() }
|
||||||
|
|
||||||
|
const readFile = sinon.stub(fu, 'readFile')
|
||||||
|
const dataRes = new Response()
|
||||||
|
dataRes.blob = sinon.stub().resolves({})
|
||||||
|
readFile.onCall(0).returns(Promise.resolve(dataRes))
|
||||||
|
|
||||||
|
const inquiriesRes = new Response()
|
||||||
|
inquiriesRes.json = sinon.stub().resolves({
|
||||||
|
version: 2,
|
||||||
|
inquiries: [{ id: 1, name: 'foo' }, { id: 2, name: 'bar' }]
|
||||||
|
})
|
||||||
|
readFile.onCall(1).returns(Promise.resolve(inquiriesRes))
|
||||||
|
const db = {
|
||||||
|
loadDb: sinon.stub().resolves()
|
||||||
|
}
|
||||||
|
sinon.stub(database, 'getNewDatabase').returns(db)
|
||||||
|
Tab.prototype.execute = sinon.stub()
|
||||||
|
|
||||||
|
const wrapper = mount(LoadView, {
|
||||||
|
store,
|
||||||
|
mocks: { $route, $router },
|
||||||
|
stubs: ['router-link']
|
||||||
|
})
|
||||||
|
|
||||||
|
await flushPromises()
|
||||||
|
|
||||||
|
// DB file is read
|
||||||
|
expect(fu.readFile.firstCall.args[0]).to.equal('https://my-url/test.db')
|
||||||
|
|
||||||
|
// Db is loaded
|
||||||
|
expect(db.loadDb.firstCall.args[0]).to.equal(await dataRes.blob.returnValues[0])
|
||||||
|
|
||||||
|
// Inquiries file is read
|
||||||
|
expect(fu.readFile.secondCall.args[0])
|
||||||
|
.to.equal('https://my-url/test_inquiries.json')
|
||||||
|
|
||||||
|
// Tab for inquiry is created
|
||||||
|
expect(actions.addTab.calledOnce).to.equal(true)
|
||||||
|
expect(actions.addTab.firstCall.args[1]).eql({
|
||||||
|
id: undefined,
|
||||||
|
name: 'foo',
|
||||||
|
layout: {
|
||||||
|
dataView: 'bottom',
|
||||||
|
sqlEditor: 'hidden',
|
||||||
|
table: 'above'
|
||||||
|
},
|
||||||
|
maximize: 'dataView'
|
||||||
|
})
|
||||||
|
const executedTab = Tab.prototype.execute.firstCall.thisValue
|
||||||
|
expect(executedTab.tempName).to.equal('foo')
|
||||||
|
expect(wrapper.find('#open-workspace-btn').exists()).to.equal(false)
|
||||||
|
expect($router.push.called).to.equal(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Doesn\'t redirect and show the button if there is an error', async () => {
|
||||||
|
const state = {
|
||||||
|
tabs: []
|
||||||
|
}
|
||||||
|
const mutations = {
|
||||||
|
setCurrentTabId: sinon.stub().callsFake(realMutations.setCurrentTabId),
|
||||||
|
setDb: sinon.stub().callsFake(realMutations.setDb)
|
||||||
|
}
|
||||||
|
const actions = {
|
||||||
|
addTab: sinon.stub().callsFake(realActions.addTab)
|
||||||
|
}
|
||||||
|
const store = new Vuex.Store({ state, mutations, actions })
|
||||||
|
const $route = {
|
||||||
|
path: '/workspace',
|
||||||
|
query: {
|
||||||
|
data_url: 'https://my-url/test.db',
|
||||||
|
data_format: 'sqlite',
|
||||||
|
inquiry_url: 'https://my-url/test_inquiries.json',
|
||||||
|
inquiry_id: [1],
|
||||||
|
maximize: 'dataView'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const $router = { push: sinon.stub() }
|
||||||
|
|
||||||
|
const readFile = sinon.stub(fu, 'readFile')
|
||||||
|
const dataRes = new Response()
|
||||||
|
dataRes.blob = sinon.stub().rejects(new Error('Something is wrong'))
|
||||||
|
readFile.onCall(0).returns(Promise.resolve(dataRes))
|
||||||
|
|
||||||
|
const inquiriesRes = new Response()
|
||||||
|
inquiriesRes.json = sinon.stub().resolves({
|
||||||
|
version: 2,
|
||||||
|
inquiries: [{ id: 1 }]
|
||||||
|
})
|
||||||
|
readFile.onCall(1).returns(Promise.resolve(inquiriesRes))
|
||||||
|
sinon.stub(database, 'getNewDatabase').returns({
|
||||||
|
loadDb: sinon.stub().resolves()
|
||||||
|
})
|
||||||
|
|
||||||
|
const wrapper = mount(LoadView, {
|
||||||
|
store,
|
||||||
|
mocks: { $route, $router },
|
||||||
|
stubs: ['router-link']
|
||||||
|
})
|
||||||
|
|
||||||
|
await flushPromises()
|
||||||
|
expect(wrapper.find('#open-workspace-btn').exists()).to.equal(true)
|
||||||
|
expect($router.push.called).to.equal(false)
|
||||||
|
expect(wrapper.find('#logs').text()).to.include('Something is wrong')
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -19,7 +19,9 @@ describe('Inquiries.vue', () => {
|
|||||||
predefinedInquiries: []
|
predefinedInquiries: []
|
||||||
}
|
}
|
||||||
const mutations = {
|
const mutations = {
|
||||||
updatePredefinedInquiries: sinon.stub()
|
setPredefinedInquiriesLoaded: sinon.stub(),
|
||||||
|
updatePredefinedInquiries: sinon.stub(),
|
||||||
|
setLoadingPredefinedInquiries: sinon.stub()
|
||||||
}
|
}
|
||||||
const store = new Vuex.Store({ state, mutations })
|
const store = new Vuex.Store({ state, mutations })
|
||||||
const wrapper = shallowMount(Inquiries, { store })
|
const wrapper = shallowMount(Inquiries, { store })
|
||||||
@@ -327,6 +329,7 @@ describe('Inquiries.vue', () => {
|
|||||||
sinon.stub(storedInquiries, 'getStoredInquiries').returns([inquiryInStorage])
|
sinon.stub(storedInquiries, 'getStoredInquiries').returns([inquiryInStorage])
|
||||||
|
|
||||||
const state = {
|
const state = {
|
||||||
|
tabs: [],
|
||||||
predefinedInquiries: []
|
predefinedInquiries: []
|
||||||
}
|
}
|
||||||
const actions = { addTab: sinon.stub().resolves(1) }
|
const actions = { addTab: sinon.stub().resolves(1) }
|
||||||
|
|||||||
@@ -406,4 +406,119 @@ describe('Tab.vue', () => {
|
|||||||
expect(wrapper.find('.above .sql-editor-panel').exists()).to.equal(true)
|
expect(wrapper.find('.above .sql-editor-panel').exists()).to.equal(true)
|
||||||
expect(wrapper.find('.bottomPane .run-result-panel').exists()).to.equal(true)
|
expect(wrapper.find('.bottomPane .run-result-panel').exists()).to.equal(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('Maximize top panel if maximized panel is above', () => {
|
||||||
|
const state = {
|
||||||
|
currentTabId: 1
|
||||||
|
}
|
||||||
|
const store = new Vuex.Store({ state, mutations })
|
||||||
|
const tab = {
|
||||||
|
id: 1,
|
||||||
|
name: 'foo',
|
||||||
|
query: 'SELECT * FROM foo; CREATE TABLE bar(a,b);',
|
||||||
|
viewType: 'chart',
|
||||||
|
viewOptions: {},
|
||||||
|
layout: {
|
||||||
|
sqlEditor: 'above',
|
||||||
|
table: 'bottom',
|
||||||
|
dataView: 'hidden'
|
||||||
|
},
|
||||||
|
maximize: 'sqlEditor',
|
||||||
|
isPredefined: false,
|
||||||
|
result: null,
|
||||||
|
isGettingResults: false,
|
||||||
|
error: null,
|
||||||
|
time: 0,
|
||||||
|
isSaved: true
|
||||||
|
}
|
||||||
|
|
||||||
|
const wrapper = mount(Tab, {
|
||||||
|
attachTo: place,
|
||||||
|
store,
|
||||||
|
stubs: ['chart'],
|
||||||
|
propsData: {
|
||||||
|
tab
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(wrapper.find('.above').element.parentElement.style.height)
|
||||||
|
.to.equal('100%')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Maximize bottom panel if maximized panel is below', () => {
|
||||||
|
const state = {
|
||||||
|
currentTabId: 1
|
||||||
|
}
|
||||||
|
const store = new Vuex.Store({ state, mutations })
|
||||||
|
const tab = {
|
||||||
|
id: 1,
|
||||||
|
name: 'foo',
|
||||||
|
query: 'SELECT * FROM foo; CREATE TABLE bar(a,b);',
|
||||||
|
viewType: 'chart',
|
||||||
|
viewOptions: {},
|
||||||
|
layout: {
|
||||||
|
sqlEditor: 'above',
|
||||||
|
table: 'bottom',
|
||||||
|
dataView: 'hidden'
|
||||||
|
},
|
||||||
|
maximize: 'table',
|
||||||
|
isPredefined: false,
|
||||||
|
result: null,
|
||||||
|
isGettingResults: false,
|
||||||
|
error: null,
|
||||||
|
time: 0,
|
||||||
|
isSaved: true
|
||||||
|
}
|
||||||
|
|
||||||
|
const wrapper = mount(Tab, {
|
||||||
|
attachTo: place,
|
||||||
|
store,
|
||||||
|
stubs: ['chart'],
|
||||||
|
propsData: {
|
||||||
|
tab
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(wrapper.find('.bottomPane').element.parentElement.style.height)
|
||||||
|
.to.equal('100%')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Panel size is 50 is nothing to maximize', () => {
|
||||||
|
const state = {
|
||||||
|
currentTabId: 1
|
||||||
|
}
|
||||||
|
const store = new Vuex.Store({ state, mutations })
|
||||||
|
const tab = {
|
||||||
|
id: 1,
|
||||||
|
name: 'foo',
|
||||||
|
query: 'SELECT * FROM foo; CREATE TABLE bar(a,b);',
|
||||||
|
viewType: 'chart',
|
||||||
|
viewOptions: {},
|
||||||
|
layout: {
|
||||||
|
sqlEditor: 'above',
|
||||||
|
table: 'bottom',
|
||||||
|
dataView: 'hidden'
|
||||||
|
},
|
||||||
|
isPredefined: false,
|
||||||
|
result: null,
|
||||||
|
isGettingResults: false,
|
||||||
|
error: null,
|
||||||
|
time: 0,
|
||||||
|
isSaved: true
|
||||||
|
}
|
||||||
|
|
||||||
|
const wrapper = mount(Tab, {
|
||||||
|
attachTo: place,
|
||||||
|
store,
|
||||||
|
stubs: ['chart'],
|
||||||
|
propsData: {
|
||||||
|
tab
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(wrapper.find('.above').element.parentElement.style.height)
|
||||||
|
.to.equal('50%')
|
||||||
|
expect(wrapper.find('.bottomPane').element.parentElement.style.height)
|
||||||
|
.to.equal('50%')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -12,9 +12,11 @@ describe('Workspace.vue', () => {
|
|||||||
tabs: []
|
tabs: []
|
||||||
}
|
}
|
||||||
const store = new Vuex.Store({ state, actions, mutations })
|
const store = new Vuex.Store({ state, actions, mutations })
|
||||||
|
const $route = { path: '/workspace', query: {} }
|
||||||
mount(Workspace, {
|
mount(Workspace, {
|
||||||
store,
|
store,
|
||||||
stubs: ['router-link']
|
stubs: ['router-link'],
|
||||||
|
mocks: { $route }
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(state.tabs[0].query).to.include('Your database is empty.')
|
expect(state.tabs[0].query).to.include('Your database is empty.')
|
||||||
@@ -24,4 +26,20 @@ describe('Workspace.vue', () => {
|
|||||||
expect(state.tabs[0].viewOptions).to.equal(undefined)
|
expect(state.tabs[0].viewOptions).to.equal(undefined)
|
||||||
expect(state.tabs[0].isSaved).to.equal(false)
|
expect(state.tabs[0].isSaved).to.equal(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('Collapse schema if hide_schema is 1', () => {
|
||||||
|
const state = {
|
||||||
|
db: {},
|
||||||
|
tabs: []
|
||||||
|
}
|
||||||
|
const store = new Vuex.Store({ state, actions, mutations })
|
||||||
|
const $route = { path: '/workspace', query: { hide_schema: '1' } }
|
||||||
|
const vm = mount(Workspace, {
|
||||||
|
store,
|
||||||
|
stubs: ['router-link'],
|
||||||
|
mocks: { $route }
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(vm.find('#schema-container').element.offsetWidth).to.equal(0)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user