Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to postpone a request on page load until worker.use is called #326

Closed
dheeraj-jn opened this issue Aug 7, 2020 · 10 comments
Closed

Comments

@dheeraj-jn
Copy link

Is your feature request related to a problem? Please describe.
I'm using Cypress as the test runner and followed the example in the documentation. It works fine for requests that are made upon an action like a button click, but for requests that are made on page load, there's a race condition between the actual request on page load and the call to worker.use(). It works fine on my local machine but fails in GitHub Actions CI.

Describe the solution you'd like
Not quite sure, but perhaps a way to postpone requests with a promise.

Describe alternatives you've considered
Delay the actual request in user land.

Additional context
None

@kettanaito
Copy link
Member

Hey, @dheeraj-jn. That's a good question.

worker.use() is a synchronous method, so promisifying it won't make much sense. I'd expect it to be complete before the tests run, if invoked in the before hook in Cypress.

It works fine on my local machine but fails in GitHub Actions CI.

Does this mean that worker.use() works fine in the before hook locally, but fails on CI only?

@dheeraj-jn
Copy link
Author

Does this mean that worker.use() works fine in the before hook locally, but fails on CI only?

Yes, it appears so.

Feel free to close this issue if it's not fixable.
BTW, thanks for great library!

@kettanaito
Copy link
Member

Could you please test this scenario on other CI (CircleCI, TravisCI)?

I don't think there's much to be done on the library's side, but if we can find a reliable reproduction scenario for this, I'd be glad to take a look at what's going on. Thanks.

@kettanaito
Copy link
Member

It may also be helpful to perform any 2 side-effects: one in the describe block, and another within a test block, to see how they behave.

describe('...', () => {
  before(() => console.log('one'))

  test('...', () => console.log('two'))
})

See how that behaves on GitHub actions.

@dheeraj-jn
Copy link
Author

Unfortunately GitHub Actions does not display console log messages in the run console.

Anyway, I worked around the problem by introducing a delay of 2 seconds in a beforeRequest hook in ky for CI runs only. This works for me. Thanks for the help.

@dheeraj-jn
Copy link
Author

I definitely look forward for a better solution to avoid flakiness and to speed up the tests.

@kettanaito
Copy link
Member

kettanaito commented Aug 17, 2020

All worker.use() does it prepends a list of given handlers to an existing list of handlers (stored in a context—a plain object that the worker references to):

use(...handlers) {
requestHandlerUtils.use(context.requestHandlers, ...handlers)
},

export function use(
currentHandlers: RequestHandlersList,
...handlers: RequestHandlersList
): void {
currentHandlers.unshift(...handlers)
}

This operation is synchronous, so it cannot introduce any race conditions on its own. Since it's the client-side of the library that references the handlers in the context, and not the worker, it's not an update issue either.

The fact that it's reproducible only on CI seems to be the only viable investigation clue. Also, maybe this is not the correct API to get the window and ensure the .then() callback is called:

before(() => {
  cy.visit('/login')
  cy.window().then((window) => {
    const { worker, rest } = window.msw
    worker.use(
      // Adds a "POST /login" runtime handler
      // for all tests in this suite.
      rest.post('/login', (req, res, ctx) => {
        return res(ctx.json({ success: true }))
      }),
    )
  })
})

I'm thinking if we should encourage to write a utility function for Cypress instead of storing the msw reference on window. You can utilize Custom Cypress commands to access the exported msw and rest variables in your tests without relying on window.

@dheeraj-jn, can you try creating this simple custom command to retrieve the mocks and let me know what you see?

// your/mocks
import { setupWorker, rest } from 'msw'

const worker = setupWorker(...)

// Export from the module instead of assigning to `window`.
export { worker, rest }

// cypress/commands/index.js
const { worker, rest } = require('../from/your/mocks)

Cypress.Commands.add('getMocks', () => {
  return { worker, rest }
})

// cypress/integration/test-suite.test.js
describe('Test suite', () => {
  before(() => {
    const { worker, rest } = cy.getMocks()
    worker.use(rest.get('/user', resolver))
  })
})

@msutkowski
Copy link
Member

Hey @dheeraj-jn! I created a repro of this targeting GitHub Actions and CircleCI earlier. What I noticed in my testing is that there are a few things that can possibly go wrong depending on your configuration.

  1. The easiest to diagnose is related to issue Improve documentation on browser integration with React #353. What I've done in this repo is follow a similar pattern, where app rendering is deferred until the worker registration promise resolves.

  2. The other issue that I ran into was in regards to CI configuration when setting this up. I experimented with the wait-on module, as well as the start-server-and-test module. I also tried to manually configure the circle config as well as the GitHub action, but ultimately decided to go with what you see in the repo. It's very possible to get a GitHub action to run the test, but fail to actually run them correctly.

I think in your case, you should try approach #1, and just compare your configuration to #2. I'm curious to hear what works for you! If you don't mind, please give us an update when you get a chance 😄

@deshiknaves
Copy link

Unfortunately GitHub Actions does not display console log messages in the run console.

Anyway, I worked around the problem by introducing a delay of 2 seconds in a beforeRequest hook in ky for CI runs only. This works for me. Thanks for the help.

The log will only show up in the browser console. To get the log out in node, you'll need something like:

// cypress/support/commands.js
Cypress.Commands.add('nodeLog', (message) => cy.task('nodeLog', message)
// cypress/plugins/index.js
module.exports = (on, config) => {
  codeCoverage(on, config)

  on('task', {
    nodeLog(message) {
      console.log(message)
      return null
    },
  })
}

Then cy.nodeLog('whatever message') that will be outputted into the node console in a GitHub Action

@kettanaito
Copy link
Member

Closing due to inactivity. Please refer to the Matt's comment if you experience a similar issue. Thanks.

@github-actions github-actions bot locked and limited conversation to collaborators Nov 14, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants