hono icon indicating copy to clipboard operation
hono copied to clipboard

Event in c.event is undefined when testing

Open tokenhousetopdev opened this issue 3 years ago • 4 comments

Here is my jest config:

import dotenv from 'dotenv'

dotenv.config({
	path: '.env.test'
})

export default {
  testEnvironment: 'miniflare',
  moduleFileExtensions: ['js', 'jsx', 'mjs'],
  coverageThreshold: {
    global: {
        branches: 70,
        functions: 70,
        lines: 70,
        statements: 70,
    },
  },
  coverageReporters: ['json-summary', 'html'],
  testTimeout: 10000,
  testRegex: '(\/tests\/.*|(.|\/))test.(mjs?|jsx?|js?|tsx?|ts?)$',
  testEnvironmentOptions: {
    kvNamespaces: ['PRESALE_API']
  }
}

The test is pretty basic I call an endpoint that calls event.waituntil and it inputs something into cloudflare kv cache. Here is the context.event when running locally using miniflare which works fine locally and in cloudflare:

  event: FetchEvent {
    type: 'fetch',
    defaultPrevented: false,
    cancelable: false,
    timeStamp: 4124.764995008707
  }

But when I run it using jest:

Context {
      _status: 200,
      _pretty: false,
      _prettySpace: 2,
      req: Request {
        [Symbol(realm)]: { settingsObject: {} },
        [Symbol(state)]: {
          method: 'GET',
          localURLsOnly: false,
          unsafeRequest: false,
          body: null,
          client: {},
          reservedClient: null,
          replacesClientId: '',
          window: 'client',
          keepalive: false,
          serviceWorkers: 'all',
          initiator: '',
          destination: '',
          priority: null,
          origin: 'client',
          policyContainer: 'client',
          referrer: 'client',
          referrerPolicy: '',
          mode: 'cors',
          useCORSPreflightFlag: false,
          credentials: 'same-origin',
          useCredentials: false,
          cache: 'default',
          redirect: 'follow',
          integrity: '',
          cryptoGraphicsNonceMetadata: '',
          parserMetadata: '',
          reloadNavigation: false,
          historyNavigation: false,
          userActivation: false,
          taintedOrigin: false,
          redirectCount: 0,
          responseTainting: 'basic',
          preventNoCacheCacheControlHeaderModification: false,
          done: false,
          timingAllowFailed: false,
          headersList: [HeadersList],
          urlList: [Array],
          url: [URL]
        },
        [Symbol(signal)]: AbortSignal { aborted: false },
        [Symbol(headers)]: HeadersList {
          [Symbol(headers map)]: [Map],
          [Symbol(headers map sorted)]: null
        }
      },
      env: {},
      executionCtx: undefined,
      notFoundHandler: [Function (anonymous)],
      finalized: false,
      _map: {
        projectId: 'dev',
        payload: {
          user: '12345678',
          role: 'user',
          exp: 1659969502,
          projectId: 'dev',
          signupDate: 1659967702,
          purchased: false,
          type: 'access',
          iat: 1659967702
        }
      }
    }

Event isn't defined anywhere.

What am I doing wrong? I am using Hono 1.6.4, I can upgrade if it would help.

tokenhousetopdev avatar Aug 08 '22 14:08 tokenhousetopdev

Updated to latest and issue still occurs.

tokenhousetopdev avatar Aug 08 '22 15:08 tokenhousetopdev

Hi @tokenhousetopdev !

An instance of FetchEvent (event) does not include an ExecutionContext object. So, we have to mock it. In the test of Cache Middleware, we make the mock object like this:

https://github.com/honojs/hono/blob/6d306b821873130938808cdc97a6754f29bc4998/src/middleware/cache/index.test.ts#L5-L12

And, make an instance of ExecutionContext , call app.fetch method:

const ctx = new Context()
const doSomething = async() => console.log('foo')
ctx.waitUntil(doSomething())

const res = await app.fetch(new Request('http://localhost/'), undefined, ctx)

This is mock but actually works. Although it will execute asynchronously, the waitUntil may work in your test.

Please try this, and tell me if it will work or not.

yusukebe avatar Aug 08 '22 22:08 yusukebe

Hey! Thanks for the reply but unfortunately I am still struggling with this a little.

Above my tests I have:

class Context {
  passThroughOnException() {
    throw new Error('Method not implemented.')
  }
  async waitUntil(promise) {
    await promise
  }
}

(Please bear in mind I am not using typescript)

And in my test I am doing:

const ctx = new Context()
const req = new Request(formattedUrl, options)
return app.request(req, undefined, ctx)

Upon running this I get the error This context has no FetchEvent. Where am I going wrong? I tried extending FetchEvent but ran into more issues as I don't really know what I should be setting etc.

tokenhousetopdev avatar Aug 09 '22 11:08 tokenhousetopdev

Hi @tokenhousetopdev !

Use app.fetch instead of app.request. app.request handles only Request object. To test ExecutionContext, we should use app.fetch. Here is all of the test code that really works:

import { Hono } from 'hono'
const app = new Hono()

class Context implements ExecutionContext {
  passThroughOnException(): void {
    throw new Error('Method not implemented.')
  }
  async waitUntil(promise: Promise<any>): Promise<void> {
    await promise
  }
}

app.get('/', (c) => {
  const doSomething = async () => console.log('foo')
  c.executionCtx.waitUntil(doSomething())
  return c.text('bar')
})

describe('Test the application', () => {
  it('Should return 200 response', async () => {
    const ctx = new Context()
    const req = new Request('http://localhost/')
    const res = await app.fetch(req, undefined, ctx)
    expect(res.status).toBe(200)
  })
})

yusukebe avatar Aug 09 '22 13:08 yusukebe

Ahh I am using the service worker syntax so what should I do e.g. instead of the below

app.get('/', (c) => {
  const doSomething = async () => console.log('foo')
  c.executionCtx.waitUntil(doSomething())
  return c.text('bar')
})

I would do:

app.get('/', (c) => {
  const doSomething = async () => console.log('foo')
  c.event.waitUntil(doSomething())
  return c.text('bar')
})

Thanks!

tokenhousetopdev avatar Aug 11 '22 13:08 tokenhousetopdev

Hi @tokenhousetopdev, The problem is solved?

Addition:

Wouldn't below work?

c.executionCtx.waitUntil(doSomething())

c.executionCtx is designed to work in both Service Workers and Module Workers mode. So, it should work on your code above.

yusukebe avatar Aug 11 '22 14:08 yusukebe

It seems OK to close this.

yusukebe avatar Sep 13 '22 22:09 yusukebe