diff options
Diffstat (limited to 'packages/bun-inspector-protocol/test')
4 files changed, 190 insertions, 303 deletions
diff --git a/packages/bun-inspector-protocol/test/inspector/websocket.test.ts b/packages/bun-inspector-protocol/test/inspector/websocket.test.ts new file mode 100644 index 000000000..4a6c60c28 --- /dev/null +++ b/packages/bun-inspector-protocol/test/inspector/websocket.test.ts @@ -0,0 +1,190 @@ +import { describe, test, expect, mock, beforeAll, afterAll } from "bun:test"; +import { WebSocketInspector } from "../../src/inspector/websocket"; +import type { Server } from "bun"; +import { serve } from "bun"; + +let server: Server; +let url: URL; + +describe("WebSocketInspector", () => { + test("fails without a URL", () => { + const ws = new WebSocketInspector(); + const fn = mock(error => { + expect(error).toBeInstanceOf(Error); + }); + ws.on("Inspector.error", fn); + expect(ws.start()).resolves.toBeFalse(); + expect(fn).toHaveBeenCalled(); + }); + + test("fails with invalid URL", () => { + const ws = new WebSocketInspector("notaurl"); + const fn = mock(error => { + expect(error).toBeInstanceOf(Error); + }); + ws.on("Inspector.error", fn); + expect(ws.start()).resolves.toBeFalse(); + expect(fn).toHaveBeenCalled(); + }); + + test("fails with valid URL but no server", () => { + const ws = new WebSocketInspector("ws://localhost:0/doesnotexist/"); + const fn = mock(error => { + expect(error).toBeInstanceOf(Error); + }); + ws.on("Inspector.error", fn); + expect(ws.start()).resolves.toBeFalse(); + expect(fn).toHaveBeenCalled(); + }); + + test("fails with invalid upgrade response", () => { + const ws = new WebSocketInspector(new URL("/", url)); + const fn = mock(error => { + expect(error).toBeInstanceOf(Error); + }); + ws.on("Inspector.error", fn); + expect(ws.start()).resolves.toBeFalse(); + expect(fn).toHaveBeenCalled(); + }); + + test("can connect to a server", () => { + const ws = new WebSocketInspector(url); + const fn = mock(() => { + expect(ws.closed).toBe(false); + }); + ws.on("Inspector.connected", fn); + expect(ws.start()).resolves.toBeTrue(); + expect(fn).toHaveBeenCalled(); + ws.close(); + }); + + test("can disconnect from a server", () => { + const ws = new WebSocketInspector(url); + const fn = mock(() => { + expect(ws.closed).toBeTrue(); + }); + ws.on("Inspector.disconnected", fn); + expect(ws.start()).resolves.toBeTrue(); + ws.close(); + expect(fn).toHaveBeenCalled(); + }); + + test("can connect to a server multiple times", () => { + const ws = new WebSocketInspector(url); + const fn0 = mock(() => { + expect(ws.closed).toBeFalse(); + }); + ws.on("Inspector.connected", fn0); + const fn1 = mock(() => { + expect(ws.closed).toBeTrue(); + }); + ws.on("Inspector.disconnected", fn1); + for (let i = 0; i < 3; i++) { + expect(ws.start()).resolves.toBeTrue(); + ws.close(); + } + expect(fn0).toHaveBeenCalledTimes(3); + expect(fn1).toHaveBeenCalledTimes(3); + }); + + test("can send a request", () => { + const ws = new WebSocketInspector(url); + const fn0 = mock(request => { + expect(request).toStrictEqual({ + id: 1, + method: "Debugger.setPauseOnAssertions", + params: { + enabled: true, + }, + }); + }); + ws.on("Inspector.request", fn0); + const fn1 = mock(response => { + expect(response).toStrictEqual({ + id: 1, + result: { + ok: true, + }, + }); + }); + ws.on("Inspector.response", fn1); + expect(ws.start()).resolves.toBeTrue(); + expect(ws.send("Debugger.setPauseOnAssertions", { enabled: true })).resolves.toMatchObject({ ok: true }); + expect(fn0).toHaveBeenCalled(); + expect(fn1).toHaveBeenCalled(); + ws.close(); + }); + + test("can send a request before connecting", () => { + const ws = new WebSocketInspector(url); + const fn0 = mock(request => { + expect(request).toStrictEqual({ + id: 1, + method: "Runtime.enable", + params: {}, + }); + }); + ws.on("Inspector.pendingRequest", fn0); + ws.on("Inspector.request", fn0); + const fn1 = mock(response => { + expect(response).toStrictEqual({ + id: 1, + result: { + ok: true, + }, + }); + }); + ws.on("Inspector.response", fn1); + const request = ws.send("Runtime.enable"); + expect(ws.start()).resolves.toBe(true); + expect(request).resolves.toMatchObject({ ok: true }); + expect(fn0).toHaveBeenCalledTimes(2); + expect(fn1).toHaveBeenCalled(); + ws.close(); + }); + + test("can receive an event", () => { + const ws = new WebSocketInspector(url); + const fn = mock(event => { + expect(event).toStrictEqual({ + method: "Debugger.scriptParsed", + params: { + scriptId: "1", + }, + }); + }); + ws.on("Inspector.event", fn); + expect(ws.start()).resolves.toBeTrue(); + expect(ws.send("Debugger.enable")).resolves.toMatchObject({ ok: true }); + expect(fn).toHaveBeenCalled(); + ws.close(); + }); +}); + +beforeAll(() => { + server = serve({ + port: 0, + fetch(request, server) { + if (request.url.endsWith("/ws") && server.upgrade(request)) { + return; + } + return new Response(); + }, + websocket: { + message(ws, message) { + const { id, method } = JSON.parse(String(message)); + ws.send(JSON.stringify({ id, result: { ok: true } })); + + if (method === "Debugger.enable") { + ws.send(JSON.stringify({ method: "Debugger.scriptParsed", params: { scriptId: "1" } })); + } + }, + }, + }); + const { hostname, port } = server; + url = new URL(`ws://${hostname}:${port}/ws`); +}); + +afterAll(() => { + server?.stop(true); +}); diff --git a/packages/bun-inspector-protocol/test/util/__snapshots__/preview.test.ts.snap b/packages/bun-inspector-protocol/test/util/__snapshots__/preview.test.ts.snap deleted file mode 100644 index 0acc17575..000000000 --- a/packages/bun-inspector-protocol/test/util/__snapshots__/preview.test.ts.snap +++ /dev/null @@ -1,143 +0,0 @@ -// Bun Snapshot v1, https://goo.gl/fbAQLP - -exports[`remoteObjectToString 1`] = `"undefined"`; - -exports[`remoteObjectToString 2`] = `"null"`; - -exports[`remoteObjectToString 3`] = `"true"`; - -exports[`remoteObjectToString 4`] = `"false"`; - -exports[`remoteObjectToString 5`] = `"0"`; - -exports[`remoteObjectToString 6`] = `"1"`; - -exports[`remoteObjectToString 7`] = `"3.141592653589793"`; - -exports[`remoteObjectToString 8`] = `"-2.718281828459045"`; - -exports[`remoteObjectToString 9`] = `"NaN"`; - -exports[`remoteObjectToString 10`] = `"Infinity"`; - -exports[`remoteObjectToString 11`] = `"-Infinity"`; - -exports[`remoteObjectToString 12`] = `"0n"`; - -exports[`remoteObjectToString 13`] = `"1n"`; - -exports[`remoteObjectToString 14`] = `"10000000000000n"`; - -exports[`remoteObjectToString 15`] = `"-10000000000000n"`; - -exports[`remoteObjectToString 16`] = `""""`; - -exports[`remoteObjectToString 17`] = `"" ""`; - -exports[`remoteObjectToString 18`] = `""Hello""`; - -exports[`remoteObjectToString 19`] = `""Hello World""`; - -exports[`remoteObjectToString 20`] = `"Array(0)"`; - -exports[`remoteObjectToString 21`] = `"Array(3) [1, 2, 3]"`; - -exports[`remoteObjectToString 22`] = `"Array(4) ["a", 1, null, undefined]"`; - -exports[`remoteObjectToString 23`] = `"Array(2) [1, Array]"`; - -exports[`remoteObjectToString 24`] = `"Array(1) [Array]"`; - -exports[`remoteObjectToString 25`] = `"{}"`; - -exports[`remoteObjectToString 26`] = `"{a: 1}"`; - -exports[`remoteObjectToString 27`] = `"{a: 1, b: 2, c: 3}"`; - -exports[`remoteObjectToString 28`] = `"{a: Object}"`; - -exports[`remoteObjectToString 29`] = ` -"ƒ() { -}" -`; - -exports[`remoteObjectToString 30`] = ` -"ƒ namedFunction() { -}" -`; - -exports[`remoteObjectToString 31`] = ` -"class { -}" -`; - -exports[`remoteObjectToString 32`] = ` -"class namedClass { -}" -`; - -exports[`remoteObjectToString 33`] = ` -"class namedClass { - a() { - } - b = 1; - c = [ - null, - undefined, - "a", - { - a: 1, - b: 2, - c: 3 - } - ]; -}" -`; - -exports[`remoteObjectToString 34`] = `"Wed Dec 31 1969 16:00:00 GMT-0800 (Pacific Standard Time)"`; - -exports[`remoteObjectToString 35`] = `"Invalid Date"`; - -exports[`remoteObjectToString 36`] = `"/(?:)/"`; - -exports[`remoteObjectToString 37`] = `"/abc/"`; - -exports[`remoteObjectToString 38`] = `"/abc/g"`; - -exports[`remoteObjectToString 39`] = `"/abc/"`; - -exports[`remoteObjectToString 40`] = `"Set(0)"`; - -exports[`remoteObjectToString 41`] = `"Set(3) [1, 2, 3]"`; - -exports[`remoteObjectToString 42`] = `"WeakSet(0)"`; - -exports[`remoteObjectToString 43`] = `"WeakSet(3) [{a: 1}, {b: 2}, {c: 3}]"`; - -exports[`remoteObjectToString 44`] = `"Map(0)"`; - -exports[`remoteObjectToString 45`] = `"Map(3) {"a" => 1, "b" => 2, "c" => 3}"`; - -exports[`remoteObjectToString 46`] = `"WeakMap(0)"`; - -exports[`remoteObjectToString 47`] = `"WeakMap(3) {{a: 1} => 1, {b: 2} => 2, {c: 3} => 3}"`; - -exports[`remoteObjectToString 48`] = `"Symbol()"`; - -exports[`remoteObjectToString 49`] = `"Symbol(namedSymbol)"`; - -exports[`remoteObjectToString 50`] = `"Error"`; - -exports[`remoteObjectToString 51`] = `"TypeError: This is a TypeError"`; - -exports[`remoteObjectToString 52`] = `"Headers {append: ƒ, delete: ƒ, get: ƒ, getAll: ƒ, has: ƒ, …}"`; - -exports[`remoteObjectToString 53`] = `"Headers {a: "1", append: ƒ, b: "2", delete: ƒ, get: ƒ, …}"`; - -exports[`remoteObjectToString 54`] = `"Request {arrayBuffer: ƒ, blob: ƒ, body: null, bodyUsed: false, cache: "default", …}"`; - -exports[`remoteObjectToString 55`] = `"Request {arrayBuffer: ƒ, blob: ƒ, body: ReadableStream, bodyUsed: false, cache: "default", …}"`; - -exports[`remoteObjectToString 56`] = `"Response {arrayBuffer: ƒ, blob: ƒ, body: null, bodyUsed: false, clone: ƒ, …}"`; - -exports[`remoteObjectToString 57`] = `"Response {arrayBuffer: ƒ, blob: ƒ, body: ReadableStream, bodyUsed: false, clone: ƒ, …}"`; diff --git a/packages/bun-inspector-protocol/test/util/preview.js b/packages/bun-inspector-protocol/test/util/preview.js deleted file mode 100644 index 15062240b..000000000 --- a/packages/bun-inspector-protocol/test/util/preview.js +++ /dev/null @@ -1,99 +0,0 @@ -console.log( - undefined, - null, - true, - false, - 0, - 1, - Math.PI, - -Math.E, - NaN, - Infinity, - -Infinity, - BigInt(0), - BigInt(1), - BigInt("10000000000000"), - BigInt("-10000000000000"), - "", - " ", - "Hello", - "Hello World", - [], - [1, 2, 3], - ["a", 1, null, undefined], - [1, [2, [3, [4, [5, [6, [7, [8, [9, [10]]]]]]]]]], - [[[[[]]]]], - {}, - { a: 1 }, - { a: 1, b: 2, c: 3 }, - { a: { b: { c: { d: { e: { f: { g: { h: { i: { j: 10 } } } } } } } } } }, - function () {}, - function namedFunction() {}, - class {}, - class namedClass {}, - class namedClass { - a() {} - b = 1; - c = [ - null, - undefined, - "a", - { - a: 1, - b: 2, - c: 3, - }, - ]; - }, - new Date(0), - new Date(NaN), - new RegExp(), - new RegExp("abc"), - new RegExp("abc", "g"), - /abc/, - new Set(), - new Set([1, 2, 3]), - new WeakSet(), - new WeakSet([{ a: 1 }, { b: 2 }, { c: 3 }]), - new Map(), - new Map([ - ["a", 1], - ["b", 2], - ["c", 3], - ]), - new WeakMap(), - new WeakMap([ - [{ a: 1 }, 1], - [{ b: 2 }, 2], - [{ c: 3 }, 3], - ]), - Symbol(), - Symbol("namedSymbol"), - new Error(), - new TypeError("This is a TypeError"), - //"a".repeat(10000), - //["a"].fill("a", 0, 10000), - new Headers(), - new Headers({ - a: "1", - b: "2", - }), - new Request("https://example.com/"), - new Request("https://example.com/", { - method: "POST", - headers: { - a: "1", - b: "2", - }, - body: '{"example":true}', - }), - new Response(), - new Response('{"example":true}', { - status: 200, - statusText: "OK", - headers: { - a: "1", - b: "2", - }, - }), -); diff --git a/packages/bun-inspector-protocol/test/util/preview.test.ts b/packages/bun-inspector-protocol/test/util/preview.test.ts deleted file mode 100644 index 99214ef0e..000000000 --- a/packages/bun-inspector-protocol/test/util/preview.test.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { beforeAll, afterAll, test, expect } from "bun:test"; -import type { PipedSubprocess } from "bun"; -import { spawn } from "bun"; -import type { JSC } from "../.."; -import { WebSocketInspector, remoteObjectToString } from "../.."; - -let subprocess: PipedSubprocess | undefined; -let objects: JSC.Runtime.RemoteObject[] = []; - -beforeAll(async () => { - subprocess = spawn({ - cwd: import.meta.dir, - cmd: [process.argv0, "--inspect-wait=0", "preview.js"], - stdout: "pipe", - stderr: "pipe", - stdin: "pipe", - }); - const decoder = new TextDecoder(); - let url: URL; - for await (const chunk of subprocess!.stdout) { - const text = decoder.decode(chunk); - if (text.includes("ws://")) { - url = new URL(/(ws:\/\/.*)/.exec(text)![0]); - break; - } - } - objects = await new Promise((resolve, reject) => { - const inspector = new WebSocketInspector({ - url, - listener: { - ["Inspector.connected"]: () => { - inspector.send("Inspector.enable"); - inspector.send("Runtime.enable"); - inspector.send("Console.enable"); - inspector.send("Debugger.enable"); - inspector.send("Debugger.resume"); - inspector.send("Inspector.initialized"); - }, - ["Inspector.disconnected"]: error => { - reject(error); - }, - ["Console.messageAdded"]: ({ message }) => { - const { parameters } = message; - resolve(parameters!); - inspector.close(); - }, - }, - }); - inspector.start(); - }); -}); - -afterAll(() => { - subprocess?.kill(); -}); - -test("remoteObjectToString", () => { - for (const object of objects) { - expect(remoteObjectToString(object)).toMatchSnapshot(); - } -}); |