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",
"version": "0.26.0",
"version": "0.27.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "sqliteviz",
"version": "0.26.0",
"version": "0.27.1",
"license": "Apache-2.0",
"dependencies": {
"buffer": "^6.0.3",

View File

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

View File

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

View File

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

View File

@@ -178,6 +178,7 @@ export default {
this.openSaveModal()
},
openSaveModal() {
this.$modal.hide('inquiry-conflict')
this.errorMsg = null
this.name = ''
this.$modal.show('save')
@@ -190,8 +191,6 @@ export default {
this.saveInquiry()
},
async saveInquiry() {
const dataSet = this.currentInquiryTab.result
const tabView = this.currentInquiryTab.view
const eventName =
this.currentInquiryTab.name && this.name
? '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
this.$modal.hide('save')
this.$modal.hide('inquiry-conflict')

View File

@@ -1,13 +1,22 @@
import { expect } from 'chai'
import sinon from 'sinon'
import { shallowMount } from '@vue/test-utils'
import { shallowMount, mount } from '@vue/test-utils'
import { createStore } from 'vuex'
import App from '@/App.vue'
import storedInquiries from '@/lib/storedInquiries'
import actions from '@/store/actions'
import mutations from '@/store/mutations'
import { nextTick } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
import { routes } from '@/router'
describe('App.vue', () => {
let clock
beforeEach(() => {
clock = sinon.useFakeTimers()
})
afterEach(() => {
sinon.restore()
})
@@ -92,4 +101,134 @@ describe('App.vue', () => {
{ 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')
.trigger('click')
await nextTick()
// check that the dialog is closed
await clock.tick(100)
expect(wrapper.find('.dialog.vfm').exists()).to.equal(false)
@@ -694,8 +692,6 @@ describe('MainMenu.vue', () => {
.find(button => button.text() === 'Save')
.trigger('click')
await nextTick()
// check that the dialog is closed
await clock.tick(100)
expect(wrapper.find('.dialog.vfm').exists()).to.equal(false)
@@ -725,8 +721,6 @@ describe('MainMenu.vue', () => {
.find(button => button.text() === 'Overwrite')
.trigger('click')
await nextTick()
// check that the dialog is closed
await clock.tick(100)
expect(wrapper.find('.dialog.vfm').exists()).to.equal(false)
@@ -759,8 +753,7 @@ describe('MainMenu.vue', () => {
createdAt: '2025-05-14T15:30:00Z'
}
],
tabs: [tab],
db: {}
tabs: [tab]
}
const mutations = {
updateTab: sinon.stub()
@@ -795,22 +788,29 @@ describe('MainMenu.vue', () => {
})
await wrapper.find('#save-btn').trigger('click')
// check that the conflict dialog is open
expect(wrapper.find('.dialog.vfm').exists()).to.equal(true)
expect(wrapper.find('.dialog.vfm .dialog-header').text()).to.contain(
'Inquiry saving conflict'
)
await clock.tick(100)
// find "Save as new" in the dialog and click
await wrapper
.findAll('.dialog-buttons-container button')
.find(button => button.text() === 'Save as new')
.trigger('click')
await nextTick()
// Hiding any dialog is done with tiny animation. Give time to finish it:
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
await wrapper.find('.dialog-body input').setValue('foo_new')
@@ -820,8 +820,6 @@ describe('MainMenu.vue', () => {
.find(button => button.text() === 'Save')
.trigger('click')
await nextTick()
// check that the dialog is closed
await clock.tick(100)
expect(wrapper.find('.dialog.vfm').exists()).to.equal(false)
@@ -1062,8 +1060,6 @@ describe('MainMenu.vue', () => {
.find(button => button.text() === 'Save')
.trigger('click')
await nextTick()
// check that the dialog is closed
await clock.tick(100)
expect(wrapper.find('.dialog.vfm').exists()).to.equal(false)
@@ -1172,8 +1168,6 @@ describe('MainMenu.vue', () => {
.find(button => button.text() === 'Save')
.trigger('click')
await nextTick()
// check that the dialog is closed
await clock.tick(100)
expect(wrapper.find('.dialog.vfm').exists()).to.equal(false)
@@ -1206,19 +1200,6 @@ describe('MainMenu.vue', () => {
// check that 'inquirySaved' event was triggered on eventBus
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 () => {
@@ -1364,8 +1345,6 @@ describe('MainMenu.vue', () => {
.find(button => button.text() === 'Save')
.trigger('click')
await nextTick()
// check that the dialog is closed
await clock.tick(100)
expect(wrapper.find('.dialog.vfm').exists()).to.equal(false)

View File

@@ -228,7 +228,6 @@ describe('DataView.vue', () => {
// Wait untill prepareCopy is finished
await wrapper.vm.$refs.viewComponent.prepareCopy.returnValues[0]
await nextTick()
// The dialog is not shown...
await clock.tick(100)
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)
await clock.tick(0)
await nextTick()
// The dialog is not shown...
await clock.tick(100)