{"id":19787536,"url":"https://github.com/svish/cypress-helper-redux","last_synced_at":"2025-04-30T23:33:39.531Z","repository":{"id":35005197,"uuid":"195702653","full_name":"Svish/cypress-helper-redux","owner":"Svish","description":"Cypress commands for manipulating Redux stores in tests.","archived":false,"fork":false,"pushed_at":"2023-05-06T05:55:30.000Z","size":960,"stargazers_count":3,"open_issues_count":6,"forks_count":1,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-04-06T06:24:55.346Z","etag":null,"topics":["best-practices","cypress","cypress-helper","typescript","typescript-definitions"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/cypress-helper-redux","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Svish.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-07-07T22:25:45.000Z","updated_at":"2023-03-09T03:26:50.000Z","dependencies_parsed_at":"2024-11-12T06:25:10.544Z","dependency_job_id":"626b0030-fc73-4caf-852d-f603ec87a0b7","html_url":"https://github.com/Svish/cypress-helper-redux","commit_stats":{"total_commits":70,"total_committers":2,"mean_commits":35.0,"dds":"0.014285714285714235","last_synced_commit":"5918cdd4be7dd465c959b3341f41203a8733aaba"},"previous_names":[],"tags_count":18,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Svish%2Fcypress-helper-redux","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Svish%2Fcypress-helper-redux/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Svish%2Fcypress-helper-redux/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Svish%2Fcypress-helper-redux/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Svish","download_url":"https://codeload.github.com/Svish/cypress-helper-redux/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251800761,"owners_count":21645964,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["best-practices","cypress","cypress-helper","typescript","typescript-definitions"],"created_at":"2024-11-12T06:23:34.064Z","updated_at":"2025-04-30T23:33:39.175Z","avatar_url":"https://github.com/Svish.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# cypress-helper-redux\n\n[Cypress](https://www.cypress.io/) [commands](https://docs.cypress.io/api/cypress-api/custom-commands.html) for manipulating a [Redux](https://redux.js.org/) store during testing. For example to:\n\n- Set up a predictable state before certain tests\n- Dispatch actions to make state adjustmens before or during tests\n- Validate state during or after tests have run\n\n[![npm version](https://img.shields.io/npm/v/cypress-helper-redux.svg?style=flat-square) ![npm downloads](https://img.shields.io/npm/dm/cypress-helper-redux?style=flat-square)](https://www.npmjs.com/package/cypress-helper-redux)\n\n## Inspiration\n\n- [Cypress Best Practices](https://docs.cypress.io/guides/references/best-practices.html#Organizing-Tests-Logging-In-Controlling-State)\n- [This presentation at YouTube](https://www.youtube.com/watch?v=5XQOK0v_YRE\u0026t=1568)\n\n## Usage\n\n### `cy.reduxVisit`\n\nA wrapper around [`cy.visit`](https://docs.cypress.io/api/commands/visit.html#Syntax) allowing you to specify the initial Redux state you want for a test:\n\n```ts\ncy.reduxVisit('/', {\n  initialState: { ... },\n});\n```\n\nOther options you supply will be sent through to `cy.visit`:\n\n```ts\ncy.reduxVisit('/', {\n  initialState: { ... },\n  method: 'GET',\n  onBeforeLoad: (window) =\u003e console.log(window),\n});\n```\n\nIf you don't want or need to specify any initial state, you can of course also just use `cy.visit` as you normally would.\n\n### `cy.redux`\n\nGives direct access to the Redux store to do with as you please.\n\n```ts\ncy.redux().then(({ store }) =\u003e {\n  store.dispatch({\n    type: 'set-value',\n    payload: 'something',\n  });\n\n  const value = store.getState().foo.value;\n  expect(value).to.equal('something');\n});\n```\n\nIf you hooked up the action creators and selectors, they're passed in as the next arguments:\n\n```ts\ncy.redux().then(({ store, actions, selectors }) =\u003e {\n  store.dispatch(actions.foo.setSomething('something'));\n  const value = selectors.foo.getSomething(store.getState());\n});\n```\n\n### `cy.reduxSelect`\n\nWrapper around `cy.redux` for selecting a value from the current state, for example to validate something after or during a test.\n\n```ts\n// Using a function\ncy.reduxSelect(state =\u003e state.foo.value).then(value =\u003e {\n  expect(value).to.equal('something');\n});\n\n// Using an already defined selector function\ncy.reduxSelect(getValue).then(value =\u003e {\n  expect(value).to.equal('something');\n});\n```\n\n### `cy.reduxSelector`\n\nWrapper around `cy.reduxSelect` which, if you've provided it, gives you access to the selectors object so you can pick your selector from that.\n\n```ts\ncy.reduxSelector(selectors =\u003e selectors.foo.getValue).then(value =\u003e {\n  expect(value).to.equal('something');\n});\n```\n\n### `cy.reduxDispatch`\n\nWrapper around `cy.redux` for dispatching actions, for example to set something up before or during a test.\n\n```ts\n// Single action\ncy.reduxDispatch({ type: 'my-action' });\n\n// Multiple actions, as parameters\ncy.reduxDispatch(\n  { type: 'my-action' },\n  { type: 'my-other-action' },\n  { type: 'my-third-action' }\n);\n\n// Multiple actions, as array\ncy.reduxDispatch([\n  { type: 'my-action' },\n  { type: 'my-other-action' },\n  { type: 'my-third-action' },\n]);\n```\n\nYou can also provide a callback for creating the actions:\n\n```ts\n// Callback returning a single action\ncy.reduxDispatch(() =\u003e ({ type: 'my-action' }));\n\n// Callback returning an array of actions\ncy.reduxDispatch(() =\u003e [\n  { type: 'my-action' },\n  { type: 'my-other-action' },\n  { type: 'my-third-action' },\n]);\n```\n\nAnd if you've hooked up the action creators object, it's passed in as the first argument:\n\n```ts\n// Callback returning a single action, using the action creators object\ncy.reduxDispatch(actions =\u003e actions.foo.myAction());\n\n// Callback returning multiple action, using the action creators object\ncy.reduxDispatch(actions =\u003e [\n  actions.foo.myAction(),\n  actions.foo.myOtherAction(),\n  actions.foo.myThirdAction(),\n]);\n```\n\n## Setup\n\n### 1. Install dependency\n\n```shell\nnpm install --save-dev cypress-helper-redux\n```\n\n### 2. Include the custom commands\n\n```js\n// In e.g. cypress/support/index.ts\ninclude 'cypress-helper-redux';\n```\n\n### 3. Connect the helper with your store\n\n```ts\n// E.g. in src/store/index.ts\n\n// Get initial state (for using cy.reduxVisit)\nconst initialState =\n  'Cypress' in window ? (window as any).__chr__initialState__ : undefined;\n\n// Create the store, as you normally would\nconst store = createStore(rootReducer, initialState);\nexport default store;\n\n// Connect with the Cypress helper\nif ('Cypress' in window) {\n  const w = window as any;\n  w.__chr__store__ = store;\n\n  // The following is optional\n  // See the sample app for an example of how this can be setup and used\n  w.__chr__actionCreators__ = actionCreators;\n  w.__chr__selectors__ = selectors;\n}\n```\n\n### 4. Typescript\n\nThe Redux helper should know at least the first 2 of the following types, but preferably all of them if you want full IDE and typechecking joyfulness:\n\n- **`MyStore` – The type of your Redux store, i.e. the type of what `createStore` returns**  \n  Or `import { Store as MyStore } from redux` if you don't care\n- **`MyRootState` – The shape of your root state**  \n  Or `type MyRootState = any` if you don't care\n- **`MyRootAction` – The type of an allowed action**  \n  Or `type MyRootAction = AnyAction` if you don't care or have a type for that\n- **`MyActionCreators` – The type of your action creator object**  \n  Or `type MyActionCreators = any | undefined` if you don't care or don't use it\n- **`MySelectors` – The type of your selectors object**  \n  Or `type MySelectors = any | undefined` if you don't care or don't use it\n\nHaven't found a way to declare those types in an application and then \"inject\" them into `cypress-helper-redux` in a way that allows automatically adjusting the Cypress API correctly. So... as a work around, create a type-definition file in you Cypress directory, paste the following into it, and adjust the types mentioned above as needed:\n\n```ts\n// E.g in cypress/support/cypress-helper-redux.d.ts\n\n// Import and/or define the types mentioned above\nimport {\n  Store as MyStore,\n  RootState as MyRootState,\n  RootAction as MyRootAction,\n  ActionCreators as MyActionCreators,\n  Selectors as MySelectors,\n} from '../../src/store';\n\n// NOTE: Do not change the following, unless the API of cypress-helper-redux changes\n\n// Define cy.redux\ninterface ReduxResponse {\n  store: MyStore;\n  actions: MyActionCreators;\n  selectors: MySelectors;\n}\ntype Redux = () =\u003e Promise\u003cReduxResponse\u003e;\n\n// Define cy.reduxVisit\ntype ReduxVisitOptions = { initialState: Partial\u003cMyRootState\u003e } \u0026 Partial\u003c\n  Cypress.VisitOptions\n\u003e;\ntype ReduxVisit = (\n  url: string,\n  options: ReduxVisitOptions\n) =\u003e Cypress.Chainable\u003cWindow\u003e;\n\n// Define cy.reduxDispatch\ntype ReduxDispatchCallback = (\n  actions: MyActionCreators\n) =\u003e MyRootAction | MyRootAction[];\ntype ReduxDispatchParameter =\n  | MyRootAction\n  | MyRootAction[]\n  | ReduxDispatchCallback;\ntype ReduxDispatch = (\n  ...actionsOrCallback: ReduxDispatchParameter[]\n) =\u003e Promise\u003cvoid\u003e;\n\n// Define cy.reduxSelect\ntype ReduxSelectSelector\u003cT\u003e = (state: MyRootState) =\u003e T;\ntype ReduxSelect = \u003cT\u003e(selector: ReduxSelectSelector\u003cT\u003e) =\u003e Promise\u003cT\u003e;\n\n// Define cy.reduxSelector\ntype ReduxSelectPickSelector\u003cT\u003e = (\n  selectors: MySelectors\n) =\u003e ReduxSelectSelector\u003cT\u003e;\ntype ReduxSelector = \u003cT\u003e(\n  pickSelector: ReduxSelectPickSelector\u003cT\u003e\n) =\u003e Promise\u003cT\u003e;\n\n// Add them all to the Cypress chain\ndeclare global {\n  namespace Cypress {\n    interface Chainable\u003cSubject\u003e {\n      redux: Redux;\n      reduxVisit: ReduxVisit;\n      reduxDispatch: ReduxDispatch;\n      reduxSelect: ReduxSelect;\n      reduxSelector: ReduxSelector;\n    }\n  }\n}\n```\n\n## Sample\n\nFor a complete example on hooking things up, have a look at the [sample app](app).\n\nIt's also an example of how one could write things to make combining Typescript with React/Redux much more enjoyable (in my opinion at least).\n\nIt uses for example a variant of [Ducks](https://github.com/erikras/ducks-modular-redux) and [typesafe-actions](https://www.npmjs.com/package/typesafe-actions) for Redux, and [Redux Hooks](https://react-redux.js.org/next/api/hooks) in React the components. You'll also find a typesafe [redux-thunk](https://www.npmjs.com/package/redux-thunk) action, which was interesting to figure out how to type...\n\n# TODO\n\n1. **Typesafe Cypress commands, without copy-pasting stuff**  \n   Really not sure how though... ideas are welcome... 😕\n\n2. **Snapshot logging**  \n   In Cypress there seems to be a way to do snapshot logging, which I think would be very good to do before and after dispatching Redux actions. But, I haven't yet been able to figure out exactly how one does that... so, please let me know if you have any pointers... 🤔\n\n3. **Helper functions to connect store with helper**  \n   Should be simple in theory, but for some reason, everything seems to crash (getting `cy is not defined` in Cypress) the _instant_ I do _any_ import of helper code from the application code... Might be simple enough as it is though, and it might prevent any helper code ending up in application bundles, so... maybe better the way it is...? Advice and feedback welcome... 👍\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsvish%2Fcypress-helper-redux","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsvish%2Fcypress-helper-redux","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsvish%2Fcypress-helper-redux/lists"}