How to set state or dispatch actions from a navigator (ex : cypress testing)

Viewed 906

A good practice given by Cypress (e2e testing) is to set the state of the app programmatically rather than using the UI. This of course makes sense.

On this video https://www.youtube.com/watch?v=5XQOK0v_YRE Brian Mann propose this solution to expose a Redux store :

Expose store

Is there any possibility with NGXS to have access to the different state programmatically during testing ? An example is for the login process : dispatching directly a Login action or setting the store with the access token, to be logged in before any test, would be nice.

1 Answers

This cofnfiguration works for me: in app folder in model:

export interface IWindowCypress {
  Cypress: {
    __store__: Store;
  };
}

in app.module.ts:

import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core';
import {NgxsModule, Store} from '@ngxs/store';

import {AppComponent, IWindowCypress} from './app.component';
import {ZooState} from './state/zoo.state';
import {NgxsReduxDevtoolsPluginModule} from '@ngxs/devtools-plugin';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule, NgxsModule.forRoot([ZooState], {}),
    NgxsReduxDevtoolsPluginModule.forRoot()
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {
  constructor(protected store: Store) {
    const windowSore: IWindowCypress = window as unknown as IWindowCypress;
    if (windowSore.Cypress) {
      console.log('ustawiłem store');
      windowSore.Cypress.__store__ = store;
    }
  }
}

using in app component:

import {Component} from '@angular/core';
import {Store} from '@ngxs/store';
import {FeedAnimals} from './state/zoo.state';

/// <reference types="Cypress" />

export interface IWindowCypress {
  Cypress: {
    __store__: Store;
  };
}

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  title = 'cypress-ngxs';

  constructor() {
    const windowSore: IWindowCypress = window as unknown as IWindowCypress;
    if (windowSore.Cypress) {
      (windowSore.Cypress.__store__ as Store).dispatch(new FeedAnimals());
    }
  }
}

using in cypress spec:

/// <reference types="Cypress" />

import {Store} from '@ngxs/store';
import {IWindowCypress} from 'src/app/app.component';
import {FeedAnimals, ZooState} from '../../../src/app/state/zoo.state';
import {Observable} from 'rxjs';

describe('My Second Test Suite', () => {
  it('My FirstTest case', () => {
    cy.visit(' http://localhost:4200/ ');
    cy.get('.content > :nth-child(2)').should(item => {
      const windowSore: IWindowCypress = window as unknown as IWindowCypress;
      if (windowSore.Cypress) {
        // get store
        const store: Store = windowSore.Cypress.__store__;
        // declare observable
        const myObs: Observable<boolean> = store.select(ZooState.zoo$);
        // subscribe
        myObs.pipe().subscribe((feed) => console.log('from subscribe: ', feed));
        // make some dispatch
        (windowSore.Cypress.__store__ as Store).dispatch(new FeedAnimals());
        (windowSore.Cypress.__store__ as Store).dispatch(new FeedAnimals());
        (windowSore.Cypress.__store__ as Store).dispatch(new FeedAnimals());
        (windowSore.Cypress.__store__ as Store).dispatch(new FeedAnimals());
      }
    });
  });
});

and zoo state:

import {Injectable} from '@angular/core';
import {Action, Selector, State, StateContext} from '@ngxs/store';

export class FeedAnimals {
  static readonly type = '[Zoo] FeedAnimals';
}

export interface ZooStateModel {
  feed: boolean;
}

@State<ZooStateModel>({
  name: 'zoo',
  defaults: {
    feed: false
  }
})
@Injectable()
export class ZooState {

  @Selector()
  static zoo$(state: ZooStateModel): boolean {
    return state.feed;
  }

  @Action(FeedAnimals)
  feedAnimals(ctx: StateContext<ZooStateModel>): void {
    console.log('fedeeeeeed');
    const state = ctx.getState();
    ctx.setState({
      ...state,
      feed: !state.feed
    });
  }
}
Related