DanWahlin / Observable-Store

Observable Store provides a simple way to manage state in Angular, React, Vue.js and other front-end applications.
MIT License
638 stars 121 forks source link

How can I access the Observable-store data through the `window` object? #301

Closed JasonLandbridge closed 1 year ago

JasonLandbridge commented 1 year ago

Hi there,

Bit of a long text but I'm trying to outline my thought process here.

So i'm using Cypress and in an E2E test I'm trying to access one of my stores to trigger a certain event to then test:

BackgroundJobsService.setStatusJobUpdate(
  generateJobStatusUpdate({
    jobType: JobTypes.InspectPlexServerByPlexAccountIdJob,
    jobStatus: JobStatus.Running,
  }),
);

The store:

// BackgroundJobsService.ts
export class BackgroundJobsService extends BaseService {
  public constructor() {
    super('BackgroundJobsService', {
      stateSliceSelector: (state: IStoreState) => {
        return {
          jobStatus: state.jobStatus,
        };
      },
    });
  }
}

This returns an error in Cypress TypeError: Cannot read properties of null (reading 'jobStatus')

So I'm assuming, the store is not initialized correctly. Which is true because when I manually initialize the store in my test, the error goes away.

BackgroundJobsService.setup();
BackgroundJobsService.setStatusJobUpdate(
  generateJobStatusUpdate({
    jobType: JobTypes.InspectPlexServerByPlexAccountIdJob,
    jobStatus: JobStatus.Running,
  }),
);

This fixes the error, but long story short. I think there are now 2 separate observable-stores created. One in the context of my app and in the context of Cypress.

I tried to use ObservableStore.allStoreServices and in the context of my app, I get all 13 services back but when I run it in the Cypress E2E test file, I get back 0. Until, I do BackgroundJobsService.setup(); then all 13 services show up, while I only intialized 1?

This is most likely some weird Cypress magic happening but to get to my question:

How can I access the Observable-store data through the window object?

This way I can verify if there are indeed 2 instances or something else is going on.

Thanks in advance!

DanWahlin commented 1 year ago

Hi Jason. It's challenging to say what the issue may be here without being able to run any type of sample and see everything. But, here are my initial thoughts.

Has the store you're trying to access to trigger the event been initialized in the application at that point of the overall flow (when you call setStatusJobUpdate() from Cypress code)? I'm wondering, if a component or service where the store is being injected hasn't been called yet so that store isn't yet initialized and available. The store won't be initialized until it's injected (not sure if you're using Angular or another option) of course. When you call setup() it sounds like that is triggering it to be initialized. The fact that you're seeing 13 when calling allStoreServices in the app versus 0 in Cypress makes me wonder about the life cycle timing and if the stores haven't yet been initialized when you run the Cypress code.

As far as accessing it through the window object, it's not put into the global scope so currently doesn't support that type of access.

Normally for these types of scenarios I'll try to create a minimum viable sample to narrow down the issue. Makes it a lot easier to debug versus trying to run the entire app. But, I realize for more complex apps that can be challenging to do.

JasonLandbridge commented 1 year ago

Hi thanks for the answer and I figured it out!

So Cypress works with IFrames which indeed separates what you run in a test, from what is actually running on the app. And my initial question went at it from the wrong way, during an E2E test in Cypress I'm not supposed to mess with the internal state, I should only mimic/mock the input from what a user can do.

What happens is, Websocket message comes in (SignalR) => Observable store gets updated => Triggers change on the page. I figured I could skip the first step and mock the trigger inside the Observable store. But that was a bad idea for the above reasons, so I developed the Cypress-SignalR-Mock plugin to do this.

Leaving it here for others so my pain is not in vain :D

DanWahlin commented 1 year ago

Excellent! Glad you got it working. Thanks for sharing your insights.