mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
Improve test harness
This commit is contained in:
@@ -8,3 +8,4 @@ src/test/fixtures
|
||||
src/react-refresh.js
|
||||
test/snapshots
|
||||
test/snapshots-no-hmr
|
||||
test/js/deno
|
||||
|
||||
BIN
test/bun.lockb
BIN
test/bun.lockb
Binary file not shown.
1
test/js/deno/.gitignore
vendored
Normal file
1
test/js/deno/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.deno.*
|
||||
@@ -1,66 +1,59 @@
|
||||
// Updated: Wed, 08 Mar 2023 00:55:15 GMT
|
||||
// URL: https://raw.githubusercontent.com/denoland/deno/main/cli/tests/unit/abort_controller_test.ts
|
||||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
// Copyright 2018+ the Deno authors. All rights reserved. MIT license.
|
||||
// https://raw.githubusercontent.com/denoland/deno/main/cli/tests/unit/abort_controller_test.ts
|
||||
|
||||
import { assert, assertEquals } from "deno:harness";
|
||||
|
||||
Deno.test(function basicAbortController() {
|
||||
const controller = new AbortController();
|
||||
assert(controller);
|
||||
const { signal } = controller;
|
||||
assert(signal);
|
||||
assertEquals(signal.aborted, false);
|
||||
controller.abort();
|
||||
assertEquals(signal.aborted, true);
|
||||
const controller = new AbortController();
|
||||
assert(controller);
|
||||
const { signal } = controller;
|
||||
assert(signal);
|
||||
assertEquals(signal.aborted, false);
|
||||
controller.abort();
|
||||
assertEquals(signal.aborted, true);
|
||||
});
|
||||
|
||||
Deno.test(function signalCallsOnabort() {
|
||||
const controller = new AbortController();
|
||||
const { signal } = controller;
|
||||
let called = false;
|
||||
signal.onabort = evt => {
|
||||
assert(evt);
|
||||
assertEquals(evt.type, "abort");
|
||||
called = true;
|
||||
};
|
||||
controller.abort();
|
||||
assert(called);
|
||||
const controller = new AbortController();
|
||||
const { signal } = controller;
|
||||
let called = false;
|
||||
signal.onabort = (evt)=>{
|
||||
assert(evt);
|
||||
assertEquals(evt.type, "abort");
|
||||
called = true;
|
||||
};
|
||||
controller.abort();
|
||||
assert(called);
|
||||
});
|
||||
|
||||
Deno.test(function signalEventListener() {
|
||||
const controller = new AbortController();
|
||||
const { signal } = controller;
|
||||
let called = false;
|
||||
signal.addEventListener("abort", function (ev) {
|
||||
assert(this === signal);
|
||||
assertEquals(ev.type, "abort");
|
||||
called = true;
|
||||
});
|
||||
controller.abort();
|
||||
assert(called);
|
||||
const controller = new AbortController();
|
||||
const { signal } = controller;
|
||||
let called = false;
|
||||
signal.addEventListener("abort", function(ev) {
|
||||
assert(this === signal);
|
||||
assertEquals(ev.type, "abort");
|
||||
called = true;
|
||||
});
|
||||
controller.abort();
|
||||
assert(called);
|
||||
});
|
||||
|
||||
Deno.test(function onlyAbortsOnce() {
|
||||
const controller = new AbortController();
|
||||
const { signal } = controller;
|
||||
let called = 0;
|
||||
signal.addEventListener("abort", () => called++);
|
||||
signal.onabort = () => {
|
||||
called++;
|
||||
};
|
||||
controller.abort();
|
||||
assertEquals(called, 2);
|
||||
controller.abort();
|
||||
assertEquals(called, 2);
|
||||
const controller = new AbortController();
|
||||
const { signal } = controller;
|
||||
let called = 0;
|
||||
signal.addEventListener("abort", ()=>called++);
|
||||
signal.onabort = ()=>{
|
||||
called++;
|
||||
};
|
||||
controller.abort();
|
||||
assertEquals(called, 2);
|
||||
controller.abort();
|
||||
assertEquals(called, 2);
|
||||
});
|
||||
|
||||
Deno.test(function controllerHasProperToString() {
|
||||
const actual = Object.prototype.toString.call(new AbortController());
|
||||
assertEquals(actual, "[object AbortController]");
|
||||
const actual = Object.prototype.toString.call(new AbortController());
|
||||
assertEquals(actual, "[object AbortController]");
|
||||
});
|
||||
|
||||
Deno.test(function abortReason() {
|
||||
const signal = AbortSignal.abort("hey!");
|
||||
assertEquals(signal.aborted, true);
|
||||
assertEquals(signal.reason, "hey!");
|
||||
const signal = AbortSignal.abort("hey!");
|
||||
assertEquals(signal.aborted, true);
|
||||
assertEquals(signal.reason, "hey!");
|
||||
});
|
||||
|
||||
90
test/js/deno/fetch/body.test.ts
Normal file
90
test/js/deno/fetch/body.test.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
// Copyright 2018+ the Deno authors. All rights reserved. MIT license.
|
||||
// https://raw.githubusercontent.com/denoland/deno/main/cli/tests/unit/body_test.ts
|
||||
|
||||
import { assert, assertEquals } from "deno:harness";
|
||||
function buildBody(body: any, headers?: Headers): Body {
|
||||
const stub = new Request("http://foo/", {
|
||||
body: body,
|
||||
headers,
|
||||
method: "POST"
|
||||
});
|
||||
return stub as Body;
|
||||
}
|
||||
const intArrays = [
|
||||
Int8Array,
|
||||
Int16Array,
|
||||
Int32Array,
|
||||
Uint8Array,
|
||||
Uint16Array,
|
||||
Uint32Array,
|
||||
Uint8ClampedArray,
|
||||
Float32Array,
|
||||
Float64Array
|
||||
];
|
||||
Deno.test(async function arrayBufferFromByteArrays() {
|
||||
const buffer = new TextEncoder().encode("ahoyhoy8").buffer;
|
||||
for (const type of intArrays){
|
||||
const body = buildBody(new type(buffer));
|
||||
const text = new TextDecoder("utf-8").decode(await body.arrayBuffer());
|
||||
assertEquals(text, "ahoyhoy8");
|
||||
}
|
||||
});
|
||||
Deno.test({
|
||||
permissions: {
|
||||
net: true
|
||||
}
|
||||
}, async function bodyMultipartFormData() {
|
||||
const response = await fetch("http://localhost:4545/multipart_form_data.txt");
|
||||
assert(response.body instanceof ReadableStream);
|
||||
const text = await response.text();
|
||||
const body = buildBody(text, response.headers);
|
||||
const formData = await body.formData();
|
||||
assert(formData.has("field_1"));
|
||||
assertEquals(formData.get("field_1")!.toString(), "value_1 \r\n");
|
||||
assert(formData.has("field_2"));
|
||||
});
|
||||
Deno.test({
|
||||
permissions: {
|
||||
net: true
|
||||
}
|
||||
}, async function bodyURLEncodedFormData() {
|
||||
const response = await fetch("http://localhost:4545/subdir/form_urlencoded.txt");
|
||||
assert(response.body instanceof ReadableStream);
|
||||
const text = await response.text();
|
||||
const body = buildBody(text, response.headers);
|
||||
const formData = await body.formData();
|
||||
assert(formData.has("field_1"));
|
||||
assertEquals(formData.get("field_1")!.toString(), "Hi");
|
||||
assert(formData.has("field_2"));
|
||||
assertEquals(formData.get("field_2")!.toString(), "<Deno>");
|
||||
});
|
||||
Deno.test({
|
||||
permissions: {}
|
||||
}, async function bodyURLSearchParams() {
|
||||
const body = buildBody(new URLSearchParams({
|
||||
hello: "world"
|
||||
}));
|
||||
const text = await body.text();
|
||||
assertEquals(text, "hello=world");
|
||||
});
|
||||
Deno.test(async function bodyArrayBufferMultipleParts() {
|
||||
const parts: Uint8Array[] = [];
|
||||
let size = 0;
|
||||
for(let i = 0; i <= 150000; i++){
|
||||
const part = new Uint8Array([
|
||||
1
|
||||
]);
|
||||
parts.push(part);
|
||||
size += part.length;
|
||||
}
|
||||
let offset = 0;
|
||||
const stream = new ReadableStream({
|
||||
pull (controller) {
|
||||
const chunk = parts[offset++];
|
||||
if (!chunk) return controller.close();
|
||||
controller.enqueue(chunk);
|
||||
}
|
||||
});
|
||||
const body = buildBody(stream);
|
||||
assertEquals((await body.arrayBuffer()).byteLength, size);
|
||||
});
|
||||
@@ -1,226 +1,4 @@
|
||||
// Deno's test utilities implemented using expect().
|
||||
// https://github.com/denoland/deno/blob/main/cli/tests/unit/test_util.ts
|
||||
|
||||
import { concatArrayBuffers } from "bun";
|
||||
import { it, expect } from "bun:test";
|
||||
|
||||
export function test(fn: () => void): void {
|
||||
it(fn.name, fn);
|
||||
}
|
||||
|
||||
export function assert(condition: unknown, message?: string): asserts condition is true {
|
||||
if (message) {
|
||||
it(message, () => assert(condition));
|
||||
} else {
|
||||
expect(condition).toBeTruthy();
|
||||
}
|
||||
}
|
||||
|
||||
export function assertFalse(condition: unknown, message?: string): asserts condition is false {
|
||||
if (message) {
|
||||
it(message, () => assertFalse(condition));
|
||||
} else {
|
||||
expect(condition).toBeFalsy();
|
||||
}
|
||||
}
|
||||
|
||||
export function assertEquals(actual: unknown, expected: unknown, message?: string): void {
|
||||
if (message) {
|
||||
it(message, () => assertEquals(actual, expected));
|
||||
} else {
|
||||
expect(actual).toEqual(expected);
|
||||
}
|
||||
}
|
||||
|
||||
export function assertExists(value: unknown, message?: string): void {
|
||||
if (message) {
|
||||
it(message, () => assertExists(value));
|
||||
} else {
|
||||
expect(value).toBeDefined();
|
||||
}
|
||||
}
|
||||
|
||||
export function assertNotEquals(actual: unknown, expected: unknown, message?: string): void {
|
||||
if (message) {
|
||||
it(message, () => assertNotEquals(actual, expected));
|
||||
} else {
|
||||
expect(actual).not.toEqual(expected);
|
||||
}
|
||||
}
|
||||
|
||||
export function assertStrictEquals(actual: unknown, expected: unknown, message?: string): void {
|
||||
if (message) {
|
||||
it(message, () => assertStrictEquals(actual, expected));
|
||||
} else {
|
||||
expect(actual).toStrictEqual(expected);
|
||||
}
|
||||
}
|
||||
|
||||
export function assertNotStrictEquals(actual: unknown, expected: unknown, message?: string): void {
|
||||
if (message) {
|
||||
it(message, () => assertNotStrictEquals(actual, expected));
|
||||
} else {
|
||||
expect(actual).not.toStrictEqual(expected);
|
||||
}
|
||||
}
|
||||
|
||||
export function assertAlmostEquals(actual: unknown, expected: number, epsilon: number = 1e-7, message?: string): void {
|
||||
if (message) {
|
||||
it(message, () => assertAlmostEquals(actual, expected));
|
||||
} else if (typeof actual === "number") {
|
||||
// TODO: toBeCloseTo()
|
||||
expect(Math.abs(actual - expected)).toBeLessThanOrEqual(epsilon);
|
||||
} else {
|
||||
expect(typeof actual).toBe("number");
|
||||
}
|
||||
}
|
||||
|
||||
export function assertInstanceOf(actual: unknown, expected: unknown, message?: string): void {
|
||||
if (message) {
|
||||
it(message, () => assertInstanceOf(actual, expected));
|
||||
} else if (typeof actual === "object") {
|
||||
if (actual !== null) {
|
||||
expect(actual).toHaveProperty("constructor", expected);
|
||||
} else {
|
||||
expect(actual).not.toBeNull();
|
||||
}
|
||||
} else {
|
||||
expect(typeof actual).toBe("object");
|
||||
}
|
||||
}
|
||||
|
||||
export function assertNotInstanceOf(actual: unknown, expected: unknown, message?: string): void {
|
||||
if (message) {
|
||||
it(message, () => assertNotInstanceOf(actual, expected));
|
||||
} else if (typeof actual === "object") {
|
||||
if (actual !== null) {
|
||||
expect(actual).not.toHaveProperty("constructor", expected);
|
||||
} else {
|
||||
expect(actual).not.toBeNull();
|
||||
}
|
||||
} else {
|
||||
expect(typeof actual).toBe("object");
|
||||
}
|
||||
}
|
||||
|
||||
export function assertStringIncludes(actual: unknown, expected: string, message?: string): void {
|
||||
if (message) {
|
||||
it(message, () => assertStringIncludes(actual, expected));
|
||||
} else if (typeof actual === "string") {
|
||||
expect(actual).toContain(expected);
|
||||
} else {
|
||||
expect(typeof actual).toBe("string");
|
||||
}
|
||||
}
|
||||
|
||||
export function assertArrayIncludes(actual: unknown, expected: unknown[], message?: string): void {
|
||||
if (message) {
|
||||
it(message, () => assertArrayIncludes(actual, expected));
|
||||
} else if (Array.isArray(actual)) {
|
||||
for (const value of expected) {
|
||||
expect(actual).toContain(value);
|
||||
}
|
||||
} else {
|
||||
expect(Array.isArray(actual)).toBe(true);
|
||||
}
|
||||
}
|
||||
|
||||
export function assertMatch(actual: unknown, expected: RegExp, message?: string): void {
|
||||
if (message) {
|
||||
it(message, () => assertMatch(actual, expected));
|
||||
} else if (typeof actual === "string") {
|
||||
expect(expected.test(actual)).toBe(true);
|
||||
} else {
|
||||
expect(typeof actual).toBe("string");
|
||||
}
|
||||
}
|
||||
|
||||
export function assertNotMatch(actual: unknown, expected: RegExp, message?: string): void {
|
||||
if (message) {
|
||||
it(message, () => assertNotMatch(actual, expected));
|
||||
} else if (typeof actual === "string") {
|
||||
expect(expected.test(actual)).toBe(false);
|
||||
} else {
|
||||
expect(typeof actual).toBe("string");
|
||||
}
|
||||
}
|
||||
|
||||
export function assertObjectMatch(actual: unknown, expected: Record<PropertyKey, unknown>, message?: string): void {
|
||||
if (message) {
|
||||
it(message, () => assertObjectMatch(actual, expected));
|
||||
} else if (typeof actual === "object") {
|
||||
// TODO: toMatchObject()
|
||||
if (actual !== null) {
|
||||
const expectedKeys = Object.keys(expected);
|
||||
for (const key of Object.keys(actual)) {
|
||||
if (!expectedKeys.includes(key)) {
|
||||
// @ts-ignore
|
||||
delete actual[key];
|
||||
}
|
||||
}
|
||||
expect(actual).toEqual(expected);
|
||||
} else {
|
||||
expect(actual).not.toBeNull();
|
||||
}
|
||||
} else {
|
||||
expect(typeof actual).toBe("object");
|
||||
}
|
||||
}
|
||||
|
||||
export function assertThrows(fn: () => void, message?: string): void {
|
||||
if (message) {
|
||||
it(message, () => assertThrows(fn));
|
||||
} else {
|
||||
try {
|
||||
fn();
|
||||
} catch (error) {
|
||||
expect(error).toBeDefined();
|
||||
return;
|
||||
}
|
||||
throw new Error("Expected an error to be thrown");
|
||||
}
|
||||
}
|
||||
|
||||
export async function assertRejects(fn: () => Promise<unknown>, message?: string): Promise<void> {
|
||||
if (message) {
|
||||
it(message, () => assertRejects(fn));
|
||||
} else {
|
||||
try {
|
||||
await fn();
|
||||
} catch (error) {
|
||||
expect(error).toBeDefined();
|
||||
return;
|
||||
}
|
||||
throw new Error("Expected an error to be thrown");
|
||||
}
|
||||
}
|
||||
|
||||
export function equal(a: unknown, b: unknown): boolean {
|
||||
return Bun.deepEquals(a, b);
|
||||
}
|
||||
|
||||
export function fail(message: string): never {
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
export function unimplemented(message: string): never {
|
||||
throw new Error(`Unimplemented: ${message}`);
|
||||
}
|
||||
|
||||
export function unreachable(): never {
|
||||
throw new Error("Unreachable");
|
||||
}
|
||||
|
||||
export function concat(...buffers: Uint8Array[]): Uint8Array {
|
||||
return new Uint8Array(concatArrayBuffers(buffers));
|
||||
}
|
||||
|
||||
export function inspect(...args: unknown[]): string {
|
||||
return Bun.inspect(...args);
|
||||
}
|
||||
|
||||
// @ts-expect-error
|
||||
globalThis["Deno"] = {
|
||||
test,
|
||||
inspect,
|
||||
};
|
||||
export * from "./harness/global.js";
|
||||
export * from "./harness/util.js";
|
||||
export * from "./harness/assert.js";
|
||||
export * from "./harness/fixture.js";
|
||||
|
||||
207
test/js/deno/harness/assert.ts
Normal file
207
test/js/deno/harness/assert.ts
Normal file
@@ -0,0 +1,207 @@
|
||||
// Deno's test assertions implemented using expect().
|
||||
// https://github.com/denoland/deno/blob/main/cli/tests/unit/test_util.ts
|
||||
|
||||
import { test, expect } from "bun:test";
|
||||
|
||||
export function assert(condition: unknown, message?: string): asserts condition is true {
|
||||
if (message) {
|
||||
test(message, () => assert(condition));
|
||||
} else {
|
||||
expect(condition).toBeTruthy();
|
||||
}
|
||||
}
|
||||
|
||||
export function assertFalse(condition: unknown, message?: string): asserts condition is false {
|
||||
if (message) {
|
||||
test(message, () => assertFalse(condition));
|
||||
} else {
|
||||
expect(condition).toBeFalsy();
|
||||
}
|
||||
}
|
||||
|
||||
export function assertEquals(actual: unknown, expected: unknown, message?: string): void {
|
||||
if (message) {
|
||||
test(message, () => assertEquals(actual, expected));
|
||||
} else {
|
||||
expect(actual).toEqual(expected);
|
||||
}
|
||||
}
|
||||
|
||||
export function assertExists(value: unknown, message?: string): void {
|
||||
if (message) {
|
||||
test(message, () => assertExists(value));
|
||||
} else {
|
||||
expect(value).toBeDefined();
|
||||
}
|
||||
}
|
||||
|
||||
export function assertNotEquals(actual: unknown, expected: unknown, message?: string): void {
|
||||
if (message) {
|
||||
test(message, () => assertNotEquals(actual, expected));
|
||||
} else {
|
||||
expect(actual).not.toEqual(expected);
|
||||
}
|
||||
}
|
||||
|
||||
export function assertStrictEquals(actual: unknown, expected: unknown, message?: string): void {
|
||||
if (message) {
|
||||
test(message, () => assertStrictEquals(actual, expected));
|
||||
} else {
|
||||
expect(actual).toStrictEqual(expected);
|
||||
}
|
||||
}
|
||||
|
||||
export function assertNotStrictEquals(actual: unknown, expected: unknown, message?: string): void {
|
||||
if (message) {
|
||||
test(message, () => assertNotStrictEquals(actual, expected));
|
||||
} else {
|
||||
expect(actual).not.toStrictEqual(expected);
|
||||
}
|
||||
}
|
||||
|
||||
export function assertAlmostEquals(actual: unknown, expected: number, epsilon: number = 1e-7, message?: string): void {
|
||||
if (message) {
|
||||
test(message, () => assertAlmostEquals(actual, expected));
|
||||
} else if (typeof actual === "number") {
|
||||
// TODO: toBeCloseTo()
|
||||
expect(Math.abs(actual - expected)).toBeLessThanOrEqual(epsilon);
|
||||
} else {
|
||||
expect(typeof actual).toBe("number");
|
||||
}
|
||||
}
|
||||
|
||||
export function assertInstanceOf(actual: unknown, expected: unknown, message?: string): void {
|
||||
if (message) {
|
||||
test(message, () => assertInstanceOf(actual, expected));
|
||||
} else if (typeof actual === "object") {
|
||||
if (actual !== null) {
|
||||
expect(actual).toHaveProperty("constructor", expected);
|
||||
} else {
|
||||
expect(actual).not.toBeNull();
|
||||
}
|
||||
} else {
|
||||
expect(typeof actual).toBe("object");
|
||||
}
|
||||
}
|
||||
|
||||
export function assertNotInstanceOf(actual: unknown, expected: unknown, message?: string): void {
|
||||
if (message) {
|
||||
test(message, () => assertNotInstanceOf(actual, expected));
|
||||
} else if (typeof actual === "object") {
|
||||
if (actual !== null) {
|
||||
expect(actual).not.toHaveProperty("constructor", expected);
|
||||
} else {
|
||||
expect(actual).not.toBeNull();
|
||||
}
|
||||
} else {
|
||||
expect(typeof actual).toBe("object");
|
||||
}
|
||||
}
|
||||
|
||||
export function assertStringIncludes(actual: unknown, expected: string, message?: string): void {
|
||||
if (message) {
|
||||
test(message, () => assertStringIncludes(actual, expected));
|
||||
} else if (typeof actual === "string") {
|
||||
expect(actual).toContain(expected);
|
||||
} else {
|
||||
expect(typeof actual).toBe("string");
|
||||
}
|
||||
}
|
||||
|
||||
export function assertArrayIncludes(actual: unknown, expected: unknown[], message?: string): void {
|
||||
if (message) {
|
||||
test(message, () => assertArrayIncludes(actual, expected));
|
||||
} else if (Array.isArray(actual)) {
|
||||
for (const value of expected) {
|
||||
expect(actual).toContain(value);
|
||||
}
|
||||
} else {
|
||||
expect(Array.isArray(actual)).toBe(true);
|
||||
}
|
||||
}
|
||||
|
||||
export function assertMatch(actual: unknown, expected: RegExp, message?: string): void {
|
||||
if (message) {
|
||||
test(message, () => assertMatch(actual, expected));
|
||||
} else if (typeof actual === "string") {
|
||||
expect(expected.test(actual)).toBe(true);
|
||||
} else {
|
||||
expect(typeof actual).toBe("string");
|
||||
}
|
||||
}
|
||||
|
||||
export function assertNotMatch(actual: unknown, expected: RegExp, message?: string): void {
|
||||
if (message) {
|
||||
test(message, () => assertNotMatch(actual, expected));
|
||||
} else if (typeof actual === "string") {
|
||||
expect(expected.test(actual)).toBe(false);
|
||||
} else {
|
||||
expect(typeof actual).toBe("string");
|
||||
}
|
||||
}
|
||||
|
||||
export function assertObjectMatch(actual: unknown, expected: Record<PropertyKey, unknown>, message?: string): void {
|
||||
if (message) {
|
||||
test(message, () => assertObjectMatch(actual, expected));
|
||||
} else if (typeof actual === "object") {
|
||||
// TODO: toMatchObject()
|
||||
if (actual !== null) {
|
||||
const expectedKeys = Object.keys(expected);
|
||||
for (const key of Object.keys(actual)) {
|
||||
if (!expectedKeys.includes(key)) {
|
||||
// @ts-ignore
|
||||
delete actual[key];
|
||||
}
|
||||
}
|
||||
expect(actual).toEqual(expected);
|
||||
} else {
|
||||
expect(actual).not.toBeNull();
|
||||
}
|
||||
} else {
|
||||
expect(typeof actual).toBe("object");
|
||||
}
|
||||
}
|
||||
|
||||
export function assertThrows(fn: () => void, message?: string): void {
|
||||
if (message) {
|
||||
test(message, () => assertThrows(fn));
|
||||
} else {
|
||||
try {
|
||||
fn();
|
||||
} catch (error) {
|
||||
expect(error).toBeDefined();
|
||||
return;
|
||||
}
|
||||
throw new Error("Expected an error to be thrown");
|
||||
}
|
||||
}
|
||||
|
||||
export async function assertRejects(fn: () => Promise<unknown>, message?: string): Promise<void> {
|
||||
if (message) {
|
||||
test(message, () => assertRejects(fn));
|
||||
} else {
|
||||
try {
|
||||
await fn();
|
||||
} catch (error) {
|
||||
expect(error).toBeDefined();
|
||||
return;
|
||||
}
|
||||
throw new Error("Expected an error to be thrown");
|
||||
}
|
||||
}
|
||||
|
||||
export function equal(a: unknown, b: unknown): boolean {
|
||||
return Bun.deepEquals(a, b);
|
||||
}
|
||||
|
||||
export function fail(message: string): never {
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
export function unimplemented(message: string): never {
|
||||
throw new Error(`Unimplemented: ${message}`);
|
||||
}
|
||||
|
||||
export function unreachable(): never {
|
||||
throw new Error("Unreachable");
|
||||
}
|
||||
26
test/js/deno/harness/fixture.ts
Normal file
26
test/js/deno/harness/fixture.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import type { Server } from "bun";
|
||||
import { serve } from "bun";
|
||||
import { afterAll, beforeAll } from "bun:test";
|
||||
|
||||
let server: Server;
|
||||
|
||||
beforeAll(() => {
|
||||
server = serve({
|
||||
port: 4545,
|
||||
fetch(request: Request): Response {
|
||||
const { url } = request;
|
||||
const { pathname, search } = new URL(url);
|
||||
const redirect = new URL(
|
||||
`${pathname}?${search}`,
|
||||
"https://raw.githubusercontent.com/denoland/deno/main/cli/tests/testdata/",
|
||||
);
|
||||
return Response.redirect(redirect.toString());
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
if (server) {
|
||||
server.stop(true);
|
||||
}
|
||||
});
|
||||
45
test/js/deno/harness/global.ts
Normal file
45
test/js/deno/harness/global.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { test as bunTest } from "bun:test";
|
||||
|
||||
type Fn = () => void | Promise<unknown>;
|
||||
type Options = {
|
||||
permissions?: {
|
||||
net?: boolean;
|
||||
};
|
||||
ignore?: boolean;
|
||||
};
|
||||
|
||||
function test(arg0: Fn | Options, arg1?: Fn): void {
|
||||
if (typeof arg0 === "function") {
|
||||
bunTest(arg0.name, arg0);
|
||||
} else if (typeof arg1 === "function") {
|
||||
if (arg0?.ignore === true || arg0?.permissions?.net === false) {
|
||||
bunTest.skip(arg1.name, arg1);
|
||||
} else {
|
||||
bunTest(arg1.name, arg1);
|
||||
}
|
||||
} else {
|
||||
throw new Error("Unimplemented");
|
||||
}
|
||||
}
|
||||
|
||||
test.ignore = (arg0: Fn | Options, arg1?: Fn) => {
|
||||
if (typeof arg0 === "function") {
|
||||
bunTest.skip(arg0.name, arg0);
|
||||
} else if (typeof arg1 === "function") {
|
||||
bunTest.skip(arg1.name, arg1);
|
||||
} else {
|
||||
throw new Error("Unimplemented");
|
||||
}
|
||||
};
|
||||
|
||||
export function inspect(...args: unknown[]): string {
|
||||
return Bun.inspect(...args);
|
||||
}
|
||||
|
||||
export const Deno = {
|
||||
test,
|
||||
inspect,
|
||||
};
|
||||
|
||||
// @ts-expect-error
|
||||
globalThis["Deno"] = Deno;
|
||||
5
test/js/deno/harness/util.ts
Normal file
5
test/js/deno/harness/util.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { concatArrayBuffers } from "bun";
|
||||
|
||||
export function concat(...buffers: Uint8Array[]): Uint8Array {
|
||||
return new Uint8Array(concatArrayBuffers(buffers));
|
||||
}
|
||||
@@ -1,114 +1,119 @@
|
||||
// Updated: Wed, 08 Mar 2023 00:55:15 GMT
|
||||
// URL: https://raw.githubusercontent.com/denoland/deno/main/cli/tests/unit/blob_test.ts
|
||||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
// Copyright 2018+ the Deno authors. All rights reserved. MIT license.
|
||||
// https://raw.githubusercontent.com/denoland/deno/main/cli/tests/unit/blob_test.ts
|
||||
|
||||
import { assert, assertEquals, assertStringIncludes } from "deno:harness";
|
||||
import { concat } from "deno:harness";
|
||||
|
||||
Deno.test(function blobString() {
|
||||
const b1 = new Blob(["Hello World"]);
|
||||
const str = "Test";
|
||||
const b2 = new Blob([b1, str]);
|
||||
assertEquals(b2.size, b1.size + str.length);
|
||||
const b1 = new Blob([
|
||||
"Hello World"
|
||||
]);
|
||||
const str = "Test";
|
||||
const b2 = new Blob([
|
||||
b1,
|
||||
str
|
||||
]);
|
||||
assertEquals(b2.size, b1.size + str.length);
|
||||
});
|
||||
|
||||
Deno.test(function blobBuffer() {
|
||||
const buffer = new ArrayBuffer(12);
|
||||
const u8 = new Uint8Array(buffer);
|
||||
const f1 = new Float32Array(buffer);
|
||||
const b1 = new Blob([buffer, u8]);
|
||||
assertEquals(b1.size, 2 * u8.length);
|
||||
const b2 = new Blob([b1, f1]);
|
||||
assertEquals(b2.size, 3 * u8.length);
|
||||
const buffer = new ArrayBuffer(12);
|
||||
const u8 = new Uint8Array(buffer);
|
||||
const f1 = new Float32Array(buffer);
|
||||
const b1 = new Blob([
|
||||
buffer,
|
||||
u8
|
||||
]);
|
||||
assertEquals(b1.size, 2 * u8.length);
|
||||
const b2 = new Blob([
|
||||
b1,
|
||||
f1
|
||||
]);
|
||||
assertEquals(b2.size, 3 * u8.length);
|
||||
});
|
||||
|
||||
Deno.test(function blobSlice() {
|
||||
const blob = new Blob(["Deno", "Foo"]);
|
||||
const b1 = blob.slice(0, 3, "Text/HTML");
|
||||
assert(b1 instanceof Blob);
|
||||
assertEquals(b1.size, 3);
|
||||
assertEquals(b1.type, "text/html");
|
||||
const b2 = blob.slice(-1, 3);
|
||||
assertEquals(b2.size, 0);
|
||||
const b3 = blob.slice(100, 3);
|
||||
assertEquals(b3.size, 0);
|
||||
const b4 = blob.slice(0, 10);
|
||||
assertEquals(b4.size, blob.size);
|
||||
const blob = new Blob([
|
||||
"Deno",
|
||||
"Foo"
|
||||
]);
|
||||
const b1 = blob.slice(0, 3, "Text/HTML");
|
||||
assert(b1 instanceof Blob);
|
||||
assertEquals(b1.size, 3);
|
||||
assertEquals(b1.type, "text/html");
|
||||
const b2 = blob.slice(-1, 3);
|
||||
assertEquals(b2.size, 0);
|
||||
const b3 = blob.slice(100, 3);
|
||||
assertEquals(b3.size, 0);
|
||||
const b4 = blob.slice(0, 10);
|
||||
assertEquals(b4.size, blob.size);
|
||||
});
|
||||
|
||||
Deno.test(function blobInvalidType() {
|
||||
const blob = new Blob(["foo"], {
|
||||
type: "\u0521",
|
||||
});
|
||||
|
||||
assertEquals(blob.type, "");
|
||||
const blob = new Blob([
|
||||
"foo"
|
||||
], {
|
||||
type: "\u0521"
|
||||
});
|
||||
assertEquals(blob.type, "");
|
||||
});
|
||||
|
||||
Deno.test(function blobShouldNotThrowError() {
|
||||
let hasThrown = false;
|
||||
|
||||
try {
|
||||
// deno-lint-ignore no-explicit-any
|
||||
const options1: any = {
|
||||
ending: "utf8",
|
||||
hasOwnProperty: "hasOwnProperty",
|
||||
};
|
||||
const options2 = Object.create(null);
|
||||
new Blob(["Hello World"], options1);
|
||||
new Blob(["Hello World"], options2);
|
||||
} catch {
|
||||
hasThrown = true;
|
||||
}
|
||||
|
||||
assertEquals(hasThrown, false);
|
||||
});
|
||||
|
||||
/* TODO https://github.com/denoland/deno/issues/7540
|
||||
Deno.test(function nativeEndLine() {
|
||||
const options = {
|
||||
ending: "native",
|
||||
} as const;
|
||||
const blob = new Blob(["Hello\nWorld"], options);
|
||||
|
||||
assertEquals(blob.size, Deno.build.os === "windows" ? 12 : 11);
|
||||
});
|
||||
*/
|
||||
|
||||
Deno.test(async function blobText() {
|
||||
const blob = new Blob(["Hello World"]);
|
||||
assertEquals(await blob.text(), "Hello World");
|
||||
});
|
||||
|
||||
Deno.test(async function blobStream() {
|
||||
const blob = new Blob(["Hello World"]);
|
||||
const stream = blob.stream();
|
||||
assert(stream instanceof ReadableStream);
|
||||
const reader = stream.getReader();
|
||||
let bytes = new Uint8Array();
|
||||
const read = async (): Promise<void> => {
|
||||
const { done, value } = await reader.read();
|
||||
if (!done && value) {
|
||||
bytes = concat(bytes, value);
|
||||
return read();
|
||||
let hasThrown = false;
|
||||
try {
|
||||
const options1: any = {
|
||||
ending: "utf8",
|
||||
hasOwnProperty: "hasOwnProperty"
|
||||
};
|
||||
const options2 = Object.create(null);
|
||||
new Blob([
|
||||
"Hello World"
|
||||
], options1);
|
||||
new Blob([
|
||||
"Hello World"
|
||||
], options2);
|
||||
} catch {
|
||||
hasThrown = true;
|
||||
}
|
||||
};
|
||||
await read();
|
||||
const decoder = new TextDecoder();
|
||||
assertEquals(decoder.decode(bytes), "Hello World");
|
||||
assertEquals(hasThrown, false);
|
||||
});
|
||||
Deno.test(async function blobText() {
|
||||
const blob = new Blob([
|
||||
"Hello World"
|
||||
]);
|
||||
assertEquals(await blob.text(), "Hello World");
|
||||
});
|
||||
Deno.test(async function blobStream() {
|
||||
const blob = new Blob([
|
||||
"Hello World"
|
||||
]);
|
||||
const stream = blob.stream();
|
||||
assert(stream instanceof ReadableStream);
|
||||
const reader = stream.getReader();
|
||||
let bytes = new Uint8Array();
|
||||
const read = async (): Promise<void> =>{
|
||||
const { done , value } = await reader.read();
|
||||
if (!done && value) {
|
||||
bytes = concat(bytes, value);
|
||||
return read();
|
||||
}
|
||||
};
|
||||
await read();
|
||||
const decoder = new TextDecoder();
|
||||
assertEquals(decoder.decode(bytes), "Hello World");
|
||||
});
|
||||
|
||||
Deno.test(async function blobArrayBuffer() {
|
||||
const uint = new Uint8Array([102, 111, 111]);
|
||||
const blob = new Blob([uint]);
|
||||
assertEquals(await blob.arrayBuffer(), uint.buffer);
|
||||
const uint = new Uint8Array([
|
||||
102,
|
||||
111,
|
||||
111
|
||||
]);
|
||||
const blob = new Blob([
|
||||
uint
|
||||
]);
|
||||
assertEquals(await blob.arrayBuffer(), uint.buffer);
|
||||
});
|
||||
|
||||
Deno.test(function blobConstructorNameIsBlob() {
|
||||
const blob = new Blob();
|
||||
assertEquals(blob.constructor.name, "Blob");
|
||||
const blob = new Blob();
|
||||
assertEquals(blob.constructor.name, "Blob");
|
||||
});
|
||||
|
||||
Deno.test(function blobCustomInspectFunction() {
|
||||
const blob = new Blob();
|
||||
assertEquals(Deno.inspect(blob), `Blob { size: 0, type: "" }`);
|
||||
assertStringIncludes(Deno.inspect(Blob.prototype), "Blob");
|
||||
Deno.test.ignore(function blobCustomInspectFunction() {
|
||||
const blob = new Blob();
|
||||
assertEquals(Deno.inspect(blob), `Blob { size: 0, type: "" }`);
|
||||
assertStringIncludes(Deno.inspect(Blob.prototype), "Blob");
|
||||
});
|
||||
|
||||
@@ -5,6 +5,11 @@
|
||||
},
|
||||
{
|
||||
"path": "html/blob.test.ts",
|
||||
"remotePath": "unit/blob_test.ts"
|
||||
"remotePath": "unit/blob_test.ts",
|
||||
"skip": ["blobCustomInspectFunction"]
|
||||
},
|
||||
{
|
||||
"path": "fetch/body.test.ts",
|
||||
"remotePath": "unit/body_test.ts"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,26 +1,102 @@
|
||||
import { mkdirSync } from "node:fs";
|
||||
import { join, dirname } from "node:path";
|
||||
import { parse, print } from "@swc/core";
|
||||
import type { ImportDeclaration, ExpressionStatement, CallExpression } from "@swc/core";
|
||||
import imports from "../resources/imports.json";
|
||||
import tests from "../resources/tests.json";
|
||||
|
||||
// FIXME: https://github.com/oven-sh/bun/issues/2350
|
||||
// import * as harness from "deno:harness";
|
||||
|
||||
for (const test of tests) {
|
||||
const path = join(import.meta.dir, "..", test.path);
|
||||
const url = new URL(test.remotePath, "https://raw.githubusercontent.com/denoland/deno/main/cli/tests/");
|
||||
const response = await fetch(url);
|
||||
console.log(response.status, url.toString(), "->", test.path);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to download from GitHub: ${url} [status: ${response.status}]`);
|
||||
continue;
|
||||
}
|
||||
let body = await response.text();
|
||||
for (const query of imports) {
|
||||
const pattern = new RegExp(`"(.*${query})"`, "gmi");
|
||||
body = body.replace(pattern, '"deno:harness"');
|
||||
}
|
||||
const src = `// Updated: ${response.headers.get("Date")}
|
||||
// URL: ${url}
|
||||
${body}`;
|
||||
const src = await response.text();
|
||||
const dst = await visit(src, test);
|
||||
try {
|
||||
mkdirSync(dirname(path));
|
||||
} catch {}
|
||||
await Bun.write(path, src);
|
||||
await Bun.write(path.replace(".test.", ".deno."), src);
|
||||
await Bun.write(path, dst);
|
||||
}
|
||||
|
||||
async function visit(src: string, test: any): Promise<string> {
|
||||
const ast = await parse(src, {
|
||||
syntax: "typescript",
|
||||
target: "esnext",
|
||||
dynamicImport: true,
|
||||
});
|
||||
for (const item of ast.body) {
|
||||
if (item.type === "ImportDeclaration") {
|
||||
visitImport(item);
|
||||
}
|
||||
if (item.type === "ExpressionStatement") {
|
||||
visitExpression(item, test);
|
||||
}
|
||||
}
|
||||
const header = `// Copyright 2018+ the Deno authors. All rights reserved. MIT license.
|
||||
// https://raw.githubusercontent.com/denoland/deno/main/cli/tests/${test.remotePath}
|
||||
\n`;
|
||||
const { code } = await print(ast, {
|
||||
isModule: true,
|
||||
});
|
||||
return header + code;
|
||||
}
|
||||
|
||||
function visitImport(item: ImportDeclaration): void {
|
||||
const src = item.source.value;
|
||||
let match = false;
|
||||
for (const name of imports) {
|
||||
if (src.endsWith(name)) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!match) {
|
||||
console.warn("Unknown import:", src);
|
||||
return;
|
||||
}
|
||||
item.source.raw = '"deno:harness"';
|
||||
// FIXME: https://github.com/oven-sh/bun/issues/2350
|
||||
/*let missing = [];
|
||||
for (const specifier of item.specifiers) {
|
||||
const name = specifier.local.value;
|
||||
if (!(name in harness)) {
|
||||
missing.push(name);
|
||||
}
|
||||
}
|
||||
if (missing.length) {
|
||||
console.warn("Harness does not contain exports:", missing);
|
||||
}*/
|
||||
}
|
||||
|
||||
function visitExpression(item: ExpressionStatement, test: any): void {
|
||||
if (
|
||||
item.expression.type === "CallExpression" &&
|
||||
item.expression.callee.type === "MemberExpression" &&
|
||||
item.expression.callee.object.type === "Identifier" &&
|
||||
item.expression.callee.object.value === "Deno"
|
||||
) {
|
||||
if (item.expression.callee.property.type === "Identifier" && item.expression.callee.property.value === "test") {
|
||||
visitTest(item.expression);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function visitTest(item: CallExpression): void {
|
||||
for (const argument of item.arguments) {
|
||||
if (argument.expression.type === "FunctionExpression") {
|
||||
const fn = argument.expression.identifier?.value;
|
||||
for (const test of tests) {
|
||||
if (test.skip && test.skip.includes(fn)) {
|
||||
item.callee.property.value = "test.ignore";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"bun-types": "canary"
|
||||
},
|
||||
"dependencies": {
|
||||
"@swc/core": "^1.3.38",
|
||||
"bktree-fast": "^0.0.7",
|
||||
"body-parser": "^1.20.2",
|
||||
"esbuild": "^0.17.11",
|
||||
|
||||
Reference in New Issue
Block a user