{"id":15350,"library":"kea-test-utils","title":"Kea Test Utilities","description":"Kea Test Utilities (kea-test-utils) is a library designed to streamline the testing of Kea logic stores, offering a fluent API for asserting logic behavior in JavaScript and TypeScript environments. Currently at version 0.2.4, it is actively maintained and ships with TypeScript types, aligning with the Kea 3.x ecosystem. The package provides core utilities such as `expectLogic` and `partial`, enabling developers to dispatch actions and then assert against the resulting state changes and subsequent dispatched actions. A critical aspect of its usage involves integrating the `testUtilsPlugin` with Kea's `resetContext` function before each test to ensure a clean, isolated testing environment. This allows for precise control over the Kea context, preventing state leakage and ensuring reliable test results. Its primary differentiator is its deep integration with Kea's internal mechanisms, providing comprehensive tools for both querying recorded action history and awaiting new actions for verification, making it indispensable for robust Kea application testing.","status":"active","version":"0.2.4","language":"javascript","source_language":"en","source_url":"https://github.com/keajs/kea-test-utils","tags":["javascript","typescript"],"install":[{"cmd":"npm install kea-test-utils","lang":"bash","label":"npm"},{"cmd":"yarn add kea-test-utils","lang":"bash","label":"yarn"},{"cmd":"pnpm add kea-test-utils","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Runtime peer dependency for Kea logic stores, required for context management and logic definition.","package":"kea","optional":false}],"imports":[{"note":"Used for fluent assertion of Kea logic actions and state. ESM is the standard for Kea 3.x.","wrong":"const { expectLogic } = require('kea-test-utils')","symbol":"expectLogic","correct":"import { expectLogic } from 'kea-test-utils'"},{"note":"Utility function for matching only a subset of object properties in `toMatchValues`. Named import is correct.","wrong":"import partial from 'kea-test-utils/partial'","symbol":"partial","correct":"import { partial } from 'kea-test-utils'"},{"note":"Must be passed to `resetContext` from 'kea' to enable action and state recording for testing. Named import is correct.","wrong":"import testUtilsPlugin from 'kea-test-utils/plugin'","symbol":"testUtilsPlugin","correct":"import { testUtilsPlugin } from 'kea-test-utils'"},{"note":"While critical for `kea-test-utils` setup, `resetContext` is imported directly from the `kea` package.","wrong":"import { resetContext } from 'kea-test-utils'","symbol":"resetContext","correct":"import { resetContext } from 'kea'"}],"quickstart":{"code":"import { kea, resetContext } from 'kea';\nimport { expectLogic, testUtilsPlugin, partial } from 'kea-test-utils';\n\ninterface CounterLogicState {\n  count: number;\n}\n\ninterface CounterLogicActions {\n  increment: (amount: number) => { amount: number };\n  decrement: (amount: number) => { amount: number };\n}\n\nconst counterLogic = kea<CounterLogicState, CounterLogicActions>({\n  path: ['scenes', 'counter'],\n  actions: {\n    increment: (amount: number) => ({ amount }),\n    decrement: (amount: number) => ({ amount }),\n  },\n  reducers: {\n    count: [\n      0,\n      {\n        increment: (state, { amount }) => state + amount,\n        decrement: (state, { amount }) => state - amount,\n      },\n    ],\n  },\n});\n\ndescribe('counterLogic', () => {\n  beforeEach(() => {\n    // Essential: Reset Kea's context and enable test utilities before each test\n    resetContext({ plugins: [testUtilsPlugin] });\n  });\n\n  test('should increment the count', async () => {\n    await expectLogic(counterLogic, () => {\n      // Dispatch actions directly on the logic\n      counterLogic.actions.increment(5);\n    })\n      .toDispatchActions(['increment'])\n      .toMatchValues({ count: 5 });\n  });\n\n  test('should decrement the count', async () => {\n    await expectLogic(counterLogic, () => {\n      counterLogic.actions.decrement(2);\n    })\n      .toDispatchActions(['decrement'])\n      .toMatchValues(partial({ count: 3 })); // Using partial to match only specific values\n  });\n\n  test('should handle multiple actions', async () => {\n    await expectLogic(counterLogic, () => {\n      counterLogic.actions.increment(10);\n      counterLogic.actions.decrement(3);\n    })\n      .toDispatchActions([\n        'increment',\n        'decrement'\n      ])\n      .toMatchValues({ count: 7 });\n  });\n});","lang":"typescript","description":"Demonstrates how to test a simple Kea logic using `expectLogic` to dispatch actions, match dispatched actions, and assert against the resulting state, including setup with `resetContext` and `testUtilsPlugin`."},"warnings":[{"fix":"Remove `listenersPlugin` from the `plugins` array passed to `resetContext` in your test setup. It is now included by default in `kea`.","message":"Upgrading Kea from v1.x to v2.x or v3.x changed how plugins are handled, specifically making 'listeners' built-in. If you explicitly passed `listenersPlugin` to `resetContext({ plugins: [...] })` in Kea v1.x, you must remove it when upgrading to Kea v2.x/v3.x or Kea will throw an error about the plugin being imported twice, preventing tests from running.","severity":"breaking","affected_versions":">=2.0"},{"fix":"Ensure `resetContext({ plugins: [testUtilsPlugin] })` is called in a `beforeEach` hook in your test suite to guarantee a clean Kea context for every test.","message":"Failure to call `resetContext({ plugins: [testUtilsPlugin] })` before each test can lead to global state leakage between tests, causing unreliable and inconsistent test results. Kea manages a global context that must be reset for proper test isolation.","severity":"gotcha","affected_versions":">=0.1"},{"fix":"Always include `testUtilsPlugin` in the `plugins` array when calling `resetContext` in your test setup: `resetContext({ plugins: [testUtilsPlugin] })`.","message":"The `expectLogic` utility relies on the `testUtilsPlugin` being loaded into Kea's context. If `testUtilsPlugin` is not included in the `plugins` array when calling `resetContext`, `expectLogic` will not be able to record actions or access the logic's internal state history, leading to test failures or unexpected behavior.","severity":"gotcha","affected_versions":">=0.1"},{"fix":"Update any hardcoded references from `kea.inline` to the new default or explicitly set `defaultPath` using `resetContext({ defaultPath: ['kea', 'inline'] })` if the old behavior is required.","message":"In Kea 1.0, the default path for logic without an explicit path (or when not using the Babel plugin) changed from `kea.inline` to `kea.logic`. If your tests or application hardcoded `kea.inline` anywhere, this change will cause issues. While this change is in the core Kea library, it impacts how logic paths are resolved in test environments.","severity":"breaking","affected_versions":">=1.0"}],"env_vars":null,"last_verified":"2026-04-21T00:00:00.000Z","next_check":"2026-07-20T00:00:00.000Z","problems":[{"fix":"Remove `listenersPlugin` from the `plugins` array in your `resetContext` call.","cause":"The `listenersPlugin` was explicitly added to `resetContext({ plugins: [...] })` after upgrading to Kea v2.x or v3.x, where listeners are now built-in to the core `kea` package.","error":"Error: Plugin 'listenersPlugin' was loaded twice."},{"fix":"Ensure `testUtilsPlugin` is passed to `resetContext` in your `beforeEach` hook: `resetContext({ plugins: [testUtilsPlugin] })`.","cause":"The `testUtilsPlugin` was not included in the `plugins` array when `resetContext` was called, preventing `expectLogic` from initializing its recording mechanisms.","error":"Error: `expectLogic` requires `testUtilsPlugin` to be enabled in `resetContext`."},{"fix":"Verify that `resetContext({ plugins: [testUtilsPlugin] })` is called within a `beforeEach` hook and that your logic is instantiated or connected correctly *after* the context has been reset.","cause":"The Kea logic might not have been properly mounted or initialized within the test context, or `resetContext` was not called, leading to an undefined logic instance. This can also happen if `resetContext` is called *after* logic instantiation.","error":"TypeError: Cannot read properties of undefined (reading 'actions') when trying to call logic.actions.someAction"}],"ecosystem":"npm"}