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

5 Commits

Author SHA1 Message Date
lana-k
b59c21c14e update tests 2025-10-19 18:25:03 +02:00
lana-k
4ed4b54a28 fix warnings 2025-10-17 21:01:40 +02:00
lana-k
2c2bb7d6d3 0.27.1 2025-10-16 22:29:56 +02:00
lana-k
efbd985b36 #128 tests 2025-10-16 22:28:33 +02:00
lana-k
9cf7d0e5dc #128 fix save and close 2025-10-09 22:49:54 +02:00
9 changed files with 157 additions and 52 deletions

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "sqliteviz", "name": "sqliteviz",
"version": "0.26.0", "version": "0.27.1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "sqliteviz", "name": "sqliteviz",
"version": "0.26.0", "version": "0.27.1",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"buffer": "^6.0.3", "buffer": "^6.0.3",

View File

@@ -1,6 +1,6 @@
{ {
"name": "sqliteviz", "name": "sqliteviz",
"version": "0.27.0", "version": "0.27.1",
"license": "Apache-2.0", "license": "Apache-2.0",
"private": true, "private": true,
"type": "module", "type": "module",

View File

@@ -5,7 +5,7 @@
:clickToClose="false" :clickToClose="false"
:contentTransition="{ name: 'loading-dialog' }" :contentTransition="{ name: 'loading-dialog' }"
:overlayTransition="{ name: 'loading-dialog' }" :overlayTransition="{ name: 'loading-dialog' }"
@update:modelValue="$emit('update:modelValue', $event)" @update:model-value="$emit('update:modelValue', $event)"
> >
<div class="dialog-header"> <div class="dialog-header">
{{ title }} {{ title }}
@@ -60,12 +60,12 @@ export default {
title: String, title: String,
loading: Boolean loading: Boolean
}, },
emits: ['cancel', 'action', 'update:modelValue'],
data() { data() {
return { return {
show: this.modelValue show: this.modelValue
} }
}, },
emits: ['cancel', 'action', 'update:modelValue'],
watch: { watch: {
modelValue() { modelValue() {
this.show = this.modelValue this.show = this.modelValue

View File

@@ -7,7 +7,7 @@ import LoadView from '@/views/LoadView'
import store from '@/store' import store from '@/store'
import database from '@/lib/database' import database from '@/lib/database'
const routes = [ export const routes = [
{ {
path: '/', path: '/',
name: 'Welcome', name: 'Welcome',

View File

@@ -178,6 +178,7 @@ export default {
this.openSaveModal() this.openSaveModal()
}, },
openSaveModal() { openSaveModal() {
this.$modal.hide('inquiry-conflict')
this.errorMsg = null this.errorMsg = null
this.name = '' this.name = ''
this.$modal.show('save') this.$modal.show('save')
@@ -190,8 +191,6 @@ export default {
this.saveInquiry() this.saveInquiry()
}, },
async saveInquiry() { async saveInquiry() {
const dataSet = this.currentInquiryTab.result
const tabView = this.currentInquiryTab.view
const eventName = const eventName =
this.currentInquiryTab.name && this.name this.currentInquiryTab.name && this.name
? 'inquiry.saveAs' ? 'inquiry.saveAs'
@@ -217,16 +216,6 @@ export default {
} }
}) })
// Restore data:
// e.g. if we save predefined inquiry the tab will be created again
// (because of new id) and
// it will be without sql result and has default view - table.
// That's why we need to restore data and view
this.$nextTick(() => {
this.currentInquiryTab.result = dataSet
this.currentInquiryTab.view = tabView
})
// Hide dialogs // Hide dialogs
this.$modal.hide('save') this.$modal.hide('save')
this.$modal.hide('inquiry-conflict') this.$modal.hide('inquiry-conflict')

View File

@@ -1,13 +1,22 @@
import { expect } from 'chai' import { expect } from 'chai'
import sinon from 'sinon' import sinon from 'sinon'
import { shallowMount } from '@vue/test-utils' import { shallowMount, mount } from '@vue/test-utils'
import { createStore } from 'vuex' import { createStore } from 'vuex'
import App from '@/App.vue' import App from '@/App.vue'
import storedInquiries from '@/lib/storedInquiries' import storedInquiries from '@/lib/storedInquiries'
import actions from '@/store/actions'
import mutations from '@/store/mutations' import mutations from '@/store/mutations'
import { nextTick } from 'vue' import { nextTick } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
import { routes } from '@/router'
describe('App.vue', () => { describe('App.vue', () => {
let clock
beforeEach(() => {
clock = sinon.useFakeTimers()
})
afterEach(() => { afterEach(() => {
sinon.restore() sinon.restore()
}) })
@@ -92,4 +101,134 @@ describe('App.vue', () => {
{ id: 3, name: 'bar' } { id: 3, name: 'bar' }
]) ])
}) })
it('Closes with saving and does not change the next tab', async () => {
const inquiries = [
{
id: 1,
name: 'foo',
query: 'SELECT * FROM foo',
viewType: 'chart',
viewOptions: {},
createdAt: '2020-11-07T20:57:04.492Z'
},
{
id: 2,
name: 'bar',
query: 'SELECT * FROM bar',
viewType: 'chart',
viewOptions: {},
createdAt: '2020-11-07T20:57:04.492Z'
}
]
sinon.stub(storedInquiries, 'getStoredInquiries').returns(inquiries)
const tab1 = {
id: 1,
name: 'foo',
query: 'select * from foo',
viewType: 'chart',
viewOptions: {},
layout: {
sqlEditor: 'above',
table: 'bottom',
dataView: 'hidden'
},
result: {
columns: ['name', 'points'],
values: {
name: ['Gryffindor', 'Hufflepuff', 'Ravenclaw', 'Slytherin'],
points: [100, 90, 95, 80]
}
},
isSaved: false
}
const tab2 = {
id: 2,
name: 'bar',
query: 'SELECT * FROM bar',
viewType: 'chart',
viewOptions: {},
layout: {
sqlEditor: 'above',
table: 'hidden',
dataView: 'bottom'
},
result: {
columns: ['id'],
values: {
id: [1, 2, 3]
}
},
isSaved: true
}
// mock store state
const state = {
tabs: [tab1, tab2],
currentTabId: 1,
currentTab: tab1,
db: {},
inquiries
}
const store = createStore({ state, mutations, actions })
const router = createRouter({
history: createWebHistory(),
routes: routes
})
router.push('/workspace')
// After this line, router is ready
await router.isReady()
const wrapper = mount(App, {
attachTo: document.body,
global: {
stubs: {
'router-link': true,
teleport: true,
transition: false,
schema: true,
AppDiagnosticInfo: true,
DataView: {
template: '<div></div>',
methods: { getOptionsForSave: sinon.stub() }
}
},
plugins: [store, router]
}
})
// click on the close icon of the first tab
const firstTabCloseIcon = wrapper.findAll('.tab')[0].find('.close-icon')
await firstTabCloseIcon.trigger('click')
// find 'Save and close' in the dialog
const closeBtn = wrapper
.findAll('.dialog-buttons-container button')
.find(button => button.text() === 'Save and close')
// click 'Save and close' in the dialog
await closeBtn.trigger('click')
await nextTick()
await nextTick()
// check that tab is closed
expect(wrapper.findAllComponents({ name: 'Tab' })).to.have.lengthOf(1)
// check that the open tab didn't change
const firstTab = wrapper.findComponent({ name: 'Tab' })
expect(firstTab.props('tab').name).to.equal('bar')
expect(firstTab.props('tab').result).to.eql({
columns: ['id'],
values: {
id: [1, 2, 3]
}
})
expect(firstTab.props('tab')).to.eql(tab2)
// check that the dialog is closed
await clock.tick(100)
await nextTick()
expect(wrapper.find('.dialog.vfm .vfm__content').exists()).to.equal(false)
wrapper.unmount()
})
}) })

View File

@@ -608,8 +608,6 @@ describe('MainMenu.vue', () => {
.find(button => button.text() === 'Overwrite') .find(button => button.text() === 'Overwrite')
.trigger('click') .trigger('click')
await nextTick()
// check that the dialog is closed // check that the dialog is closed
await clock.tick(100) await clock.tick(100)
expect(wrapper.find('.dialog.vfm').exists()).to.equal(false) expect(wrapper.find('.dialog.vfm').exists()).to.equal(false)
@@ -694,8 +692,6 @@ describe('MainMenu.vue', () => {
.find(button => button.text() === 'Save') .find(button => button.text() === 'Save')
.trigger('click') .trigger('click')
await nextTick()
// check that the dialog is closed // check that the dialog is closed
await clock.tick(100) await clock.tick(100)
expect(wrapper.find('.dialog.vfm').exists()).to.equal(false) expect(wrapper.find('.dialog.vfm').exists()).to.equal(false)
@@ -725,8 +721,6 @@ describe('MainMenu.vue', () => {
.find(button => button.text() === 'Overwrite') .find(button => button.text() === 'Overwrite')
.trigger('click') .trigger('click')
await nextTick()
// check that the dialog is closed // check that the dialog is closed
await clock.tick(100) await clock.tick(100)
expect(wrapper.find('.dialog.vfm').exists()).to.equal(false) expect(wrapper.find('.dialog.vfm').exists()).to.equal(false)
@@ -759,8 +753,7 @@ describe('MainMenu.vue', () => {
createdAt: '2025-05-14T15:30:00Z' createdAt: '2025-05-14T15:30:00Z'
} }
], ],
tabs: [tab], tabs: [tab]
db: {}
} }
const mutations = { const mutations = {
updateTab: sinon.stub() updateTab: sinon.stub()
@@ -795,22 +788,29 @@ describe('MainMenu.vue', () => {
}) })
await wrapper.find('#save-btn').trigger('click') await wrapper.find('#save-btn').trigger('click')
// check that the conflict dialog is open // check that the conflict dialog is open
expect(wrapper.find('.dialog.vfm').exists()).to.equal(true) expect(wrapper.find('.dialog.vfm').exists()).to.equal(true)
expect(wrapper.find('.dialog.vfm .dialog-header').text()).to.contain( expect(wrapper.find('.dialog.vfm .dialog-header').text()).to.contain(
'Inquiry saving conflict' 'Inquiry saving conflict'
) )
await clock.tick(100)
// find "Save as new" in the dialog and click // find "Save as new" in the dialog and click
await wrapper await wrapper
.findAll('.dialog-buttons-container button') .findAll('.dialog-buttons-container button')
.find(button => button.text() === 'Save as new') .find(button => button.text() === 'Save as new')
.trigger('click') .trigger('click')
await nextTick() // Hiding any dialog is done with tiny animation. Give time to finish it:
await clock.tick(100) await clock.tick(100)
// Note: don't call nextTick before clock.tick. That leads to extra trap in
// trapStack and the test fails with focus-trap error in afterEach hook
// when unmount the component
// check that only one dialog open
expect(wrapper.findAll('.dialog.vfm').length).to.equal(1)
// enter the new name // enter the new name
await wrapper.find('.dialog-body input').setValue('foo_new') await wrapper.find('.dialog-body input').setValue('foo_new')
@@ -820,8 +820,6 @@ describe('MainMenu.vue', () => {
.find(button => button.text() === 'Save') .find(button => button.text() === 'Save')
.trigger('click') .trigger('click')
await nextTick()
// check that the dialog is closed // check that the dialog is closed
await clock.tick(100) await clock.tick(100)
expect(wrapper.find('.dialog.vfm').exists()).to.equal(false) expect(wrapper.find('.dialog.vfm').exists()).to.equal(false)
@@ -1062,8 +1060,6 @@ describe('MainMenu.vue', () => {
.find(button => button.text() === 'Save') .find(button => button.text() === 'Save')
.trigger('click') .trigger('click')
await nextTick()
// check that the dialog is closed // check that the dialog is closed
await clock.tick(100) await clock.tick(100)
expect(wrapper.find('.dialog.vfm').exists()).to.equal(false) expect(wrapper.find('.dialog.vfm').exists()).to.equal(false)
@@ -1172,8 +1168,6 @@ describe('MainMenu.vue', () => {
.find(button => button.text() === 'Save') .find(button => button.text() === 'Save')
.trigger('click') .trigger('click')
await nextTick()
// check that the dialog is closed // check that the dialog is closed
await clock.tick(100) await clock.tick(100)
expect(wrapper.find('.dialog.vfm').exists()).to.equal(false) expect(wrapper.find('.dialog.vfm').exists()).to.equal(false)
@@ -1206,19 +1200,6 @@ describe('MainMenu.vue', () => {
// check that 'inquirySaved' event was triggered on eventBus // check that 'inquirySaved' event was triggered on eventBus
expect(eventBus.$emit.calledOnceWith('inquirySaved')).to.equal(true) expect(eventBus.$emit.calledOnceWith('inquirySaved')).to.equal(true)
// We saved predefined inquiry, so the tab will be created again
// (because of new id) and it will be without sql result and has default view - table.
// That's why we need to restore data and view.
// Check that result and view are preserved in the currentTab:
expect(state.currentTab.viewType).to.equal('chart')
expect(state.currentTab.result).to.eql({
columns: ['id', 'name'],
values: [
[1, 'Harry Potter'],
[2, 'Drako Malfoy']
]
})
}) })
it('Cancel saving', async () => { it('Cancel saving', async () => {
@@ -1364,8 +1345,6 @@ describe('MainMenu.vue', () => {
.find(button => button.text() === 'Save') .find(button => button.text() === 'Save')
.trigger('click') .trigger('click')
await nextTick()
// check that the dialog is closed // check that the dialog is closed
await clock.tick(100) await clock.tick(100)
expect(wrapper.find('.dialog.vfm').exists()).to.equal(false) expect(wrapper.find('.dialog.vfm').exists()).to.equal(false)

View File

@@ -228,7 +228,6 @@ describe('DataView.vue', () => {
// Wait untill prepareCopy is finished // Wait untill prepareCopy is finished
await wrapper.vm.$refs.viewComponent.prepareCopy.returnValues[0] await wrapper.vm.$refs.viewComponent.prepareCopy.returnValues[0]
await nextTick()
// The dialog is not shown... // The dialog is not shown...
await clock.tick(100) await clock.tick(100)
expect(wrapper.find('.dialog.vfm .vfm__content').exists()).to.equal(false) expect(wrapper.find('.dialog.vfm .vfm__content').exists()).to.equal(false)

View File

@@ -139,7 +139,6 @@ describe('RunResult.vue', () => {
// Switch to microtasks (let serialize run) // Switch to microtasks (let serialize run)
await clock.tick(0) await clock.tick(0)
await nextTick()
// The dialog is not shown... // The dialog is not shown...
await clock.tick(100) await clock.tick(100)