text: /* eslint-env mocha */
  /** @jsx h */
  
  import { h, render, Component } from 'preact'
  import assert from 'assert'
  import { state, props } from 'cerebral/tags'
  import { Controller, Module } from 'cerebral'
  import { Container, connect } from './'
  
  const Empty = () => null
  
  describe('Preact', () => {
    let container, $, mount
  
    before(() => {
      container = document.createElement('div')
      document.body.appendChild(container)
      $ = (s) => container.querySelector(s)
      mount = (jsx) => {
        return render(jsx, container, container.firstChild)
      }
    })
    afterEach(() => {
      mount(<Empty />)
      container.innerHTML = ''
    })
    after(() => {
      document.body.removeChild(container)
      container = null
    })
    describe('container', () => {
      it('should be able to expose controller', () => {
        const controller = Controller(
          Module({
            state: {
              foo: 'bar',
            },
          })
        )
        const TestComponent = connect(
          {
            foo: state`foo`,
          },
          (props) => {
            return <div>{props.foo}</div>
          }
        )
        mount(
          <Container app={controller}>
            <TestComponent />
          </Container>
        )
  
        assert.strictEqual($('div').innerHTML, 'bar')
      })
      it('should throw when no controller provided', () => {
        const TestComponent = connect(
          {
            foo: state`foo`,
          },
          (props) => {
            return <div>{props.foo}</div>
          }
        )
        assert.throws(
          () => {
            mount(
              <Container>
                <TestComponent />
              </Container>
            )
          },
          (err) => {
            if (err instanceof Error) {
              return (
                err.message ===
                'Cerebral - You are not passing a Cerebral app to Container'
              )
            }
          }
        )
      })
      it('should throw when container component is not provided', () => {
        const TestComponent = connect(
          {
            foo: state`foo`,
          },
          (props) => {
            return <div>{props.foo}</div>
          }
        )
        assert.throws(
          () => {
            mount(<TestComponent />)
          },
          (err) => {
            if (err instanceof Error) {
              return (
                err.message ===
                'Cerebral - Can not find controller, did you remember to use the Container component? Read more at: http://cerebraljs.com/docs/api/components.html#preact-(beta)'
              )
            }
          }
        )
      })
      it('should be able to unregister component from container after unmounting component', () => {
        const controller = Controller(
          Module({
            state: {
              foo: 'bar',
            },
          }),
          {
            devtools: {
              init() {},
              send() {},
              updateWatchMap() {},
              updateComputedMap() {},
              sendWatchMap() {},
            },
          }
        )
        const TestComponent = connect(
          {
            foo: state`foo`,
          },
          (props) => {
            return <div>{props.foo}</div>
          }
        )
        mount(
          <Container app={controller}>
            <TestComponent />
          </Container>
        )
        assert.strictEqual($('div').innerHTML, 'bar')
        mount(<Empty />)
        container.innerHTML = ''
        assert.deepStrictEqual(
          controller.dependencyStore.getAllUniqueEntities(),
          []
        )
      })
    })
    describe('connect', () => {
      it('should convert component to json', () => {
        const controller = Controller(
          Module({
            state: {
              foo: 'bar',
            },
          })
        )
        const MyComponent = (props) => {
          return <div>{props.foo}</div>
        }
        MyComponent.displayName = 'Test'
        const TestComponent = connect(
          {
            foo: state`foo`,
          },
          MyComponent
        )
        const tree = mount(
          <Container app={controller}>
            <TestComponent />
          </Container>
        )
        assert.strictEqual(
          tree._component._component instanceof TestComponent,
          true
        )
        assert.strictEqual(tree._component._component.toJSON(), 'Test')
      })
      it('should render ones where multiple state matches', () => {
        const controller = Controller(
          Module({
            state: {
              foo: 'bar',
              bar: 'foo',
            },
            sequences: {
              test: [
                ({ state }) => {
                  state.set('foo', 'bar2')
                  state.set('bar', 'foo2')
                },
              ],
            },
          })
        )
        let renderCount = 0
        const TestComponent = connect(
          {
            foo: state`foo`,
            bar: state`bar`,
          },
          (props) => {
            renderCount++
            return <div>{props.foo}</div>
          }
        )
        mount(
          <Container app={controller}>
            <TestComponent />
          </Container>
        )
  
        assert.strictEqual($('div').innerHTML, 'bar')
        assert.strictEqual(renderCount, 1)
        controller.getSequence('test')()
        assert.strictEqual(renderCount, 2)
        assert.strictEqual($('div').innerHTML, 'bar2')
      })
      it('should rerender affected components', () => {
        let renderCount = 0
        const controller = Controller(
          Module({
            state: {
              foo: 'bar',
            },
            sequences: {
              methodCalled: [({ state }) => state.set('foo', 'bar2')],
            },
          })
        )
        class TestComponentClass2 extends Component {
          render() {
            renderCount++
            return <div />
          }
        }
        const TestComponent2 = connect(
          {},
          TestComponentClass2
        )
        class TestComponentClass extends Component {
          render() {
            renderCount++
            return (
              <div>
                <TestComponent2 />
              </div>
            )
          }
        }
        const TestComponent = connect(
          {
            foo: state`foo`,
          },
          TestComponentClass
        )
        mount(
          <Container app={controller}>
            <TestComponent />
          </Container>
        )
        controller.getSequence('methodCalled')()
        assert.strictEqual(renderCount, 3)
      })
      it('should allow props tag and computed', () => {
        const controller = Controller(
          Module({
            state: {
              foo: {
                baz: 'mip',
              },
              aComputed: (get) => get(props`foo`),
            },
          })
        )
        const TestComponent = connect(
          {
            a: state`aComputed`,
            b: props`bar.id`,
            c: state`foo.${props`propKey`}`,
          },
          ({ a, b, c }) => {
            return <div>{a + b + c}</div>
          }
        )
        mount(
          <Container app={controller}>
            <TestComponent foo="bar" bar={{ id: '1' }} propKey="baz" />
          </Container>
        )
        assert.strictEqual($('div').innerHTML, 'bar1mip')
      })
      it('should update on props change', (done) => {
        const controller = Controller(Module({}))
        class TestComponentClass2 extends Component {
          render() {
            return <div>{this.props.foo}</div>
          }
        }
        const TestComponent2 = connect(
          {},
          TestComponentClass2
        )
        class TestComponentClass extends Component {
          constructor(props) {
            super(props)
            this.state = { foo: 'bar' }
          }
  
          changePath() {
            this.setState(
              {
                foo: 'bar2',
              },
              () => {
                assert.strictEqual($('div').innerHTML, 'bar2')
                done()
              }
            )
          }
  
          render() {
            return <TestComponent2 foo={this.state.foo} />
          }
        }
        const TestComponent = connect(
          {},
          TestComponentClass
        )
        const tree = mount(
          <Container app={controller}>
            <TestComponent />
          </Container>
        )
        assert.strictEqual(
          tree._component._component._component instanceof TestComponentClass,
          true
        )
        assert.strictEqual($('div').innerHTML, 'bar')
        const component = tree._component._component._component
        component.changePath()
      })
    })
  })
  