Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions tests/bun/tests/event-source.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { ErrorEvent } from '@standard-server/core'
import { toFetchResponse } from '@standard-server/fetch'
import { afterAll, describe, expect, it } from 'bun:test'

let createBody: () => AsyncGenerator

const server = Bun.serve({
port: 0,
fetch: () => toFetchResponse({
status: 200,
headers: {},
body: createBody(),
}),
})

afterAll(() => server.stop(true))

/**
* Listens with EventSource until a close or error event arrives.
*/
function receive(): Promise<{ event: string, data: string }[]> {
const source = new EventSource(server.url)
const received: { event: string, data: string }[] = []

return new Promise((resolve) => {
for (const event of ['message', 'close', 'error']) {
source.addEventListener(event, ({ data }: any) => {
received.push({ event, data })

if (event !== 'message') {
source.close()
resolve(received)
}
})
}
})
}

// bun-types declares EventSource, but Bun doesn't implement it yet
describe.skipIf(typeof EventSource === 'undefined')('eventSource', () => {
it('receives message and close events', async () => {
createBody = async function* () {
yield 'hello'
yield { order: 2 }
// must return a value: a close event without data is dropped by EventSource, which reconnects
return 'bye'
}

expect(await receive()).toEqual([
{ event: 'message', data: '"hello"' },
{ event: 'message', data: '{"order":2}' },
{ event: 'close', data: '"bye"' },
])
})

it('receives message and error events', async () => {
createBody = async function* () {
yield 'hello'
throw new ErrorEvent({ code: 'BOOM' })
}

expect(await receive()).toEqual([
{ event: 'message', data: '"hello"' },
{ event: 'error', data: '{"code":"BOOM"}' },
])
})
})
64 changes: 64 additions & 0 deletions tests/deno/tests/event-source.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { ErrorEvent } from '@standard-server/core'
import { toFetchResponse } from '@standard-server/fetch'
import { expect } from '@std/expect'
import { afterAll, describe, it } from '@std/testing/bdd'

let createBody: () => AsyncGenerator

const server = Deno.serve({ port: 0, onListen: () => {} }, () => toFetchResponse({
status: 200,
headers: {},
body: createBody(),
}))

afterAll(() => server.shutdown())

/**
* Listens with Deno's native EventSource until a close or error event arrives.
*/
function receive(): Promise<{ event: string, data: string }[]> {
const source = new EventSource(`http://localhost:${server.addr.port}`)
const received: { event: string, data: string }[] = []

return new Promise((resolve) => {
for (const event of ['message', 'close', 'error']) {
source.addEventListener(event, ({ data }: any) => {
received.push({ event, data })

if (event !== 'message') {
source.close()
resolve(received)
}
})
}
})
}

describe('eventSource', () => {
it('receives message and close events', async () => {
createBody = async function* () {
yield 'hello'
yield { order: 2 }
// must return a value: a close event without data is dropped by EventSource, which reconnects
return 'bye'
}

expect(await receive()).toEqual([
{ event: 'message', data: '"hello"' },
{ event: 'message', data: '{"order":2}' },
{ event: 'close', data: '"bye"' },
])
})

it('receives message and error events', async () => {
createBody = async function* () {
yield 'hello'
throw new ErrorEvent({ code: 'BOOM' })
}

expect(await receive()).toEqual([
{ event: 'message', data: '"hello"' },
{ event: 'error', data: '{"code":"BOOM"}' },
])
})
})
68 changes: 68 additions & 0 deletions tests/event-source.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import type { AddressInfo } from 'node:net'
import * as http from 'node:http'
import { ErrorEvent } from '@standard-server/core'
import { sendStandardResponse } from '@standard-server/node'

let createBody: () => AsyncGenerator

const server = http.createServer((_req, res) => sendStandardResponse(res, {
status: 200,
headers: {},
body: createBody(),
}))

server.listen(0)

afterAll(() => {
server.close()
})

/**
* Listens with EventSource until a close or error event arrives.
*/
function receive(): Promise<{ event: string, data: string }[]> {
const source = new EventSource(`http://localhost:${(server.address() as AddressInfo).port}`)
const received: { event: string, data: string }[] = []

return new Promise((resolve) => {
for (const event of ['message', 'close', 'error']) {
source.addEventListener(event, ({ data }: any) => {
received.push({ event, data })

if (event !== 'message') {
source.close()
resolve(received)
}
})
}
})
}

describe.skipIf(typeof EventSource === 'undefined')('eventSource', () => {
it('receives message and close events', async () => {
createBody = async function* () {
yield 'hello'
yield { order: 2 }
// must return a value: a close event without data is dropped by EventSource, which reconnects
return 'bye'
Comment thread
pullfrog[bot] marked this conversation as resolved.
}

expect(await receive()).toEqual([
{ event: 'message', data: '"hello"' },
{ event: 'message', data: '{"order":2}' },
{ event: 'close', data: '"bye"' },
])
})

it('receives message and error events', async () => {
createBody = async function* () {
yield 'hello'
throw new ErrorEvent({ code: 'BOOM' })
}

expect(await receive()).toEqual([
{ event: 'message', data: '"hello"' },
{ event: 'error', data: '{"code":"BOOM"}' },
])
})
})
2 changes: 2 additions & 0 deletions vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export default defineConfig(() => ({
globals: true,
include: ['**/*.test.ts'],
exclude: [...defaultExclude, '**/.claude/**', './tests/bun/**', './tests/deno/**'],
// exposes Node's EventSource global, which is still behind a flag (Node 20 - 26)
execArgv: ['--experimental-eventsource'],
coverage: {
include: ['packages/*/src/**'],
exclude: ['**.test-d.*', '**.test.*', '**/*.bench.ts', './tests/bun/**', './tests/deno/**'],
Expand Down
Loading