From 931cf380bc76a0449d4176c4b92db0c603f19c74 Mon Sep 17 00:00:00 2001 From: lana-k Date: Wed, 8 Oct 2025 19:39:56 +0200 Subject: [PATCH] #127 tests --- package.json | 2 +- tests/views/MainView/MainMenu.spec.js | 2 +- .../Tabs/Tab/DataView/DataView.spec.js | 1 - .../MainView/Workspace/Tabs/Tabs.spec.js | 229 +++++++++++++++++- 4 files changed, 224 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 1ba49c7..82203f5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sqliteviz", - "version": "0.26.1", + "version": "0.27.0", "license": "Apache-2.0", "private": true, "type": "module", diff --git a/tests/views/MainView/MainMenu.spec.js b/tests/views/MainView/MainMenu.spec.js index 586b24d..9d1727e 100644 --- a/tests/views/MainView/MainMenu.spec.js +++ b/tests/views/MainView/MainMenu.spec.js @@ -1146,7 +1146,7 @@ describe('MainMenu.vue', () => { name: 'bar', id: 2, query: 'SELECT * FROM foo', - chart: [] + viewType: 'chart' }) } const store = createStore({ state, mutations, actions }) diff --git a/tests/views/MainView/Workspace/Tabs/Tab/DataView/DataView.spec.js b/tests/views/MainView/Workspace/Tabs/Tab/DataView/DataView.spec.js index c798adc..49625c7 100644 --- a/tests/views/MainView/Workspace/Tabs/Tab/DataView/DataView.spec.js +++ b/tests/views/MainView/Workspace/Tabs/Tab/DataView/DataView.spec.js @@ -182,7 +182,6 @@ describe('DataView.vue', () => { // Wait untill prepareCopy is finished await wrapper.vm.$refs.viewComponent.prepareCopy.returnValues[0] - await nextTick() await nextTick() // The dialog is shown... diff --git a/tests/views/MainView/Workspace/Tabs/Tabs.spec.js b/tests/views/MainView/Workspace/Tabs/Tabs.spec.js index d830a28..b7637a5 100644 --- a/tests/views/MainView/Workspace/Tabs/Tabs.spec.js +++ b/tests/views/MainView/Workspace/Tabs/Tabs.spec.js @@ -5,6 +5,9 @@ import mutations from '@/store/mutations' import { createStore } from 'vuex' import Tabs from '@/views/MainView/Workspace/Tabs' import eventBus from '@/lib/eventBus' +import { nextTick } from 'vue' +import cIo from '@/lib/utils/clipboardIo' +import csv from '@/lib/csv' describe('Tabs.vue', () => { let clock @@ -46,7 +49,7 @@ describe('Tabs.vue', () => { id: 1, name: 'foo', query: 'select * from foo', - chart: [], + viewType: 'chart', isSaved: true }, { @@ -54,7 +57,7 @@ describe('Tabs.vue', () => { name: null, tempName: 'Untitled', query: '', - chart: [], + viewType: 'chart', isSaved: false } ], @@ -97,7 +100,7 @@ describe('Tabs.vue', () => { id: 1, name: 'foo', query: 'select * from foo', - chart: [], + viewType: 'chart', isSaved: true }, { @@ -105,7 +108,7 @@ describe('Tabs.vue', () => { name: null, tempName: 'Untitled', query: '', - chart: [], + viewType: 'chart', isSaved: false } ], @@ -436,7 +439,7 @@ describe('Tabs.vue', () => { id: 1, name: 'foo', query: 'select * from foo', - chart: [], + viewType: 'chart', isSaved: true }, { @@ -444,7 +447,7 @@ describe('Tabs.vue', () => { name: null, tempName: 'Untitled', query: '', - chart: [], + viewType: 'chart', isSaved: false } ], @@ -477,7 +480,7 @@ describe('Tabs.vue', () => { id: 1, name: 'foo', query: 'select * from foo', - chart: [], + viewType: 'chart', isSaved: true } ], @@ -501,4 +504,216 @@ describe('Tabs.vue', () => { expect(event.preventDefault.calledOnce).to.equal(false) wrapper.unmount() }) + + it('Copy image to clipboard dialog works in the context of the tab', async () => { + // mock store state - 2 inquiries open + const state = { + tabs: [ + { + id: 1, + name: 'foo', + query: 'select * from foo', + viewType: 'chart', + viewOptions: undefined, + layout: { + sqlEditor: 'above', + table: 'hidden', + dataView: 'bottom' + }, + isSaved: true + }, + { + id: 2, + name: null, + tempName: 'Untitled', + query: '', + viewType: 'chart', + viewOptions: undefined, + layout: { + sqlEditor: 'above', + table: 'hidden', + dataView: 'bottom' + }, + isSaved: false + } + ], + currentTabId: 2 + } + const store = createStore({ state }) + sinon.stub(cIo, 'copyImage') + + // mount the component + const wrapper = mount(Tabs, { + attachTo: document.body, + global: { + stubs: { teleport: true, transition: true, RouterLink: true }, + plugins: [store] + } + }) + const firstTabDataView = wrapper + .findAllComponents({ name: 'Tab' })[0] + .findComponent({ name: 'DataView' }) + + const secondTabDataView = wrapper + .findAllComponents({ name: 'Tab' })[1] + .findComponent({ name: 'DataView' }) + + // Stub prepareCopy method so it takes long and copy dialog will be shown + sinon + .stub(firstTabDataView.vm.$refs.viewComponent, 'prepareCopy') + .callsFake(async () => { + await clock.tick(5000) + return 'prepareCopy result in tab 1' + }) + + sinon + .stub(secondTabDataView.vm.$refs.viewComponent, 'prepareCopy') + .callsFake(async () => { + await clock.tick(5000) + return 'prepareCopy result in tab 2' + }) + + // Click Copy to clipboard button in the second tab + const copyBtn = secondTabDataView.findComponent({ + ref: 'copyToClipboardBtn' + }) + await copyBtn.trigger('click') + + // The dialog is shown... + expect(wrapper.find('.dialog.vfm .vfm__content').exists()).to.equal(true) + expect(wrapper.find('.dialog.vfm .dialog-header').text()).to.contain( + 'Copy to clipboard' + ) + + // Switch to microtasks (let prepareCopy run) + await clock.tick(0) + // Wait untill prepareCopy is finished + await secondTabDataView.vm.$refs.viewComponent.prepareCopy.returnValues[0] + + await nextTick() + + // Click copy button in the dialog + await wrapper + .find('.dialog-buttons-container button.primary') + .trigger('click') + + // The dialog is not shown... + await clock.tick(100) + expect(wrapper.find('.dialog.vfm .vfm__content').exists()).to.equal(false) + + // copyImage is called with prepare copy result calculated in tab 2, not null + // i.e. the dialog works in the tab 2 context + expect( + cIo.copyImage.calledOnceWith('prepareCopy result in tab 2') + ).to.equal(true) + + wrapper.unmount() + }) + + it('Copy CSV to clipboard dialog works in the context of the tab', async () => { + // mock store state - 2 inquiries open + const state = { + tabs: [ + { + id: 1, + name: 'foo', + query: 'select * from foo', + viewType: 'chart', + viewOptions: undefined, + layout: { + sqlEditor: 'above', + table: 'bottom', + dataView: 'hidden' + }, + result: { + columns: ['id', 'name'], + values: { + id: [1, 2, 3], + name: ['Gryffindor', 'Hufflepuff'] + } + }, + isSaved: true + }, + { + id: 2, + name: null, + tempName: 'Untitled', + query: '', + viewType: 'chart', + viewOptions: undefined, + layout: { + sqlEditor: 'above', + table: 'bottom', + dataView: 'hidden' + }, + result: { + columns: ['name', 'points'], + values: { + name: ['Gryffindor', 'Hufflepuff', 'Ravenclaw', 'Slytherin'], + points: [100, 90, 95, 80] + } + }, + isSaved: false + } + ], + currentTabId: 2 + } + const store = createStore({ state }) + sinon.stub(cIo, 'copyText') + + // mount the component + const wrapper = mount(Tabs, { + attachTo: document.body, + global: { + stubs: { teleport: true, transition: true, RouterLink: true }, + plugins: [store] + } + }) + + const secondTabRunResult = wrapper + .findAllComponents({ name: 'Tab' })[1] + .findComponent({ name: 'RunResult' }) + + // Stub prepareCopy method so it takes long and copy dialog will be shown + sinon.stub(csv, 'serialize').callsFake(() => { + clock.tick(5000) + return 'csv serialize result' + }) + + // Click Copy to clipboard button in the second tab + const copyBtn = secondTabRunResult.findComponent({ + ref: 'copyToClipboardBtn' + }) + await copyBtn.trigger('click') + + // The dialog is shown... + expect(wrapper.find('.dialog.vfm .vfm__content').exists()).to.equal(true) + expect(wrapper.find('.dialog.vfm .dialog-header').text()).to.contain( + 'Copy to clipboard' + ) + + // Switch to microtasks (let prepareCopy run) + await clock.tick(0) + await nextTick() + + // Click copy button in the dialog + await wrapper + .find('.dialog-buttons-container button.primary') + .trigger('click') + + // The dialog is not shown... + await clock.tick(100) + expect(wrapper.find('.dialog.vfm .vfm__content').exists()).to.equal(false) + + // copyText is called with 'csv serialize result' calculated in tab 2, not null + // i.e. the dialog works in the tab 2 context + expect( + cIo.copyText.calledOnceWith( + 'csv serialize result', + 'CSV copied to clipboard successfully' + ) + ).to.equal(true) + + wrapper.unmount() + }) })