mirror of
https://github.com/lana-k/sqliteviz.git
synced 2026-08-05 07:19:17 +08:00
531 lines
14 KiB
JavaScript
531 lines
14 KiB
JavaScript
import { expect } from 'chai'
|
|
import sinon from 'sinon'
|
|
import { mount, flushPromises } from '@vue/test-utils'
|
|
import Chart from '@/components/Chart.vue'
|
|
import chartHelper from '@/lib/chartHelper'
|
|
import * as dereference from 'react-chart-editor/lib/lib/dereference'
|
|
import fIo from '@/lib/utils/fileIo'
|
|
import { nextTick } from 'vue'
|
|
|
|
describe('Chart.vue', () => {
|
|
const $store = { state: { isWorkspaceVisible: true } }
|
|
|
|
afterEach(() => {
|
|
sinon.restore()
|
|
})
|
|
|
|
it('getOptionsForSave called with proper arguments', () => {
|
|
const wrapper = mount(Chart, {
|
|
global: {
|
|
mocks: { $store }
|
|
}
|
|
})
|
|
const vm = wrapper.vm
|
|
const stub = sinon.stub(chartHelper, 'getOptionsForSave').returns('result')
|
|
const chartData = vm.getOptionsForSave()
|
|
expect(stub.calledOnceWith(vm.state, vm.dataSources)).to.equal(true)
|
|
expect(chartData).to.equal('result')
|
|
wrapper.unmount()
|
|
})
|
|
|
|
it('emits update when plotly updates', async () => {
|
|
const wrapper = mount(Chart, {
|
|
global: {
|
|
mocks: { $store }
|
|
}
|
|
})
|
|
wrapper.findComponent({ ref: 'plotlyEditor' }).vm.$emit('update')
|
|
expect(wrapper.emitted('update')).to.have.lengthOf(1)
|
|
wrapper.unmount()
|
|
})
|
|
|
|
it('calls dereference and updates chart when dataSources is changed', async () => {
|
|
sinon.spy(dereference, 'default')
|
|
const dataSources = {
|
|
name: ['Gryffindor'],
|
|
points: [80]
|
|
}
|
|
|
|
const wrapper = mount(Chart, {
|
|
props: {
|
|
dataSources,
|
|
initOptions: {
|
|
data: [
|
|
{
|
|
type: 'bar',
|
|
mode: 'markers',
|
|
x: null,
|
|
xsrc: 'name',
|
|
meta: {
|
|
columnNames: {
|
|
x: 'name',
|
|
y: 'points',
|
|
text: 'points'
|
|
}
|
|
},
|
|
orientation: 'v',
|
|
y: null,
|
|
ysrc: 'points',
|
|
text: null,
|
|
textsrc: 'points'
|
|
}
|
|
],
|
|
layout: {},
|
|
frames: []
|
|
}
|
|
},
|
|
global: {
|
|
mocks: { $store }
|
|
}
|
|
})
|
|
await flushPromises()
|
|
|
|
expect(wrapper.find('svg.main-svg .overplot text').text()).to.equal('80')
|
|
const newDataSources = {
|
|
name: ['Gryffindor'],
|
|
points: [100]
|
|
}
|
|
|
|
await wrapper.setProps({ dataSources: newDataSources })
|
|
await flushPromises()
|
|
expect(dereference.default.called).to.equal(true)
|
|
expect(wrapper.find('svg.main-svg .overplot text').text()).to.equal('100')
|
|
})
|
|
|
|
it('the plot resizes when the container resizes', async () => {
|
|
const wrapper = mount(Chart, {
|
|
attachTo: document.body,
|
|
props: {
|
|
dataSources: null
|
|
},
|
|
global: {
|
|
mocks: { $store }
|
|
}
|
|
})
|
|
|
|
// don't call flushPromises here, otherwize resize observer will be call to often
|
|
// which causes ResizeObserver loop completed with undelivered notifications.
|
|
await nextTick()
|
|
|
|
const container =
|
|
wrapper.find('.chart-container').wrapperElement.parentElement
|
|
const plot = wrapper.find('.svg-container').wrapperElement
|
|
|
|
const initialContainerWidth = container.scrollWidth
|
|
const initialContainerHeight = container.scrollHeight
|
|
|
|
const initialPlotWidth = plot.scrollWidth
|
|
const initialPlotHeight = plot.scrollHeight
|
|
|
|
const newContainerWidth = initialContainerWidth * 2 || 1000
|
|
const newContainerHeight = initialContainerHeight * 2 || 2000
|
|
|
|
container.style.width = `${newContainerWidth}px`
|
|
container.style.height = `${newContainerHeight}px`
|
|
|
|
await flushPromises()
|
|
|
|
expect(plot.scrollWidth).not.to.equal(initialPlotWidth)
|
|
expect(plot.scrollHeight).not.to.equal(initialPlotHeight)
|
|
|
|
container.style.width = 'unset'
|
|
container.style.height = 'unset'
|
|
wrapper.unmount()
|
|
})
|
|
|
|
it("doesn't calls dereference when dataSources is null", async () => {
|
|
sinon.stub(dereference, 'default')
|
|
const dataSources = {
|
|
id: [1],
|
|
name: ['foo']
|
|
}
|
|
|
|
// mount the component
|
|
const wrapper = mount(Chart, {
|
|
props: { dataSources },
|
|
global: {
|
|
mocks: { $store }
|
|
}
|
|
})
|
|
|
|
await wrapper.setProps({ dataSources: null })
|
|
expect(dereference.default.calledOnce).to.equal(true)
|
|
wrapper.unmount()
|
|
})
|
|
|
|
it('saveAsPng', async () => {
|
|
sinon.spy(fIo, 'downloadFromUrl')
|
|
const dataSources = {
|
|
id: [1],
|
|
name: ['foo']
|
|
}
|
|
|
|
const wrapper = mount(Chart, {
|
|
props: { dataSources },
|
|
global: {
|
|
mocks: { $store }
|
|
}
|
|
})
|
|
sinon.spy(wrapper.vm, 'prepareCopy')
|
|
|
|
await nextTick() // chart is rendered
|
|
await wrapper.vm.saveAsPng()
|
|
|
|
const url = await wrapper.vm.prepareCopy.returnValues[0]
|
|
expect(wrapper.emitted().loadingImageCompleted.length).to.equal(1)
|
|
expect(fIo.downloadFromUrl.calledOnceWith(url, 'chart'))
|
|
wrapper.unmount()
|
|
})
|
|
|
|
it('dataSources are passed correctly', async () => {
|
|
const dataSources = {
|
|
name: ['Gryffindor'],
|
|
points: [80]
|
|
}
|
|
|
|
const wrapper = mount(Chart, {
|
|
attachTo: document.body,
|
|
props: {
|
|
dataSources,
|
|
showViewSettings: true
|
|
},
|
|
global: {
|
|
mocks: { $store }
|
|
}
|
|
})
|
|
await flushPromises()
|
|
await wrapper.find('button.js-add-button').wrapperElement.click()
|
|
|
|
await flushPromises()
|
|
|
|
await wrapper
|
|
.find('.field .dropdown-container .Select__indicator')
|
|
.wrapperElement.dispatchEvent(
|
|
new MouseEvent('mousedown', { bubbles: true })
|
|
)
|
|
|
|
expect(wrapper.find('.Select__menu').text()).to.contain('name' + 'points')
|
|
wrapper.unmount()
|
|
})
|
|
|
|
it('hides and shows controls depending on showViewSettings and resizes the plot', async () => {
|
|
const wrapper = mount(Chart, {
|
|
attachTo: document.body,
|
|
props: {
|
|
dataSources: null,
|
|
showViewSettings: false
|
|
},
|
|
global: {
|
|
mocks: { $store }
|
|
}
|
|
})
|
|
|
|
// don't call flushPromises here, otherwize resize observer will be call to often
|
|
// which causes ResizeObserver loop completed with undelivered notifications.
|
|
await nextTick()
|
|
|
|
const plot = wrapper.find('.svg-container').wrapperElement
|
|
await flushPromises()
|
|
const initialPlotWidth = plot.scrollWidth
|
|
const initialPlotHeight = plot.scrollHeight
|
|
|
|
expect(wrapper.find('.plotly_editor .editor_controls').exists()).to.equal(
|
|
false
|
|
)
|
|
|
|
await wrapper.setProps({ showViewSettings: true })
|
|
|
|
await flushPromises()
|
|
|
|
expect(plot.scrollWidth).not.to.equal(initialPlotWidth)
|
|
expect(plot.scrollHeight).to.equal(initialPlotHeight)
|
|
expect(wrapper.find('.plotly_editor .editor_controls').exists()).to.equal(
|
|
true
|
|
)
|
|
wrapper.unmount()
|
|
})
|
|
|
|
it('selections are shown in value viewer', async () => {
|
|
const dataSources = {
|
|
name: ['Gryffindor', 'Hufflepuff', 'Ravenclaw', 'Slytherin'],
|
|
points: [100, 90, 95, 80]
|
|
}
|
|
const wrapper = mount(Chart, {
|
|
attachTo: document.body,
|
|
props: {
|
|
dataSources,
|
|
initOptions: {
|
|
data: [
|
|
{
|
|
type: 'scatter',
|
|
mode: 'markers',
|
|
x: ['Gryffindor', 'Hufflepuff', 'Ravenclaw', 'Slytherin'],
|
|
xsrc: 'name',
|
|
meta: {
|
|
columnNames: {
|
|
x: 'name',
|
|
y: 'points'
|
|
}
|
|
},
|
|
y: [100, 90, 95, 80],
|
|
ysrc: 'points',
|
|
selectedpoints: [1, 2]
|
|
}
|
|
],
|
|
layout: {
|
|
autosize: true,
|
|
mapbox: {
|
|
style: 'open-street-map'
|
|
},
|
|
dragmode: 'select',
|
|
selections: [
|
|
{
|
|
xref: 'x',
|
|
yref: 'y',
|
|
line: {
|
|
width: 1,
|
|
dash: 'dot'
|
|
},
|
|
type: 'rect',
|
|
x0: 0.6238202370500439,
|
|
y0: 98.1184336198663,
|
|
x1: 2.4324242756804213,
|
|
y1: 87.03915950334289
|
|
}
|
|
],
|
|
title: {
|
|
subtitle: {
|
|
text: 'Click to enter Plot subtitle'
|
|
}
|
|
},
|
|
xaxis: {
|
|
range: [-0.20324846356453027, 3.20324846356453],
|
|
autorange: true,
|
|
type: 'category'
|
|
},
|
|
yaxis: {
|
|
range: [78.4909264565425, 101.5090735434575],
|
|
autorange: true,
|
|
type: 'linear'
|
|
}
|
|
},
|
|
frames: []
|
|
},
|
|
showViewSettings: true,
|
|
showValueViewer: true
|
|
},
|
|
global: {
|
|
mocks: { $store }
|
|
}
|
|
})
|
|
|
|
await flushPromises()
|
|
const valueViewerText = wrapper
|
|
.findComponent({ name: 'ValueViewer' })
|
|
.text()
|
|
|
|
expect(valueViewerText).to.contain(`"name": "Hufflepuff"`)
|
|
expect(valueViewerText).to.contain(`"points": 90`)
|
|
|
|
expect(valueViewerText).to.contain(`"name": "Ravenclaw"`)
|
|
expect(valueViewerText).to.contain(`"points": 95`)
|
|
|
|
expect(valueViewerText).not.to.contain(`"name": "Gryffindor"`)
|
|
expect(valueViewerText).not.to.contain(`"points": 100`)
|
|
|
|
expect(valueViewerText).not.to.contain(`"name": "Slytherin"`)
|
|
expect(valueViewerText).not.to.contain(`"points": 80`)
|
|
wrapper.unmount()
|
|
})
|
|
|
|
it('clears selections on dataSources change', async () => {
|
|
const dataSources = {
|
|
name: ['Gryffindor', 'Hufflepuff', 'Ravenclaw', 'Slytherin'],
|
|
points: [100, 90, 95, 80]
|
|
}
|
|
const wrapper = mount(Chart, {
|
|
attachTo: document.body,
|
|
props: {
|
|
dataSources,
|
|
initOptions: {
|
|
data: [
|
|
{
|
|
type: 'scatter',
|
|
mode: 'markers',
|
|
x: ['Gryffindor', 'Hufflepuff', 'Ravenclaw', 'Slytherin'],
|
|
xsrc: 'name',
|
|
meta: {
|
|
columnNames: {
|
|
x: 'name',
|
|
y: 'points'
|
|
}
|
|
},
|
|
y: [100, 90, 95, 80],
|
|
ysrc: 'points',
|
|
selectedpoints: [1, 2]
|
|
}
|
|
],
|
|
layout: {
|
|
autosize: true,
|
|
mapbox: {
|
|
style: 'open-street-map'
|
|
},
|
|
dragmode: 'select',
|
|
selections: [
|
|
{
|
|
xref: 'x',
|
|
yref: 'y',
|
|
line: {
|
|
width: 1,
|
|
dash: 'dot'
|
|
},
|
|
type: 'rect',
|
|
x0: 0.6238202370500439,
|
|
y0: 98.1184336198663,
|
|
x1: 2.4324242756804213,
|
|
y1: 87.03915950334289
|
|
}
|
|
],
|
|
title: {
|
|
subtitle: {
|
|
text: 'Click to enter Plot subtitle'
|
|
}
|
|
},
|
|
xaxis: {
|
|
range: [-0.20324846356453027, 3.20324846356453],
|
|
autorange: true,
|
|
type: 'category'
|
|
},
|
|
yaxis: {
|
|
range: [78.4909264565425, 101.5090735434575],
|
|
autorange: true,
|
|
type: 'linear'
|
|
}
|
|
},
|
|
frames: []
|
|
},
|
|
showViewSettings: true,
|
|
showValueViewer: true
|
|
},
|
|
global: {
|
|
mocks: { $store }
|
|
}
|
|
})
|
|
|
|
await flushPromises()
|
|
const valueViewerText = wrapper
|
|
.findComponent({ name: 'ValueViewer' })
|
|
.text()
|
|
|
|
expect(valueViewerText).to.contain(`"name": "Hufflepuff"`)
|
|
expect(valueViewerText).to.contain(`"name": "Ravenclaw"`)
|
|
|
|
await wrapper.setProps({
|
|
dataSources: {
|
|
name: ['Gryffindor', 'Hufflepuff'],
|
|
points: [100, 90]
|
|
}
|
|
})
|
|
|
|
await flushPromises()
|
|
|
|
expect(wrapper.findComponent({ name: 'ValueViewer' }).text()).equal(
|
|
`No points selected to view`
|
|
)
|
|
wrapper.unmount()
|
|
})
|
|
|
|
it('clears selections on changes in chart settings', async () => {
|
|
const dataSources = {
|
|
name: ['Gryffindor', 'Hufflepuff', 'Ravenclaw', 'Slytherin'],
|
|
points: [100, 90, 95, 80]
|
|
}
|
|
const wrapper = mount(Chart, {
|
|
attachTo: document.body,
|
|
props: {
|
|
dataSources,
|
|
initOptions: {
|
|
data: [
|
|
{
|
|
type: 'scatter',
|
|
mode: 'markers',
|
|
x: ['Gryffindor', 'Hufflepuff', 'Ravenclaw', 'Slytherin'],
|
|
xsrc: 'name',
|
|
meta: {
|
|
columnNames: {
|
|
x: 'name',
|
|
y: 'points'
|
|
}
|
|
},
|
|
y: [100, 90, 95, 80],
|
|
ysrc: 'points',
|
|
selectedpoints: [1, 2]
|
|
}
|
|
],
|
|
layout: {
|
|
autosize: true,
|
|
mapbox: {
|
|
style: 'open-street-map'
|
|
},
|
|
dragmode: 'select',
|
|
selections: [
|
|
{
|
|
xref: 'x',
|
|
yref: 'y',
|
|
line: {
|
|
width: 1,
|
|
dash: 'dot'
|
|
},
|
|
type: 'rect',
|
|
x0: 0.6238202370500439,
|
|
y0: 98.1184336198663,
|
|
x1: 2.4324242756804213,
|
|
y1: 87.03915950334289
|
|
}
|
|
],
|
|
title: {
|
|
subtitle: {
|
|
text: 'Click to enter Plot subtitle'
|
|
}
|
|
},
|
|
xaxis: {
|
|
range: [-0.20324846356453027, 3.20324846356453],
|
|
autorange: true,
|
|
type: 'category'
|
|
},
|
|
yaxis: {
|
|
range: [78.4909264565425, 101.5090735434575],
|
|
autorange: true,
|
|
type: 'linear'
|
|
}
|
|
},
|
|
frames: []
|
|
},
|
|
showViewSettings: true,
|
|
showValueViewer: true
|
|
},
|
|
global: {
|
|
mocks: { $store }
|
|
}
|
|
})
|
|
|
|
await flushPromises()
|
|
const valueViewerText = wrapper
|
|
.findComponent({ name: 'ValueViewer' })
|
|
.text()
|
|
|
|
expect(valueViewerText).to.contain(`"name": "Hufflepuff"`)
|
|
expect(valueViewerText).to.contain(`"name": "Ravenclaw"`)
|
|
|
|
// Add another trace
|
|
await wrapper.find('button.js-add-button').wrapperElement.click()
|
|
|
|
await flushPromises()
|
|
|
|
expect(wrapper.findComponent({ name: 'ValueViewer' }).text()).equal(
|
|
`No points selected to view`
|
|
)
|
|
wrapper.unmount()
|
|
})
|
|
})
|