123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029 |
- import React, { PureComponent } from "react"
- import System from "core/system"
- import { fromJS } from "immutable"
- import { render } from "enzyme"
- import ViewPlugin from "core/plugins/view/index.js"
- import filterPlugin from "core/plugins/filter/index.js"
- import { connect, Provider } from "react-redux"
- describe("bound system", function(){
- describe("wrapActions", function(){
- it("should replace an action", function(){
- // Given
- const system = new System({
- plugins: {
- statePlugins: {
- josh: {
- actions: {
- simple: () => {
- return { type: "simple" }
- }
- },
- wrapActions: {
- simple: () => () => {
- return { type: "newSimple" }
- }
- }
- }
- }
- }
- })
- // When
- let action = system.getSystem().joshActions.simple(1)
- expect(action).toEqual({
- type: "newSimple"
- })
- })
- it("should expose the original action, and the system as args", function(){
- // Given
- const simple = () => ({type: "simple" })
- const system = new System({
- plugins: {
- statePlugins: {
- josh: {
- actions: { simple },
- wrapActions: {
- simple: (oriAction, system) => (actionArg) => {
- return {
- type: "newSimple",
- oriActionResult: oriAction(),
- system: system.getSystem(),
- actionArg
- }
- }
- }
- }
- }
- }
- })
- // When
- let action = system.getSystem().joshActions.simple(1)
- expect(action).toEqual({
- type: "newSimple",
- oriActionResult: { type: "simple" },
- system: system.getSystem(),
- actionArg: 1
- })
- })
- it("should support multiple wraps of the same action", function(){
- const system = new System({
- plugins: [
- {
- statePlugins: {
- kyle: {
- actions: {
- simple: () => {
- return {
- type: "simple",
- }
- }
- }
- }
- }
- },
- {
- statePlugins: {
- kyle: {
- wrapActions: {
- simple: (ori) => () => {
- return {
- ...ori(),
- firstWrap: true
- }
- }
- }
- }
- }
- },
- {
- statePlugins: {
- kyle: {
- wrapActions: {
- simple: (ori) => () => {
- return {
- ...ori(),
- secondWrap: true
- }
- }
- }
- }
- }
- }
- ]
- })
- // When
- let action = system.getSystem().kyleActions.simple(1)
- expect(action).toEqual({
- type: "simple",
- firstWrap: true,
- secondWrap: true,
- })
- })
- it("should execute wrapActions in the order they appear ( via plugins )", function(){
- const system = new System({
- plugins: [
- {
- statePlugins: {
- kyle: {
- actions: {
- simple: () => {
- return {
- type: "one",
- }
- }
- }
- }
- }
- },
- {
- statePlugins: {
- kyle: {
- wrapActions: {
- simple: (ori) => () => {
- const obj = ori()
- obj.type += "-two"
- return obj
- }
- }
- }
- }
- },
- {
- statePlugins: {
- kyle: {
- wrapActions: {
- simple: (ori) => () => {
- const obj = ori()
- obj.type += "-three"
- return obj
- }
- }
- }
- }
- }
- ]
- })
- // When
- let action = system.getSystem().kyleActions.simple(1)
- expect(action.type).toEqual("one-two-three")
- })
- it("should have a the latest system", function(){
- // Given
- const system = new System({
- plugins: [
- {
- statePlugins: {
- kyle: {
- actions: {
- simple: () => {
- return {
- type: "one",
- }
- }
- },
- wrapActions: {
- simple: (ori, {joshActions}) => () => {
- return joshActions.hello()
- }
- }
- }
- }
- },
- ]
- })
- // When
- const kyleActions = system.getSystem().kyleActions
- system.register({
- statePlugins: {
- josh: {
- actions: {
- hello(){ return {type: "hello" } }
- }
- }
- }
- })
- const action = kyleActions.simple()
- expect(action).toEqual({ type: "hello"})
- })
- it.skip("should be able to create async actions", function(){
- const system = new System({
- plugins: [
- {
- statePlugins: {
- kyle: {
- actions: {
- simple: () => {
- return {
- type: "one",
- }
- }
- }
- }
- }
- },
- {
- statePlugins: {
- kyle: {
- wrapActions: {
- // eslint-disable-next-line no-unused-vars
- simple: (ori) => (arg) => (sys) => {
- return { type: "called" }
- }
- }
- }
- }
- },
- ]
- })
- // When
- let action = system.getSystem().kyleActions.simple(1)
- expect(action.type).toEqual("called")
- })
- })
- describe("fn", function() {
- it("should return helper functions", function () {
- // Given
- const system = new System({
- plugins: [
- filterPlugin
- ]
- })
- // When
- const fn = system.getSystem().fn.opsFilter
- expect(typeof fn).toEqual("function")
- })
- })
- describe("selectors", function(){
- it("should have the first arg be the nested state, and all other args to follow", function(){
- // Given
- const system = new System({
- state: {
- josh: {
- one: 1
- }
- },
- plugins: {
- statePlugins: {
- josh: {
- selectors: {
- simple: (state, arg1) => {
- return { state, arg1 }
- }
- }
- }
- }
- }
- })
- // When
- let res = system.getSystem().joshSelectors.simple(1)
- expect(res).toEqual({
- state: fromJS({
- one: 1
- }),
- arg1: 1
- })
- })
- describe("when selector returns a function", function(){
- it("should pass the system to that function", function(){
- // Given
- const system = new System({
- plugins: {
- statePlugins: {
- josh: {
- selectors: {
- advanced: () => (mySystem) => {
- // Then
- expect(mySystem).toEqual(system.getSystem())
- return "hi"
- }
- }
- }
- }
- }
- })
- // When
- let res = system.getSystem().joshSelectors.advanced(1)
- expect(res).toEqual("hi")
- })
- })
- describe("wrapSelectors", () => {
- it("should wrap a selector and provide a reference to the original", function(){
- // Given
- const system = new System({
- plugins: [
- {
- statePlugins: {
- doge: {
- selectors: {
- wow: () => (system) => {
- return "original"
- }
- }
- }
- }
- },
- {
- statePlugins: {
- doge: {
- wrapSelectors: {
- wow: (ori) => (system) => {
- // Then
- return ori() + " wrapper"
- }
- }
- }
- }
- }
- ]
- })
- // When
- let res = system.getSystem().dogeSelectors.wow(1)
- expect(res).toEqual("original wrapper")
- })
- it("should provide a live reference to the system to a wrapper", function(done){
- // Given
- const mySystem = new System({
- plugins: [
- {
- statePlugins: {
- doge: {
- selectors: {
- wow: () => (system) => {
- return "original"
- }
- }
- }
- }
- },
- {
- statePlugins: {
- doge: {
- wrapSelectors: {
- wow: (ori, system) => () => {
- // Then
- expect(mySystem.getSystem()).toEqual(system.getSystem())
- done()
- return ori() + " wrapper"
- }
- }
- }
- }
- }
- ]
- })
- mySystem.getSystem().dogeSelectors.wow(1)
- })
- it("should provide the state as the first argument to the inner function", function(done){
- // Given
- const mySystem = new System({
- state: {
- doge: {
- abc: "123"
- }
- },
- plugins: [
- {
- statePlugins: {
- doge: {
- selectors: {
- wow: () => (system) => {
- return "original"
- }
- }
- }
- }
- },
- {
- statePlugins: {
- doge: {
- wrapSelectors: {
- wow: (ori, system) => (dogeState) => {
- // Then
- expect(dogeState.toJS().abc).toEqual("123")
- done()
- return ori() + " wrapper"
- }
- }
- }
- }
- }
- ]
- })
- mySystem.getSystem().dogeSelectors.wow(1)
- })
- })
- })
- describe("getComponent", function() {
- it("returns a component from the system", function() {
- const system = new System({
- plugins: [
- ViewPlugin,
- {
- components: {
- test: ({ name }) => <div>{name} component</div>
- }
- }
- ]
- })
- // When
- let Component = system.getSystem().getComponent("test")
- const renderedComponent = render(<Component name="Test" />)
- expect(renderedComponent.text()).toEqual("Test component")
- })
- it("allows container components to provide their own `mapStateToProps` function", function() {
- // Given
- class ContainerComponent extends PureComponent {
- mapStateToProps(nextState, props) {
- return {
- "fromMapState": "This came from mapStateToProps"
- }
- }
- static defaultProps = {
- "fromMapState" : ""
- }
- render() {
- const { exampleSelectors, fromMapState, fromOwnProps } = this.props
- return (
- <div>{ fromMapState } {exampleSelectors.foo()} {fromOwnProps}</div>
- )
- }
- }
- const system = new System({
- plugins: [
- ViewPlugin,
- {
- components: {
- ContainerComponent
- }
- },
- {
- statePlugins: {
- example: {
- selectors: {
- foo() { return "and this came from the system" }
- }
- }
- }
- }
- ]
- })
- // When
- let Component = system.getSystem().getComponent("ContainerComponent", true)
- const renderedComponent = render(
- <Provider store={system.getStore()}>
- <Component fromOwnProps="and this came from my own props" />
- </Provider>
- )
- // Then
- expect(renderedComponent.text()).toEqual("This came from mapStateToProps and this came from the system and this came from my own props")
- })
- it("gives the system and own props as props to a container's `mapStateToProps` function", function() {
- // Given
- class ContainerComponent extends PureComponent {
- mapStateToProps(nextState, props) {
- const { exampleSelectors, fromMapState, fromOwnProps } = props
- return {
- "fromMapState": `This came from mapStateToProps ${exampleSelectors.foo()} ${fromOwnProps}`
- }
- }
- static defaultProps = {
- "fromMapState" : ""
- }
- render() {
- const { fromMapState } = this.props
- return (
- <div>{ fromMapState }</div>
- )
- }
- }
- const system = new System({
- plugins: [
- ViewPlugin,
- {
- components: {
- ContainerComponent
- }
- },
- {
- statePlugins: {
- example: {
- selectors: {
- foo() { return "and this came from the system" }
- }
- }
- }
- }
- ]
- })
- // When
- let Component = system.getSystem().getComponent("ContainerComponent", true)
- const renderedComponent = render(
- <Provider store={system.getStore()}>
- <Component fromOwnProps="and this came from my own props" />
- </Provider>
- )
- // Then
- expect(renderedComponent.text()).toEqual("This came from mapStateToProps and this came from the system and this came from my own props")
- })
- })
- describe("afterLoad", function() {
- it("should call a plugin's `afterLoad` method after the plugin is loaded", function() {
- // Given
- const system = new System({
- plugins: [
- {
- afterLoad(system) {
- this.rootInjects.wow = system.dogeSelectors.wow
- },
- statePlugins: {
- doge: {
- selectors: {
- wow: () => (system) => {
- return "so selective"
- }
- }
- }
- }
- }
- ]
- })
- // When
- let res = system.getSystem().wow()
- expect(res).toEqual("so selective")
- })
- it("should call a preset plugin's `afterLoad` method after the plugin is loaded", function() {
- // Given
- const MyPlugin = {
- afterLoad(system) {
- this.rootInjects.wow = system.dogeSelectors.wow
- },
- statePlugins: {
- doge: {
- selectors: {
- wow: () => (system) => {
- return "so selective"
- }
- }
- }
- }
- }
- const system = new System({
- plugins: [
- [MyPlugin]
- ]
- })
- // When
- let res = system.getSystem().wow()
- expect(res).toEqual("so selective")
- })
- it("should call a function preset plugin's `afterLoad` method after the plugin is loaded", function() {
- // Given
- const MyPlugin = {
- afterLoad(system) {
- this.rootInjects.wow = system.dogeSelectors.wow
- },
- statePlugins: {
- doge: {
- selectors: {
- wow: () => (system) => {
- return "so selective"
- }
- }
- }
- }
- }
- const system = new System({
- plugins: [
- () => {
- return [MyPlugin]
- }
- ]
- })
- // When
- let res = system.getSystem().wow()
- expect(res).toEqual("so selective")
- })
- it("should call a registered plugin's `afterLoad` method after the plugin is loaded", function() {
- // Given
- const MyPlugin = {
- afterLoad(system) {
- this.rootInjects.wow = system.dogeSelectors.wow
- },
- statePlugins: {
- doge: {
- selectors: {
- wow: () => (system) => {
- return "so selective"
- }
- }
- }
- }
- }
- const system = new System({
- plugins: []
- })
- system.register([MyPlugin])
- // When
- let res = system.getSystem().wow()
- expect(res).toEqual("so selective")
- })
- })
- describe("rootInjects", function() {
- it("should attach a rootInject function as an instance method", function() {
- // This is the same thing as the `afterLoad` tests, but is here for posterity
-
- // Given
- const system = new System({
- plugins: [
- {
- afterLoad(system) {
- this.rootInjects.wow = system.dogeSelectors.wow
- },
- statePlugins: {
- doge: {
- selectors: {
- wow: () => (system) => {
- return "so selective"
- }
- }
- }
- }
- }
- ]
- })
- // When
- let res = system.getSystem().wow()
- expect(res).toEqual("so selective")
- })
- })
- describe("error catching", function() {
- it("should encapsulate thrown errors in an afterLoad method", function() {
- // Given
- const ThrowyPlugin = {
- afterLoad(system) {
- throw new Error("afterLoad BREAKS STUFF!")
- },
- statePlugins: {
- doge: {
- selectors: {
- wow: () => (system) => {
- return "so selective"
- }
- }
- }
- }
- }
- const system = new System({
- plugins: []
- })
- // When
- expect(() => {
- system.register([ThrowyPlugin])
- // let resSystem = system.getSystem()
- }).not.toThrow()
- })
- it("should encapsulate thrown errors in an action creator", function(){
- // Given
- const system = new System({
- plugins: {
- statePlugins: {
- throw: {
- actions: {
- func() {
- throw new Error("this action creator THROWS!")
- }
- }
- }
- }
- }
- })
- expect(() => {
- // TODO: fix existing action error catcher that creates THROWN ERR actions
- system.getSystem().throwActions.func()
- }).not.toThrow()
- })
- it("should encapsulate thrown errors in a reducer", function(){
- // Given
- const system = new System({
- plugins: {
- statePlugins: {
- throw: {
- actions: {
- func: () => {
- return {
- type: "THROW_FUNC",
- payload: "BOOM!"
- }
- }
- },
- reducers: {
- "THROW_FUNC": (state, action) => {
- throw new Error("this reducer EXPLODES!")
- }
- }
- }
- }
- }
- })
- expect(() => {
- system.getSystem().throwActions.func()
- }).not.toThrow()
- })
- it("should encapsulate thrown errors in a selector", function(){
- // Given
- const system = new System({
- plugins: {
- statePlugins: {
- throw: {
- selectors: {
- func: (state, arg1) => {
- throw new Error("this selector THROWS!")
- }
- }
- }
- }
- }
- })
- expect(system.getSystem().throwSelectors.func).not.toThrow()
- })
- it("should encapsulate thrown errors in a complex selector", function(){
- // Given
- const system = new System({
- plugins: {
- statePlugins: {
- throw: {
- selectors: {
- func: (state, arg1) => system => {
- throw new Error("this selector THROWS!")
- }
- }
- }
- }
- }
- })
- expect(system.getSystem().throwSelectors.func).not.toThrow()
- })
- it("should encapsulate thrown errors in a wrapAction", function(){
- // Given
- const system = new System({
- plugins: {
- statePlugins: {
- throw: {
- actions: {
- func: () => {
- return {
- type: "THROW_FUNC",
- payload: "this original action does NOT throw"
- }
- }
- },
- wrapActions: {
- func: (ori) => (...args) => {
- throw new Error("this wrapAction UNRAVELS EVERYTHING!")
- }
- }
- }
- }
- }
- })
- expect(system.getSystem().throwActions.func).not.toThrow()
- })
- it("should encapsulate thrown errors in a wrapSelector", function(){
- // Given
- const system = new System({
- plugins: {
- statePlugins: {
- throw: {
- selectors: {
- func: (state, arg1) => {
- return 123
- }
- },
- wrapSelectors: {
- func: (ori) => (...props) => {
- return ori(...props)
- }
- }
- }
- }
- }
- })
- expect(system.getSystem().throwSelectors.func).not.toThrow()
- })
- describe("components", function() {
- it("should catch errors thrown inside of React Component Class render methods", function() {
- // Given
- class BrokenComponent extends React.Component {
- // eslint-disable-next-line react/require-render-return
- render() {
- throw new Error("This component is broken")
- }
- }
- const system = new System({
- plugins: [
- ViewPlugin,
- {
- components: {
- BrokenComponent
- }
- }
- ]
- })
- // When
- let Component = system.getSystem().getComponent("BrokenComponent")
- const renderedComponent = render(<Component />)
- // Then
- expect(renderedComponent.text()).toEqual("😱 Could not render BrokenComponent, see the console.")
- })
- it("should catch errors thrown inside of pure component render methods", function() {
- // Given
- class BrokenComponent extends PureComponent {
- // eslint-disable-next-line react/require-render-return
- render() {
- throw new Error("This component is broken")
- }
- }
- const system = new System({
- plugins: [
- ViewPlugin,
- {
- components: {
- BrokenComponent
- }
- }
- ]
- })
- // When
- let Component = system.getSystem().getComponent("BrokenComponent")
- const renderedComponent = render(<Component />)
- // Then
- expect(renderedComponent.text()).toEqual("😱 Could not render BrokenComponent, see the console.")
- })
- it("should catch errors thrown inside of stateless component functions", function() {
- // Given
- // eslint-disable-next-line react/require-render-return
- let BrokenComponent = function BrokenComponent() { throw new Error("This component is broken") }
- const system = new System({
- plugins: [
- ViewPlugin,
- {
- components: {
- BrokenComponent
- }
- }
- ]
- })
- // When
- let Component = system.getSystem().getComponent("BrokenComponent")
- const renderedComponent = render(<Component />)
- // Then
- expect(renderedComponent.text().startsWith("😱 Could not render")).toEqual(true)
- })
- it("should catch errors thrown inside of container components", function() {
- // Given
- class BrokenComponent extends React.Component {
- // eslint-disable-next-line react/require-render-return
- render() {
- throw new Error("This component is broken")
- }
- }
- const system = new System({
- plugins: [
- ViewPlugin,
- {
- components: {
- BrokenComponent
- }
- }
- ]
- })
- // When
- let Component = system.getSystem().getComponent("BrokenComponent", true)
- const renderedComponent = render(
- <Provider store={system.getStore()}>
- <Component />
- </Provider>
- )
- // Then
- expect(renderedComponent.text()).toEqual("😱 Could not render BrokenComponent, see the console.")
- })
- })
- })
- })
|