test('takes mixed array', () =>
  Promise.all([
    expectSaga(sagaTakeArrayMix)
      .put({ type: 'DONE', payload: 'foo payload' })
      .dispatch({ type: 'FOO', payload: 'foo payload' })
      .run(),

    expectSaga(sagaTakeArrayMix)
      .put({ type: 'DONE', payload: 'bar payload' })
      .dispatch({ type: 'BAR', payload: 'bar payload' })
      .run(),
  ]));
test('uses provided value for `cancelled`', () =>
  expectSaga(saga)
    .provide({
      cancelled: () => true,
    })
    .put({ type: 'CANCELLED' })
    .run());
test('uses provided value from `all`', () =>
  expectSaga(saga)
    .provide({
      all: () => [20, { type: 'Y', payload: 22 }],
    })
    .put({ type: 'DONE', payload: 42 })
    .run());
test('dynamic values have access to effect', () =>
  expectSaga(saga)
    .provide([
      [m.apply.fn(context.apiFunction), dynamic(effect => effect.args[0] * 3)],
    ])
    .put({ type: 'DONE', payload: 64 })
    .run());
test('handles reducers when supplying initial state', () =>
  expectSaga(saga)
    .withReducer(dogReducer, initialDog)
    .put({ type: AGE_BEFORE, payload: 11 })
    .put({ type: AGE_AFTER, payload: 12 })
    .dispatch({ type: HAVE_BIRTHDAY })
    .run());
test('matches based on error value', () => {
  const customErrorInstance = new CustomError();

  return expectSaga(errorSaga, customErrorInstance)
    .throws(customErrorInstance)
    .run();
});
test('negative take assertion fails', () =>
  expectSaga(saga)
    .not.take('READY')
    .run()
    .then(unreachableError)
    .catch(e => {
      expect(e.message).toMatch(errorRegex);
    }));
test('take assertion fails', () =>
  expectSaga(saga)
    .take('FOO')
    .run()
    .then(unreachableError)
    .catch(e => {
      expect(e.message).toMatch(errorRegex);
    }));
test('fails when non-matching error value thrown', done =>
  expectSaga(errorSaga, { message: 'Error 1' })
    .throws({ message: 'Error 2' })
    .run()
    .catch(e => {
      expect(e.message).toMatch(/but instead threw/i);
      done();
    }));
test('negative assertion fails when matching error thrown', done =>
  expectSaga(errorSaga, new CustomError())
    .not.throws(CustomError)
    .run()
    .catch(e => {
      expect(e.message).toMatch(/expected not to throw/i);
      done();
    }));
test('fails when non-matching error type thrown', done =>
  expectSaga(errorSaga, new Error())
    .throws(CustomError)
    .run()
    .catch(e => {
      expect(e.message).toMatch(/but instead threw/i);
      done();
    }));
test('uses static provided values from matchers', () => {
  const fakeTask = createMockTask();

  return expectSaga(saga)
    .provide([[m.join(fakeTask), 'hello'], forkProvider(fakeTask)])
    .put({ type: 'DONE', payload: 'hello' })
    .run();
});
test('inner static providers use dynamic values for static providers', () =>
  expectSaga(localSaga)
    .provide([
      [m.call(apiFunction), dynamic(() => 20)],
      [m.take('Y'), dynamic(() => ({ type: 'Y', payload: 22 }))],
    ])
    .put({ type: 'DONE', payload: 42 })
    .run());
test('inner static providers from matchers for `all` work', () =>
  expectSaga(localSaga)
    .provide([
      [m.call(apiFunction), 20],
      [m.take('Y'), { type: 'Y', payload: 22 }],
    ])
    .put({ type: 'DONE', payload: 42 })
    .run());
test('inner providers for `all` work', () =>
  expectSaga(localSaga)
    .provide({
      call: () => 20,
      take: () => ({ type: 'Y', payload: 22 }),
    })
    .put({ type: 'DONE', payload: 42 })
    .run());
test('checks other expectations when matching error thrown', done =>
  expectSaga(errorSaga, new CustomError())
    .put({ type: 'TEST_3' })
    .throws(CustomError)
    .run()
    .catch(e => {
      expect(e.message).toMatch(/put expectation unmet/i);
      done();
    }));
test('fails when no error thrown', done =>
  expectSaga(noErrorSaga)
    .put({ type: 'TEST_1' })
    .put({ type: 'TEST_2' })
    .throws(CustomError)
    .run()
    .catch(e => {
      expect(e.message).toMatch(/but no error thrown/i);
      done();
    }));
test('exception bubbles up when no error expected', done => {
  const errorValue = new Error();

  return expectSaga(errorSaga, errorValue)
    .run()
    .catch(e => {
      expect(e).toBe(errorValue);
      done();
    });
});
test('takes action creators with toString defined', async () => {
  const spy = jest.fn();

  await expectSaga(sagaTakeActionCreatorWithToString, spy)
    .dispatch(actionCreatorWithToString(42))
    .dispatch({ type: 'IGNORED' })
    .dispatch(actionCreatorWithToString(42))
    .silentRun(10);

  expect(spy).toHaveBeenCalledTimes(2);
});
test('uses dynamic values for static providers', () => {
  const fakeTask = createMockTask();

  return expectSaga(saga)
    .provide([
      [m.join(fakeTask), dynamic(() => 'hello')],
      forkProvider(fakeTask),
    ])
    .put({ type: 'DONE', payload: 'hello' })
    .run();
});
it('can delay actions', () => {
  function callback(elapsed) {
    expect(elapsed).toBeGreaterThanOrEqual(240);
  }

  return expectSaga(mainSaga, callback)
    .put({ type: 'DONE' })
    .dispatch({ type: 'FOO' })
    .delay(250)
    .dispatch({ type: 'BAR' })
    .run({ timeout: false });
});
test('uses provided value for `apply` via `call`', () =>
  expectSaga(saga)
    .provide({
      call({ fn, context: ctx, args: [arg] }, next) {
        if (ctx === context && fn === context.apiFunction) {
          return arg * 2;
        }

        return next();
      },
    })
    .put({ type: 'DONE', payload: 43 })
    .run());
test('dynamic values have access to effect', () =>
  expectSaga(saga)
    .provide([
      [
        m.getContext(contextVar),
        dynamic(property => {
          expect(property).toBe(contextVar);
          return 42;
        }),
      ],
    ])
    .put({ type: 'DONE', payload: 42 })
    .run());
test('uses provided value for `getContext`', () =>
  expectSaga(saga)
    .provide({
      getContext(property, next) {
        if (property === contextVar) {
          return 42;
        }

        return next();
      },
    })
    .put({ type: 'DONE', payload: 42 })
    .run());
test('handles dispatches only for reducer', () =>
  expectSaga(saga)
    .withReducer(dogReducer)

    .put({ type: AGE_BEFORE, payload: 11 })
    .put({ type: AGE_AFTER, payload: 12 })

    .dispatch({ type: READY })
    .dispatch({ type: HAVE_BIRTHDAY })
    .dispatch({ type: HAD_BIRTHDAY })
    .dispatch({ type: DONE })

    .run());
test('tests and exposes changed store state with initial state', async () => {
  const expectedFinalState = {
    name: 'Tucker',
    age: 12,
  };

  const { storeState } = await expectSaga(saga, initialDog)
    .withReducer(dogReducer)
    .hasFinalState(expectedFinalState)
    .dispatch({ type: HAVE_BIRTHDAY })
    .run();

  expect(storeState).toEqual(expectedFinalState);
});
test('fails with wrong put payload', () =>
  expectSaga(saga)
    .withReducer(dogReducer)

    .put({ type: AGE_BEFORE, payload: 11 })
    .put({ type: AGE_AFTER, payload: 11 })

    .dispatch({ type: HAVE_BIRTHDAY })

    .run()
    .then(unreachableError)
    .catch(e => {
      expect(e.message).toMatch(errorRegex);
    }));
it('can dispatch actions while running', async () => {
  const saga = expectSaga(mainSaga, () => {});

  saga.put({ type: 'DONE' });

  saga.dispatch({ type: 'FOO' });

  const promise = saga.run({ timeout: false });

  await delay(250);

  saga.dispatch({ type: 'BAR' });

  await promise;
});
test('tests negated store state with initial state', () => {
  const unexpectedFinalState = {
    name: 'Tucker',
    age: 11,
  };

  return (
    expectSaga(saga, initialDog)
      .withReducer(dogReducer)
      // $FlowFixMe
      .not.hasFinalState(unexpectedFinalState)
      .dispatch({ type: HAVE_BIRTHDAY })
      .run()
  );
});
test('hasFinalState fails with incorrect state with initial state', () => {
  const incorrectFinalState = {
    name: 'Tucker',
    age: 11,
  };

  return expectSaga(saga)
    .withReducer(dogReducer, initialDog)
    .hasFinalState(incorrectFinalState)
    .dispatch({ type: HAVE_BIRTHDAY })
    .run()
    .then(unreachableError)
    .catch(e => {
      expect(e.message).toMatch(/Expected to have final store state/);
    });
});