forked from apache/superset
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add data structures for chart plugin system (apache#6028)
* add unit tests * add test structure * add unit tests for Registry * add LoaderRegistry unit test * add unit test for makeSingleton * add type check * add plugin data structures * simplify API * add preset tests * update test message * fix lint * update makeSingleton * update Plugin, Preset and unit test * revise Registry code * update unit test, add remove function * update test * update unit test * update plugin unit test * add .keys(), .entries() and .entriesAsPromise() * update test description
- Loading branch information
1 parent
7d44ad8
commit 81bc719
Showing
16 changed files
with
602 additions
and
0 deletions.
There are no files selected for viewing
175 changes: 175 additions & 0 deletions
175
superset/assets/spec/javascripts/modules/Registry_spec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
import { describe, it } from 'mocha'; | ||
import { expect } from 'chai'; | ||
import Registry from '../../../src/modules/Registry'; | ||
|
||
describe('Registry', () => { | ||
it('exists', () => { | ||
expect(Registry !== undefined).to.equal(true); | ||
}); | ||
|
||
describe('new Registry(name)', () => { | ||
it('can create a new registry when name is not given', () => { | ||
const registry = new Registry(); | ||
expect(registry).to.be.instanceOf(Registry); | ||
}); | ||
it('can create a new registry when name is given', () => { | ||
const registry = new Registry('abc'); | ||
expect(registry).to.be.instanceOf(Registry); | ||
expect(registry.name).to.equal('abc'); | ||
}); | ||
}); | ||
|
||
describe('.has(key)', () => { | ||
it('returns true if an item with the given key exists', () => { | ||
const registry = new Registry(); | ||
registry.registerValue('a', 'testValue'); | ||
expect(registry.has('a')).to.equal(true); | ||
registry.registerLoader('b', () => 'testValue2'); | ||
expect(registry.has('b')).to.equal(true); | ||
}); | ||
it('returns false if an item with the given key does not exist', () => { | ||
const registry = new Registry(); | ||
expect(registry.has('a')).to.equal(false); | ||
}); | ||
}); | ||
|
||
describe('.registerValue(key, value)', () => { | ||
it('registers the given value with the given key', () => { | ||
const registry = new Registry(); | ||
registry.registerValue('a', 'testValue'); | ||
expect(registry.has('a')).to.equal(true); | ||
expect(registry.get('a')).to.equal('testValue'); | ||
}); | ||
it('returns the registry itself', () => { | ||
const registry = new Registry(); | ||
expect(registry.registerValue('a', 'testValue')).to.equal(registry); | ||
}); | ||
}); | ||
|
||
describe('.registerLoader(key, loader)', () => { | ||
it('registers the given loader with the given key', () => { | ||
const registry = new Registry(); | ||
registry.registerLoader('a', () => 'testValue'); | ||
expect(registry.has('a')).to.equal(true); | ||
expect(registry.get('a')).to.equal('testValue'); | ||
}); | ||
it('returns the registry itself', () => { | ||
const registry = new Registry(); | ||
expect(registry.registerLoader('a', () => 'testValue')).to.equal(registry); | ||
}); | ||
}); | ||
|
||
describe('.get(key)', () => { | ||
it('given the key, returns the value if the item is a value', () => { | ||
const registry = new Registry(); | ||
registry.registerValue('a', 'testValue'); | ||
expect(registry.get('a')).to.equal('testValue'); | ||
}); | ||
it('given the key, returns the result of the loader function if the item is a loader', () => { | ||
const registry = new Registry(); | ||
registry.registerLoader('b', () => 'testValue2'); | ||
expect(registry.get('b')).to.equal('testValue2'); | ||
}); | ||
it('returns null if the item with specified key does not exist', () => { | ||
const registry = new Registry(); | ||
expect(registry.get('a')).to.equal(null); | ||
}); | ||
it('If the key was registered multiple times, returns the most recent item.', () => { | ||
const registry = new Registry(); | ||
registry.registerValue('a', 'testValue'); | ||
expect(registry.get('a')).to.equal('testValue'); | ||
registry.registerLoader('a', () => 'newValue'); | ||
expect(registry.get('a')).to.equal('newValue'); | ||
}); | ||
}); | ||
|
||
describe('.getAsPromise(key)', () => { | ||
it('given the key, returns a promise of item value if the item is a value', () => { | ||
const registry = new Registry(); | ||
registry.registerValue('a', 'testValue'); | ||
return registry.getAsPromise('a').then((value) => { | ||
expect(value).to.equal('testValue'); | ||
}); | ||
}); | ||
it('given the key, returns a promise of result of the loader function if the item is a loader ', () => { | ||
const registry = new Registry(); | ||
registry.registerLoader('a', () => 'testValue'); | ||
return registry.getAsPromise('a').then((value) => { | ||
expect(value).to.equal('testValue'); | ||
}); | ||
}); | ||
it('returns a rejected promise if the item with specified key does not exist', () => { | ||
const registry = new Registry(); | ||
return registry.getAsPromise('a').then(null, (err) => { | ||
expect(err).to.equal('Item with key "a" is not registered.'); | ||
}); | ||
}); | ||
it('If the key was registered multiple times, returns a promise of the most recent item.', () => { | ||
const registry = new Registry(); | ||
registry.registerValue('a', 'testValue'); | ||
const promise1 = registry.getAsPromise('a').then((value) => { | ||
expect(value).to.equal('testValue'); | ||
}); | ||
registry.registerLoader('a', () => 'newValue'); | ||
const promise2 = registry.getAsPromise('a').then((value) => { | ||
expect(value).to.equal('newValue'); | ||
}); | ||
return Promise.all([promise1, promise2]); | ||
}); | ||
}); | ||
|
||
describe('.keys()', () => { | ||
it('returns an array of keys', () => { | ||
const registry = new Registry(); | ||
registry.registerValue('a', 'testValue'); | ||
registry.registerLoader('b', () => 'test2'); | ||
expect(registry.keys()).to.deep.equal(['a', 'b']); | ||
}); | ||
}); | ||
|
||
describe('.entries()', () => { | ||
it('returns an array of { key, value }', () => { | ||
const registry = new Registry(); | ||
registry.registerValue('a', 'test1'); | ||
registry.registerLoader('b', () => 'test2'); | ||
expect(registry.entries()).to.deep.equal([ | ||
{ key: 'a', value: 'test1' }, | ||
{ key: 'b', value: 'test2' }, | ||
]); | ||
}); | ||
}); | ||
|
||
describe('.entriesAsPromise()', () => { | ||
it('returns a Promise of an array { key, value }', () => { | ||
const registry = new Registry(); | ||
registry.registerValue('a', 'test1'); | ||
registry.registerLoader('b', () => 'test2'); | ||
registry.registerLoader('c', () => Promise.resolve('test3')); | ||
return registry.entriesAsPromise().then((entries) => { | ||
expect(entries).to.deep.equal([ | ||
{ key: 'a', value: 'test1' }, | ||
{ key: 'b', value: 'test2' }, | ||
{ key: 'c', value: 'test3' }, | ||
]); | ||
}); | ||
}); | ||
}); | ||
|
||
describe('.remove(key)', () => { | ||
it('removes the item with given key', () => { | ||
const registry = new Registry(); | ||
registry.registerValue('a', 'testValue'); | ||
registry.remove('a'); | ||
expect(registry.get('a')).to.equal(null); | ||
}); | ||
it('does not throw error if the key does not exist', () => { | ||
const registry = new Registry(); | ||
expect(() => registry.remove('a')).to.not.throw(); | ||
}); | ||
it('returns itself', () => { | ||
const registry = new Registry(); | ||
registry.registerValue('a', 'testValue'); | ||
expect(registry.remove('a')).to.equal(registry); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { it, describe } from 'mocha'; | ||
import { expect } from 'chai'; | ||
import isRequired from '../../../src/utils/isRequired'; | ||
|
||
describe('isRequired(field)', () => { | ||
it('should throw error with the given field in the message', () => { | ||
expect(() => isRequired('myField')).to.throw(Error, 'myField is required.'); | ||
}); | ||
}); |
38 changes: 38 additions & 0 deletions
38
superset/assets/spec/javascripts/utils/makeSingleton_spec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import { describe, it } from 'mocha'; | ||
import { expect } from 'chai'; | ||
import makeSingleton from '../../../src/utils/makeSingleton'; | ||
|
||
describe('makeSingleton()', () => { | ||
class Dog { | ||
constructor(name) { | ||
this.name = name; | ||
} | ||
sit() { | ||
this.isSitting = true; | ||
} | ||
} | ||
describe('makeSingleton(BaseClass)', () => { | ||
const getInstance = makeSingleton(Dog); | ||
|
||
it('returns a function for getting singleton instance of a given base class', () => { | ||
expect(getInstance).to.be.a('Function'); | ||
expect(getInstance()).to.be.instanceOf(Dog); | ||
}); | ||
it('returned function returns same instance across all calls', () => { | ||
expect(getInstance()).to.equal(getInstance()); | ||
}); | ||
}); | ||
describe('makeSingleton(BaseClass, ...args)', () => { | ||
const getInstance = makeSingleton(Dog, 'Doug'); | ||
|
||
it('returns a function for getting singleton instance of a given base class constructed with the given arguments', () => { | ||
expect(getInstance).to.be.a('Function'); | ||
expect(getInstance()).to.be.instanceOf(Dog); | ||
expect(getInstance().name).to.equal('Doug'); | ||
}); | ||
it('returned function returns same instance across all calls', () => { | ||
expect(getInstance()).to.equal(getInstance()); | ||
}); | ||
}); | ||
|
||
}); |
42 changes: 42 additions & 0 deletions
42
superset/assets/spec/javascripts/visualizations/models/ChartPlugin_spec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { describe, it } from 'mocha'; | ||
import { expect } from 'chai'; | ||
import ChartPlugin from '../../../../src/visualizations/core/models/ChartPlugin'; | ||
import ChartMetadata from '../../../../src/visualizations/core/models/ChartMetadata'; | ||
|
||
describe('ChartPlugin', () => { | ||
const metadata = new ChartMetadata({}); | ||
|
||
it('exists', () => { | ||
expect(ChartPlugin).to.not.equal(undefined); | ||
}); | ||
|
||
describe('new ChartPlugin()', () => { | ||
it('creates a new plugin', () => { | ||
const plugin = new ChartPlugin({ | ||
metadata, | ||
Chart() {}, | ||
}); | ||
expect(plugin).to.be.instanceof(ChartPlugin); | ||
}); | ||
it('throws an error if metadata is not specified', () => { | ||
expect(() => new ChartPlugin()).to.throw(Error); | ||
}); | ||
it('throws an error if none of Chart or loadChart is specified', () => { | ||
expect(() => new ChartPlugin({ metadata })).to.throw(Error); | ||
}); | ||
}); | ||
|
||
describe('.register(key)', () => { | ||
const plugin = new ChartPlugin({ | ||
metadata, | ||
Chart() {}, | ||
}); | ||
it('throws an error if key is not provided', () => { | ||
expect(() => plugin.register()).to.throw(Error); | ||
expect(() => plugin.configure({ key: 'abc' }).register()).to.not.throw(Error); | ||
}); | ||
it('returns itself', () => { | ||
expect(plugin.configure({ key: 'abc' }).register()).to.equal(plugin); | ||
}); | ||
}); | ||
}); |
48 changes: 48 additions & 0 deletions
48
superset/assets/spec/javascripts/visualizations/models/Plugin_spec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import { describe, it } from 'mocha'; | ||
import { expect } from 'chai'; | ||
import Plugin from '../../../../src/visualizations/core/models/Plugin'; | ||
|
||
describe('Plugin', () => { | ||
it('exists', () => { | ||
expect(Plugin).to.not.equal(undefined); | ||
}); | ||
|
||
describe('new Plugin()', () => { | ||
it('creates a new plugin', () => { | ||
const plugin = new Plugin(); | ||
expect(plugin).to.be.instanceof(Plugin); | ||
}); | ||
}); | ||
|
||
describe('.configure(config, replace)', () => { | ||
it('extends the default config with given config when replace is not set or false', () => { | ||
const plugin = new Plugin(); | ||
plugin.configure({ key: 'abc', foo: 'bar' }); | ||
plugin.configure({ key: 'def' }); | ||
expect(plugin.config).to.deep.equal({ key: 'def', foo: 'bar' }); | ||
}); | ||
it('replaces the default config with given config when replace is true', () => { | ||
const plugin = new Plugin(); | ||
plugin.configure({ key: 'abc', foo: 'bar' }); | ||
plugin.configure({ key: 'def' }, true); | ||
expect(plugin.config).to.deep.equal({ key: 'def' }); | ||
}); | ||
it('returns the plugin itself', () => { | ||
const plugin = new Plugin(); | ||
expect(plugin.configure({ key: 'abc' })).to.equal(plugin); | ||
}); | ||
}); | ||
|
||
describe('.resetConfig()', () => { | ||
it('resets config back to default', () => { | ||
const plugin = new Plugin(); | ||
plugin.configure({ key: 'abc', foo: 'bar' }); | ||
plugin.resetConfig(); | ||
expect(plugin.config).to.deep.equal({}); | ||
}); | ||
it('returns the plugin itself', () => { | ||
const plugin = new Plugin(); | ||
expect(plugin.resetConfig()).to.equal(plugin); | ||
}); | ||
}); | ||
}); |
65 changes: 65 additions & 0 deletions
65
superset/assets/spec/javascripts/visualizations/models/Preset_spec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import { describe, it } from 'mocha'; | ||
import { expect } from 'chai'; | ||
import Preset from '../../../../src/visualizations/core/models/Preset'; | ||
import Plugin from '../../../../src/visualizations/core/models/Plugin'; | ||
|
||
describe('Preset', () => { | ||
it('exists', () => { | ||
expect(Preset).to.not.equal(undefined); | ||
}); | ||
|
||
describe('new Preset()', () => { | ||
it('creates new preset', () => { | ||
const preset = new Preset(); | ||
expect(preset).to.be.instanceOf(Preset); | ||
}); | ||
}); | ||
|
||
describe('.register()', () => { | ||
it('register all listed presets then plugins', () => { | ||
const values = []; | ||
class Plugin1 extends Plugin { | ||
register() { | ||
values.push(1); | ||
} | ||
} | ||
class Plugin2 extends Plugin { | ||
register() { | ||
values.push(2); | ||
} | ||
} | ||
class Plugin3 extends Plugin { | ||
register() { | ||
values.push(3); | ||
} | ||
} | ||
class Plugin4 extends Plugin { | ||
register() { | ||
const { key } = this.config; | ||
values.push(key); | ||
} | ||
} | ||
|
||
const preset1 = new Preset({ | ||
plugins: [new Plugin1()], | ||
}); | ||
const preset2 = new Preset({ | ||
plugins: [new Plugin2()], | ||
}); | ||
const preset3 = new Preset({ | ||
presets: [preset1, preset2], | ||
plugins: [ | ||
new Plugin3(), | ||
new Plugin4().configure({ key: 'abc' }), | ||
], | ||
}); | ||
preset3.register(); | ||
expect(values).to.deep.equal([1, 2, 3, 'abc']); | ||
}); | ||
|
||
it('returns itself', () => { | ||
const preset = new Preset(); | ||
expect(preset.register()).to.equal(preset); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.