Skip to content

Commit

Permalink
fix: return promises for ReporterServer.url and `ReporterServer.clo…
Browse files Browse the repository at this point in the history
…se()`
  • Loading branch information
ph-fritsche committed May 10, 2023
1 parent 1db2499 commit 6a9d8b8
Show file tree
Hide file tree
Showing 5 changed files with 262 additions and 8 deletions.
2 changes: 1 addition & 1 deletion src/conductor/ChromeTestConductor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ await ((async () => {
await execModule(${JSON.stringify(testFile)})
const runner = new TestRunner(${JSON.stringify(this.reporterServer.url)}, fetch, setTimeout)
const runner = new TestRunner(${JSON.stringify(await this.reporterServer.url)}, fetch, setTimeout)
await runner.run(${JSON.stringify(runId)}, suite)
})()).then(
r => window['__${callbackId}-resolve'](String(r)),
Expand Down
2 changes: 1 addition & 1 deletion src/conductor/NodeTestConductor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ const setTimeout = global.setTimeout
await execModule(${JSON.stringify(testFile)})
const runner = new TestRunner(${JSON.stringify(this.reporterServer.url)}, fetch, setTimeout)
const runner = new TestRunner(${JSON.stringify(await this.reporterServer.url)}, fetch, setTimeout)
await runner.run(${JSON.stringify(runId)}, suite)
exit()
Expand Down
6 changes: 3 additions & 3 deletions src/reporter/ReporterMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { TestGroup, TestResult, TestError } from '../test'
import type { CoverageMapData } from 'istanbul-lib-coverage'

export type ReporterMessageMap<
TGroup extends TestGroup,
TResult extends TestResult,
TError extends TestError,
TGroup extends TestGroup = TestGroup,
TResult extends TestResult = TestResult,
TError extends TestError = TestError,
> = {
schedule: {
runId: string
Expand Down
13 changes: 10 additions & 3 deletions src/reporter/ReporterServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,13 @@ export type ReporterEventMap = {

export class ReporterServer {
constructor() {
this.http.listen(0, '127.0.0.1')
this.url = new Promise((res, rej) => this.http.listen(0, '127.0.0.1', () => {
try {
res(this.getUrl())
} catch(e) {
rej(e)
}
}))
}

protected readonly fileServers = new Map<string, FileServer>()
Expand Down Expand Up @@ -146,8 +152,9 @@ export class ReporterServer {
res.end()
}
})
readonly url: Promise<URL>

get url() {
private getUrl() {
const addr = this.http.address()
if (!addr) {
throw new Error('Reporter server is unavailable.')
Expand All @@ -158,7 +165,7 @@ export class ReporterServer {
}

close() {
this.http.close()
return new Promise<void>((res, rej) => this.http.close((e) => e ? rej(e) : res()))
}

async registerFileServer(server: FileServer) {
Expand Down
247 changes: 247 additions & 0 deletions test/reporter/ReporterServer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
import { TestConductor } from '#src/conductor/TestConductor'
import { ReporterEventMap, ReporterServer } from '#src/reporter/ReporterServer'
import { TestGroup as ReporterTestGroup } from '#src/reporter/TestGroup'
import { TestRun } from '#src/reporter/TestRun'
import { ReporterMessageMap } from '#src/reporter/ReporterMessage'
import * as Entity from '#src/test'
import { fn } from 'jest-mock'
import fetch from 'node-fetch'
import { SourceMapGenerator } from 'source-map'
import { FileProvider, HttpFileServer } from '#src/server'

const after = new Set<() => Promise<void>|void>()
afterEach(() => Promise.allSettled(Array.from(after.values()).map(async f => {
await f()
after.delete(f)
})))

function setupReporterServer<K extends keyof ReporterEventMap>(
listen: K,
) {
const reporter = new ReporterServer()
const {run} = TestRun.create(new DummyConductor(reporter, 'http://localhost/dummy'))
reporter.testRuns.set(run.id, run)
const listener = fn<(e: ReporterEventMap[K]) => void>()
const sendReport = async (
message: K extends keyof ReporterMessageMap ? ReporterMessageMap[K] : never,
) => fetch(await reporter.url, {
method: 'POST',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify({...message, type: listen}),
})
const fileServer = new HttpFileServer(new FileProvider('/some/local/path'))

after.add(() => reporter.close())
after.add(() => fileServer.close())

reporter.emitter.addListener(listen, listener)

return {
reporter,
listener,
run,
sendReport,
fileServer,
}
}

class DummyConductor extends TestConductor {
runTestSuite(): Promise<void> {
return Promise.resolve()
}
}

class DummyTestGroup extends Entity.TestGroup {
createTest(title: string) {
const t = new Entity.Test({title})
Object.defineProperty(t, 'parent', {
configurable: true,
get: () => this,
})
this._children.push(t)
}
}

test('report `start`', async () => {
const { reporter, listener, run } = setupReporterServer('start')

await reporter.reportStart(run)

expect(listener).toHaveBeenCalledWith({
type: 'start',
run: run,
})
})

test('report `done`', async () => {
const { reporter, listener, run } = setupReporterServer('done')

await reporter.reportDone(run)

expect(listener).toHaveBeenCalledWith({
type: 'done',
run: run,
})
})

test('report `error`', async () => {
const { reporter, listener, run } = setupReporterServer('error')

const group = new ReporterTestGroup({title: 'some group'})
run.groups.set(group.id, group)
const error = new Error()

await reporter.reportError(run, group.id, error)

expect(listener).toHaveBeenCalledWith({
type: 'error',
run: run,
group: group,
error,
})
})

test('receive `schedule`', async () => {
const { listener, run, sendReport } = setupReporterServer('schedule')

const group = new DummyTestGroup({title: 'some group'})
group.createTest('some child')
group.createTest('other child')

await sendReport({
runId: run.id,
group: group,
})

expect(listener).toHaveBeenCalledWith({
type: 'schedule',
run: run,
group: expect.objectContaining({
id: group.id,
children: [
expect.objectContaining({
id: group.children[0].id,
title: 'some child',
}),
expect.objectContaining({
id: group.children[1].id,
title: 'other child',
}),
],
}),
})
expect(run.groups.get(group.id)).toEqual(expect.objectContaining({
id: group.id,
}))
expect(run.tests.get(group.children[0].id)).toEqual(expect.objectContaining({
id: group.children[0].id,
title: 'some child',
}))
})

test('receive `result`', async () => {
const { listener, run, sendReport } = setupReporterServer('result')

const test = new Entity.Test({title: 'some test'})

await sendReport({
runId: run.id,
testId: test.id,
result: new Entity.TestResult({status: 'success'}),
})

expect(listener).toHaveBeenCalledWith({
type: 'result',
run,
testId: test.id,
result: expect.objectContaining({
status: 'success',
}),
})
})

test('receive `error`', async () => {
const { reporter, listener, run, sendReport, fileServer } = setupReporterServer('error')

const group = new ReporterTestGroup({title: 'some group'})
run.groups.set(group.id, group)

const sourceMap = new SourceMapGenerator({file: 'some/file.js', skipValidation: true})
sourceMap.addMapping({
source: 'some/file.ts',
name: 'some/file.js',
generated: {line: 5, column: 10},
original: {line: 50, column: 60},
})
sourceMap.addMapping({
source: 'some/file.ts',
name: 'some/file.js',
generated: {line: 20, column: 30},
original: {line: 1, column: 2},
})
fileServer.provider.files.set('some/file.js', Promise.resolve({
content: `foo bar\n\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,${Buffer.from(String(sourceMap)).toString('base64')}`,
}))
await reporter.registerFileServer(fileServer)
const fileServerUrl = String(await fileServer.url)

await sendReport({
runId: run.id,
groupId: group.id,
hook: 'some hook',
error: new Entity.TestError({
message: 'some error',
name: 'Error',
stack: [
`Error: some error`,
` at some.function (${fileServerUrl}/some/file.js:5:10)`,
` at other.function (${fileServerUrl}/some/file.js:20:30)`,
` at unmapped.function (${fileServerUrl}/some/file.js:200:300)`,
` at unmapped.location (http://example.com/some/file.js:5:10)`,
// TODO: guard against missing files / failing requests
// ` at unmapped.file (${fileServerUrl}/unmapped/file.js:200:300)`,
].join('\n'),
}),
})

expect(listener).toHaveBeenCalledWith({
type: 'error',
run,
group: group,
error: expect.objectContaining({
message: 'some error',
name: 'Error',
hook: 'some hook',
group: group,
}),
})
expect(listener.mock.calls[0][0].error.stack).toMatchInlineSnapshot(`
Error: some error
at some.function (/some/local/path/some/file.ts:50:60)
at other.function (/some/local/path/some/file.ts:1:2)
at unmapped.function (/some/local/path/some/file.js:200:300)
at unmapped.location (http://example.com/some/file.js:5:10)
`)
})

test('receive `complete`', async () => {
const { listener, run, sendReport } = setupReporterServer('complete')

const group = new ReporterTestGroup({title: 'some group'})
run.groups.set(group.id, group)

await sendReport({
runId: run.id,
groupId: group.id,
coverage: {},
})

expect(listener).toHaveBeenCalledWith({
type: 'complete',
run,
group: group,
coverage: {},
})
})

0 comments on commit 6a9d8b8

Please sign in to comment.