Commit 39383c3e authored by Sascha Herzinger's avatar Sascha Herzinger
Browse files

Lots of stuff because of broken .gitignore

parent f8f37f4e
......@@ -185,19 +185,6 @@ Session.vim
# auto-generated tag files
tags
### Web ###
*.asp
*.cer
*.csr
*.css
*.htm
*.html
*.js
*.jsp
*.php
*.rss
*.xhtml
### WebStorm ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
......
......@@ -28,12 +28,12 @@ class FractalJS {
}
// noinspection JSMethodCanBeStatic
loadData ({descriptors}) {
loadData (descriptors) {
return store.getters.requestManager.createData(descriptors)
}
// noinspection JSMethodCanBeStatic
setChart ({chart, selector}) {
setChart (chart, selector) {
return store.getters.chartManager.setChart(chart, selector)
}
......@@ -43,7 +43,7 @@ class FractalJS {
}
// noinspection JSMethodCanBeStatic
setSubsets ({subsets}) {
setSubsets (subsets) {
store.dispatch('setSubsets', subsets)
}
......@@ -53,12 +53,12 @@ class FractalJS {
}
// noinspection JSMethodCanBeStatic
chart2id ({selector, callback}) {
chart2id (selector, callback) {
return store.getters.stateManager.chart2id(selector, callback)
}
// noinspection JSMethodCanBeStatic
id2chart ({selector, stateID}) {
id2chart (selector, stateID) {
return store.getters.stateManager.id2chart(selector, stateID)
}
}
......
......@@ -18,9 +18,16 @@ export default class {
if (!this.AVAILABLE_CHARTS.hasOwnProperty(chart)) {
throw new Error(`Chart '${chart} is not available. Must be one of: ${this.AVAILABLE_CHARTS}`)
}
let container = document.querySelectorAll(selector)
if (container.length !== 1) {
throw new Error(`Selector to set a chart must match exactly one element. Matched elements: ${container.length}`)
}
container = container[0]
const Chart = Vue.extend(this.AVAILABLE_CHARTS[chart])
const vm = new Chart()
vm.$mount(selector)
const el = document.createElement('div')
container.appendChild(el)
vm.$mount(el)
return vm
}
}
import store from '../store/store'
export default class {
getChartElementForSelector (selector) {
let chartElement = document.querySelectorAll(selector)
if (chartElement.length !== 1) {
throw new Error('The given selector must point to exactly one element. ' +
'#Elements for this selector: ' + chartElement.length)
}
chartElement = chartElement[0]
if (typeof chartElement.__vue__ === 'undefined' || chartElement.__vue__.$options.name !== 'chart') {
throw new Error('The given selector must point to a div element with class "fjs-chart". ' +
'This is the div that replaced the placeholder when creating the chart initially.')
}
return chartElement
}
chart2id (selector, callback) {
const chartElement = this.getChartElementForSelector(selector)
chartElement.__vue__.$parent._setStateChangedCallback(async function (name, state) {
const rv = await store.getters.requestManager.saveState({chartName: name, chartState: state})
const stateID = rv.data.state_id
callback(stateID)
})
}
async getState (stateID) {
function timeout (ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
await store.getters.requestManager.requestStateAccess(stateID)
let timeWaited = 0
let delay = 200
while (timeWaited <= 900000) { // we wait 15 minutes
await timeout(delay)
timeWaited += delay
delay += 100
delay = delay > 3000 ? 3000 : delay
const rv = await store.getters.requestManager.getState(stateID)
if (rv.status === 200) {
return rv.data.state
}
}
}
async id2chart (selector, stateID) {
const state = await this.getState(stateID)
const vm = store.getters.chartManager.setChart(state.chartName, selector)
vm._setState(state.chartState)
}
}
......@@ -96,11 +96,16 @@ export default {
/**
* Commits a control panel vm for keeping track of all such instances.
* @param context The context of the action.
* @param vm The vm of the contr
* @param vm The vm of the control panel.
*/
addControlPanel: (context, vm) => {
context.commit(types.ADD_CONTROL_PANEL, vm)
},
/**
* Commits an option object if it is defined that all components can access for configuration.
* @param context The context of the action.
* @param options The object that contains configuration options.
*/
setOptions: (context, options) => {
if (typeof options === 'object') {
context.commit(types.SET_OPTIONS, options)
......
import objectPath from 'object-path'
export default {
data () {
return {
_dataToSave: [],
_callback: () => {}
}
},
methods: {
registerDataToSave (list) {
this.$data._dataToSave = list
list.forEach(path => {
this.$watch(path, () => {
this.$data._callback(this.$options.name, this._savedState)
}, {deep: true})
})
},
_setState (state) {
Object.assign(this.$data, state)
},
_setStateChangedCallback (callback) {
this.$data._callback = callback
}
},
computed: {
_savedState () {
const state = {}
this.$data._dataToSave.forEach(path => {
objectPath.ensureExists(state, path, objectPath.get(this.$data, path))
})
return state
}
}
}
\ No newline at end of file
......@@ -16,11 +16,15 @@ describe('Chart manager', () => {
it('fails to set non existing charts', () => {
const f = () => cm.setChart('foo', '.placeholder')
expect(f).toThrow()
expect(f).toThrowError(/.+not available.+/)
})
it('sets chart if it exists', () => {
cm.setChart('correlation-analysis', '.placeholder')
expect(document.querySelector('.fjs-correlation-analysis')).toBeDefined()
expect(document.querySelector('.fjs-chart')).toBeDefined()
})
afterAll(() => {
document.body.innerHTML = ''
})
})
......@@ -9,22 +9,15 @@
<body>
<input type="button" onclick="loadData()" value="load data"/>
<input type="button" onclick="deleteData()" value="delete data"/>
<div class="container">
<div>
<div class="placeholder" id="placeholder1"></div>
</div>
<div>
<div class="placeholder" id="placeholder2"></div>
</div>
<div>
<div class="placeholder" id="placeholder3"></div>
</div>
<div>
<div class="placeholder" id="placeholder4"></div>
</div>
<input type="button" onclick="saveState()" value="save state"/>
<input type="text" class="save-id">
<input type="button" onclick="loadState()" value="load state"/>
<input type="text" class="load-id">
<div class="containers">
<div class="container container-1"></div>
<div class="container container-2"></div>
<div class="container container-3"></div>
<div class="container container-4"></div>
</div>
</body>
......@@ -86,15 +79,23 @@
fjs.clearCache()
}
function saveState () {
fjs.chart2id('.fjs-chart', id => { document.querySelector('.save-id').value = id })
}
function loadState () {
fjs.id2chart('#placeholder4', document.querySelector('.load-id').value)
}
function setCharts () {
fjs.setChart({chart: 'correlation-analysis', selector: '#placeholder1'})
fjs.setChart({chart: 'boxplot', selector: '#placeholder2'})
fjs.setChart({chart: 'heatmap', selector: '#placeholder3'})
fjs.setChart({chart: 'pca-analysis', selector: '#placeholder4'})
// fjs.setChart('correlation-analysis', '.container-1')
fjs.setChart('boxplot', '.container-2')
// fjs.setChart('heatmap', '.container-3')
// fjs.setChart('pca-analysis', '.container-4')
}
function resize () {
const container = document.querySelector('.container')
const container = document.querySelector('.containers')
container.style.height = document.documentElement.clientHeight + 'px'
container.style.width = document.documentElement.clientWidth + 'px'
}
......@@ -107,12 +108,12 @@
</script>
<style>
.container {
.containers {
width: 1000px;
height: 1000px;
}
.container > div {
.container {
width: 48%;
height: 48%;
float: left;
......
......@@ -47,4 +47,8 @@ describe('DataBox', () => {
expect(vm.$el.querySelector('.fjs-data-entry-header input[data-id="A"]').checked).toBeTruthy()
expect(vm.$el.querySelector('.fjs-data-entry-header input[data-id="B"]').checked).toBeFalsy()
})
afterAll(() => {
document.body.innerHTML = ''
})
})
......@@ -22,4 +22,8 @@ describe('initializer', () => {
const fjs = init({handler: 'ada', dataSource: 'foo', fractalisNode: 'foo', getAuth: () => {}})
expect(fjs.constructor.name).toBe('FractalJS')
})
afterAll(() => {
document.body.innerHTML = ''
})
})
import runAnalysis from '../src/vue/mixins/run-analysis'
import RequestManager from '../src/services/request-manager'
import store from '../src/store/store'
describe('runAnalysis method', () => {
beforeEach(() => {
const requestManager = new RequestManager(
{handler: '', dataSource: '', fractalisNode: '', getAuth: () => {}})
store.dispatch('setRequestManager', requestManager)
})
it('returns a promise', () => {
const rv = runAnalysis('', {})
expect(rv instanceof Promise).toBe(true)
})
it('fails if unknown task state', done => {
spyOn(store.getters.requestManager, 'createAnalysis')
.and.returnValue(Promise.resolve({data: {task_id: 123}}))
spyOn(store.getters.requestManager, 'getAnalysisStatus')
.and.returnValue(Promise.resolve({data: {state: 'FOO', result: ''}}))
runAnalysis('', {})
.then(done.fail)
.catch(done)
})
it('resolves if task state is successful', done => {
spyOn(store.getters.requestManager, 'createAnalysis')
.and.returnValue(Promise.resolve({data: {task_id: 123}}))
spyOn(store.getters.requestManager, 'getAnalysisStatus')
.and.returnValue(Promise.resolve({data: {state: 'SUCCESS', result: 123}}))
runAnalysis('', {})
.then(response => {
expect(response).toBe(123)
done()
})
.catch(done.fail)
})
it('rejects if task state is unsuccessful', done => {
spyOn(store.getters.requestManager, 'createAnalysis')
.and.returnValue(Promise.resolve({data: {task_id: 123}}))
spyOn(store.getters.requestManager, 'getAnalysisStatus')
.and.returnValue(Promise.resolve({data: {state: 'FAILURE', result: ''}}))
runAnalysis('', {})
.then(done.fail)
.catch(done)
})
it('does wait for task state to switch from SUBMITTED to final state', done => {
spyOn(store.getters.requestManager, 'createAnalysis')
.and.returnValue(Promise.resolve({data: {task_id: 123}}))
spyOn(store.getters.requestManager, 'getAnalysisStatus')
.and.returnValues(
Promise.resolve({data: {state: 'SUBMITTED', result: ''}}),
Promise.resolve({data: {state: 'SUBMITTED', result: ''}}),
Promise.resolve({data: {state: 'SUBMITTED', result: ''}}),
Promise.resolve({data: {state: 'SUCCESS', result: 123}})
)
runAnalysis('', {})
.then(response => {
expect(response).toBe(123)
expect(store.getters.requestManager.getAnalysisStatus).toHaveBeenCalledTimes(4)
done()
})
.catch(done.fail)
})
afterAll(() => {
document.body.innerHTML = ''
})
})
import StateManager from '../src/services/state-manager'
import RequestManager from '../src/services/request-manager'
import ChartManager from '../src/services/chart-manager'
import Vue from 'vue'
import stateSaver from '../src/vue/mixins/state-saver'
import store from '../src/store/store'
import Chart from '../src/vue/components/Chart.vue'
describe('state manager', () => {
let stateManager
beforeAll(() => {
stateManager = new StateManager()
const chartManager = new ChartManager()
const requestManager = new RequestManager(
{handler: '', dataSource: '', fractalisNode: '', getAuth: () => {}})
store.dispatch('setRequestManager', requestManager)
store.dispatch('setChartManager', chartManager)
})
let vm
beforeEach(() => {
const container = document.createElement('div')
const el = document.createElement('div')
el.id = 'chart'
document.body.appendChild(container)
container.appendChild(el)
const chart = new Vue({
template: '<div><chart/></div>',
mixins: [stateSaver],
components: { Chart }
})
vm = chart.$mount('#chart')
})
afterEach(() => {
document.body.innerHTML = ''
})
describe('chart2id', () => {
it('throws if selector matches more or less than 1 element', () => {
const el1 = document.createElement('div')
el1.className = 'bar'
const el2 = document.createElement('div')
el2.className = 'bar'
document.body.appendChild(el1)
document.body.appendChild(el2)
let f = () => stateManager.chart2id('#foo', _ => {})
expect(f).toThrowError(/.+must point to exactly one.+/)
let g = () => stateManager.chart2id('.bar', _ => {})
expect(g).toThrowError(/.+must point to exactly one.+/)
})
it('throws if selector is no chart component', () => {
const el = document.createElement('div')
el.id = 'foo'
document.body.appendChild(el)
let f = () => stateManager.chart2id('#foo', _ => {})
expect(f).toThrowError(/.+must point to a div element with class "fjs-chart".+/)
})
it('makes save state request and calls callback with returned id when called back', done => {
spyOn(store.getters.requestManager, 'saveState').and.returnValue({data: {state_id: 456}})
const spy = jasmine.createSpy('spy')
const callback = id => spy(id)
stateManager.chart2id('.fjs-chart', callback)
vm.$data._callback('foo', 123).then(() => {
expect(store.getters.requestManager.saveState).toHaveBeenCalledWith({chartName: 'foo', chartState: 123})
expect(spy).toHaveBeenCalledWith(456)
done()
})
})
})
describe('getState', () => {
it('throws if request access fails', done => {
spyOn(store.getters.requestManager, 'requestStateAccess').and.returnValue(Promise.reject(new Error('foo')))
stateManager.getState(123)
.then(done.fail)
.catch(e => {
expect(e.toString()).toBe('Error: foo')
done()
})
})
it('throws if getting state fails', done => {
spyOn(store.getters.requestManager, 'requestStateAccess').and.returnValue(null)
spyOn(store.getters.requestManager, 'getState').and.returnValue(Promise.reject(new Error('foo')))
stateManager.getState(123)
.then(done.fail)
.catch(e => {
expect(e.toString()).toBe('Error: foo')
done()
})
})
it('to return state when status code becomes 200', done => {
spyOn(store.getters.requestManager, 'requestStateAccess').and.returnValue(null)
spyOn(store.getters.requestManager, 'getState').and.returnValues(
Promise.resolve({status: 202}),
Promise.resolve({status: 202}),
Promise.resolve({status: 200, data: {state: 'foo'}}))
stateManager.getState(123)
.then(state => {
expect(store.getters.requestManager.getState).toHaveBeenCalledTimes(3)
expect(state).toBe('foo')
done()
})
.catch(done.fail)
})
it('to fail when getting status rejects', done => {
spyOn(store.getters.requestManager, 'requestStateAccess').and.returnValue(Promise.resolve(''))
spyOn(store.getters.requestManager, 'getState').and.returnValues(
Promise.resolve({status: 202}),
Promise.resolve({status: 202}),
Promise.reject(new Error('foo')))
stateManager.getState(123)
.then(done.fail)
.catch(error => {
expect(store.getters.requestManager.getState).toHaveBeenCalledTimes(3)
expect(error.toString()).toBe('Error: foo')
done()
})
})
})
describe('id2chart', () => {
it('to throw error if selector does match no element', done => {
spyOn(stateManager, 'getState').and.returnValue(Promise.resolve({chartName: 'boxplot'}))
stateManager.id2chart('#foo', 123)
.then(done.fail)
.catch(e => {
expect(e.toString()).toContain('exactly one element')
done()
})
})
it('to throw error if selector does match more than one element', done => {
const el1 = document.createElement('div')
el1.className = 'bar'
const el2 = document.createElement('div')
el2.className = 'bar'
document.body.appendChild(el1)
document.body.appendChild(el2)
spyOn(stateManager, 'getState').and.returnValue(Promise.resolve({chartName: 'boxplot'}))
stateManager.id2chart('.bar', 123)
.then(done.fail)
.catch(e => {
expect(e.toString()).toContain('exactly one element')
done()
})
})
it('to update state if successful', done => {
spyOn(stateManager, 'getState').and.returnValue(
Promise.resolve({chartName: '', chartState: {'some': 'thing'}}))
spyOn(store.getters.chartManager, 'setChart').and.returnValue(vm)
stateManager.id2chart('.fjs-chart', 123)
.then(() => {
expect(vm.$data.some).toBe('thing')
done()
})
.catch(done.fail)
})
it('to throw if cannot get state for id', done => {
spyOn(stateManager, 'getState').and.returnValue(Promise.reject(new Error('foo')))
stateManager.id2chart('.fjs-chart', 123)
.then(done.fail)
.catch(e => {
expect(e.toString()).toContain('foo')
done()
})
})
})
})
......@@ -54,8 +54,12 @@ describe('store', () => {
it('should have working unsetTask action', () => {
const task = {taskID: 'A', taskName: 'foo', taskState: 'SUBMITTED'}
store.dispatch('setTask', task)
store.dispatch('unsetTask', {taskID: 'A'})
store.dispatch('unsetTask', 'A')
expect(store.getters.tasks['A']).not.toBeDefined()
})
})
afterAll(() => {
document.body.innerHTML = ''
})
})
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment