test: refactor the example tests to esm, add bun:test => ava polyfill (#1730)

This commit is contained in:
LongYinan 2023-09-20 01:18:01 -07:00 committed by GitHub
parent ebc48cf6c4
commit 2e03db1fec
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 234 additions and 64 deletions

View file

@ -17,6 +17,7 @@ jobs:
build_and_test:
name: Memory leak detect job
runs-on: ubuntu-latest
timeout-minutes: 40
steps:
- uses: actions/checkout@v4

View file

@ -400,7 +400,7 @@ jobs:
- name: Install ziglang
uses: goto-bus-stop/setup-zig@v2
with:
version: 0.10.1
version: 0.11.0
- name: Cache cargo
uses: actions/cache@v3
@ -486,6 +486,43 @@ jobs:
- name: Check build
run: cargo check -p ${{ matrix.settings.package }} -F ${{ matrix.settings.features }}
test-latest-bun:
runs-on: ubuntu-latest
name: Test latest bun
timeout-minutes: 10
continue-on-error: true
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v1
with:
bun-version: latest
- name: Setup node
uses: actions/setup-node@v3
with:
node-version: 18
cache: 'yarn'
- name: Install
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
targets: x86_64-unknown-linux-gnu
- name: Cache cargo
uses: actions/cache@v3
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: stable-x86_64-unknown-linux-gnu-node@18-cargo-cache
- name: Install dependencies
run: yarn install --immutable --mode=skip-build
- name: Build
run: |
bun run build
bun run build:test
- name: Test
run: bun run test:bun
release-npm:
runs-on: ubuntu-latest
needs:

View file

@ -1,6 +0,0 @@
export default {
extensions: {
ts: 'module',
},
files: ['**/__tests__/**/*.spec.ts'],
}

View file

@ -95,5 +95,13 @@
"build": "tsc && yarn build:cjs",
"build:cjs": "node ./esbuild.mjs",
"test": "node --loader ts-node/esm/transpile-only ../node_modules/ava/entrypoints/cli.mjs"
},
"ava": {
"extensions": {
"ts": "module"
},
"files": [
"**/__tests__/**/*.spec.ts"
]
}
}

View file

@ -2,7 +2,7 @@
"compilerOptions": {
"strict": true,
"target": "ES2022",
"module": "ESNext",
"module": "NodeNext",
"moduleResolution": "nodenext",
"resolveJsonModule": true,
"allowSyntheticDefaultImports": true,

View file

@ -12,6 +12,8 @@ Generated by [AVA](https://avajs.dev).
'@napi-rs/cli',
'@types/lodash',
'ava',
'cross-env',
'electron',
'lodash',
'sinon',
]

View file

@ -0,0 +1,3 @@
import { test, expect } from 'bun:test'
export { test, expect }

View file

@ -1,6 +1,10 @@
import { createRequire } from 'node:module'
import test from 'ava'
import { receiveString } from '..'
const require = createRequire(import.meta.url)
const { receiveString }: typeof import('../index.js') = require('../index.node')
test('Function message', (t) => {
// @ts-expect-error

View file

@ -1,6 +1,14 @@
import { createRequire } from 'node:module'
import test from 'ava'
import { Fib, Fib2, Fib3 } from '..'
const require = createRequire(import.meta.url)
const {
Fib,
Fib2,
Fib3,
}: typeof import('../index.js') = require('../index.node')
for (const [index, factory] of [
() => new Fib(),

View file

@ -1,6 +1,12 @@
import { createRequire } from 'node:module'
import test from 'ava'
import { NotWritableClass } from '..'
const require = createRequire(import.meta.url)
const {
NotWritableClass,
}: typeof import('../index.js') = require('../index.node')
test('Not Writable Class', (t) => {
const obj = new NotWritableClass('1')

View file

@ -1,6 +1,10 @@
import { createRequire } from 'node:module'
import test from 'ava'
import {
const require = createRequire(import.meta.url)
const {
validateArray,
validateTypedArray,
validateBigint,
@ -20,7 +24,7 @@ import {
returnUndefinedIfInvalid,
returnUndefinedIfInvalidPromise,
validateOptional,
} from '..'
}: typeof import('../index.d.ts') = require('../index.node')
test('should validate array', (t) => {
t.is(validateArray([1, 2, 3]), 3)

View file

@ -0,0 +1,67 @@
const { bun } = process.versions
/**@type {import('ava').TestFn} */
let testRunner
if (bun) {
const { test, expect } = await import('./bun-test.js')
const testContext = {
is: (actual, expected) => {
expect(actual).toEqual(expected)
},
deepEqual: (actual, expected) => {
expect(actual).toEqual(expected)
},
throws: (fn, expected) => {
if (expected) {
expect(fn).toThrow(expected)
} else {
expect(fn).toThrow()
}
},
notThrows: (fn, expected) => {
if (expected) {
expect(fn).not.toThrow(expected)
} else {
expect(fn).not.toThrow()
}
},
throwsAsync: async (fn, expected) => {
if (expected) {
expect(fn instanceof Promise ? fn : await fn()).rejects.toEqual(
expected,
)
} else {
expect(fn instanceof Promise ? fn : await fn()).rejects.toBeTruthy()
}
},
notThrowsAsync: async (fn, expected) => {
if (expected) {
expect(fn instanceof Promise ? fn : await fn()).resolves.toBe(expected)
} else {
expect(fn instanceof Promise ? fn : await fn()).resolves.toBeTruthy()
}
},
true: (actual, message) => {
expect(actual).toBe(true, message)
},
false: (actual, message) => {
expect(actual).toBe(false, message)
},
}
testRunner = (title, spec) => {
test(title, async () => {
await Promise.resolve(spec(testContext))
})
}
testRunner.skip = (label, fn) => {
test.skip(label, () => {
fn(testContext)
})
}
} else {
const test = (await import('ava')).default
testRunner = test
}
export { testRunner as test }

View file

@ -1,8 +1,11 @@
import { readFileSync } from 'fs'
import { join } from 'path'
import { readFileSync } from 'node:fs'
import { join } from 'node:path'
import { fileURLToPath } from 'node:url'
import test from 'ava'
const __dirname = join(fileURLToPath(import.meta.url), '..')
test('should generate correct type def file', (t) => {
t.snapshot(readFileSync(join(__dirname, '..', 'index.d.ts'), 'utf8'))
})

View file

@ -1,8 +1,12 @@
// use the commonjs syntax to prevent compiler from transpiling the module syntax
const path = require('path')
import { createRequire } from 'node:module'
import * as path from 'node:path'
const test = require('ava').default
import test from 'ava'
const require = createRequire(import.meta.url)
const __dirname = path.dirname(new URL(import.meta.url).pathname)
test('unload module', (t) => {
const { add } = require('../index.node')

View file

@ -1,10 +1,18 @@
import { exec } from 'child_process'
import { join } from 'path'
import { exec } from 'node:child_process'
import { createRequire } from 'node:module'
import { join } from 'node:path'
import { fileURLToPath } from 'node:url'
import test from 'ava'
import { spy } from 'sinon'
import {
import type { AliasedStruct } from '../index.js'
import { test } from './test.framework.js'
const require = createRequire(import.meta.url)
const __dirname = join(fileURLToPath(import.meta.url), '..')
const {
DEFAULT_COST,
add,
fibonacci,
@ -78,7 +86,6 @@ import {
receiveAllOptionalObject,
fnReceivedAliased,
ALIAS,
AliasedStruct,
appendBuffer,
returnNull,
returnUndefined,
@ -134,7 +141,7 @@ import {
chronoNativeDateTime,
chronoNativeDateTimeReturn,
throwAsyncError,
} from '..'
}: typeof import('../index.d.ts') = require('../index.node')
test('export const', (t) => {
t.is(DEFAULT_COST, 12)
@ -251,7 +258,7 @@ test('class factory', (t) => {
const error = t.throws(() => new ClassWithFactory())
t.true(
error!.message.startsWith(
error?.message.startsWith(
'Class contains no `constructor`, can not new it!',
),
)
@ -463,7 +470,7 @@ test('should throw if object type is not matched', (t) => {
// @ts-expect-error
const err1 = t.throws(() => receiveStrictObject({ name: 1 }))
t.is(
err1!.message,
err1?.message,
'Failed to convert JavaScript value `Number 1 ` into rust type `String`',
)
// @ts-expect-error
@ -472,7 +479,7 @@ test('should throw if object type is not matched', (t) => {
})
test('aliased rust struct and enum', (t) => {
const a: ALIAS = ALIAS.A
const a = ALIAS.A
const b: AliasedStruct = {
a,
b: 1,
@ -506,7 +513,7 @@ test('serde-roundtrip', (t) => {
t.is(testSerdeRoundtrip(null), null)
let err = t.throws(() => testSerdeRoundtrip(undefined))
t.is(err!.message, 'undefined cannot be represented as a serde_json::Value')
t.is(err?.message, 'undefined cannot be represented as a serde_json::Value')
err = t.throws(() => testSerdeRoundtrip(() => {}))
t.is(
@ -681,7 +688,7 @@ test('external', (t) => {
// @ts-expect-error
const e = t.throws(() => getExternal(ext2))
t.is(
e!.message,
e?.message,
'T on `get_value_external` is not the type of wrapped object',
)
})
@ -783,7 +790,7 @@ Napi4Test('throw error from thread safe function', async (t) => {
threadsafeFunctionThrowError(reject)
})
const err = await t.throwsAsync(throwPromise)
t.is(err!.message, 'ThrowFromNative')
t.is(err?.message, 'ThrowFromNative')
})
Napi4Test('thread safe function closure capture data', (t) => {
@ -803,7 +810,7 @@ Napi4Test('resolve value from thread safe function fatal mode', async (t) => {
})
Napi4Test('throw error from thread safe function fatal mode', (t) => {
const p = exec('node ./tsfn-error.js', {
const p = exec('node ./tsfn-error.cjs', {
cwd: __dirname,
})
let stderr = Buffer.from([])

View file

@ -1,9 +1,9 @@
import { join } from 'path'
import { Worker } from 'worker_threads'
const { join } = require('node:path')
const { Worker } = require('node:worker_threads')
import test from 'ava'
const test = require('ava').default
import { Animal, Kind, DEFAULT_COST } from '..'
const { Animal, Kind, DEFAULT_COST } = require('../index.node')
// aarch64-unknown-linux-gnu is extremely slow in CI, skip it or it will timeout
const t =
@ -12,8 +12,10 @@ const t =
t('should be able to require in worker thread', async (t) => {
await Promise.all(
Array.from({ length: 100 }).map(() => {
const w = new Worker(join(__dirname, 'worker.js'))
return new Promise<void>((resolve, reject) => {
const w = new Worker(join(__dirname, 'worker.cjs'), {
execArgv: []
})
return new Promise((resolve, reject) => {
w.postMessage({ type: 'require' })
w.on('message', (msg) => {
t.is(msg, Animal.withKind(Kind.Cat).whoami() + DEFAULT_COST)
@ -35,8 +37,10 @@ t('custom GC works on worker_threads', async (t) => {
await Promise.all(
Array.from({ length: 50 }).map(() =>
Promise.all([
new Promise<Worker>((resolve, reject) => {
const w = new Worker(join(__dirname, 'worker.js'))
new Promise((resolve, reject) => {
const w = new Worker(join(__dirname, 'worker.cjs'), {
execArgv: []
})
w.postMessage({
type: 'async:buffer',
})
@ -50,8 +54,10 @@ t('custom GC works on worker_threads', async (t) => {
}).then((w) => {
return w.terminate()
}),
new Promise<Worker>((resolve, reject) => {
const w = new Worker(join(__dirname, 'worker.js'))
new Promise((resolve, reject) => {
const w = new Worker(join(__dirname, 'worker.cjs'), {
execArgv: []
})
w.postMessage({
type: 'async:arraybuffer',
})
@ -73,8 +79,10 @@ t('custom GC works on worker_threads', async (t) => {
t('should be able to new Class in worker thread concurrently', async (t) => {
await Promise.all(
Array.from({ length: 100 }).map(() => {
const w = new Worker(join(__dirname, 'worker.js'))
return new Promise<void>((resolve, reject) => {
const w = new Worker(join(__dirname, 'worker.cjs'), {
execArgv: []
})
return new Promise((resolve, reject) => {
w.postMessage({ type: 'constructor' })
w.on('message', (msg) => {
t.is(msg, 'Ellie')

View file

@ -2,35 +2,34 @@
"name": "@examples/napi",
"private": true,
"version": "0.0.0",
"type": "module",
"main": "./index.node",
"types": "./index.d.ts",
"scripts": {
"build": "napi-raw build --no-js",
"test": "ava"
"test": "cross-env TS_NODE_PROJECT=./tsconfig.json node --es-module-specifier-resolution=node --loader ts-node/esm/transpile-only ../../node_modules/ava/entrypoints/cli.mjs"
},
"devDependencies": {
"@napi-rs/cli": "workspace:*",
"@types/lodash": "^4.14.195",
"ava": "^5.3.1",
"cross-env": "7.0.3",
"electron": "^26.2.1",
"lodash": "^4.17.21",
"sinon": "^15.2.0"
},
"ava": {
"extensions": [
"ts",
"tsx",
"js"
],
"require": [
"ts-node/register/transpile-only"
],
"extensions": {
"ts": "module",
"cts": "commonjs",
"cjs": true
},
"files": [
"__tests__/**/*.spec.ts",
"__tests__/**/*.spec.js"
"__tests__/**/*.spec.cts",
"__tests__/**/*.spec.js",
"__tests__/**/*.spec.cjs"
],
"environmentVariables": {
"TS_NODE_PROJECT": "../tsconfig.json"
},
"timeout": "10m"
}
}

View file

@ -3,10 +3,13 @@
"include": ["."],
"compilerOptions": {
"outDir": "./dist",
"rootDir": "__tests__",
"target": "ES2018",
"rootDir": ".",
"target": "ESNext",
"module": "ESNext",
"skipLibCheck": false,
"noEmit": true
"noEmit": true,
"types": ["bun-types"],
"importHelpers": false
},
"exclude": ["dist", "electron.js", "electron-renderer"]
"exclude": ["dist", "electron.cjs", "electron-renderer", "node_modules"]
}

View file

@ -28,8 +28,9 @@
"format:toml": "taplo format",
"lint": "eslint -c .eslintrc.yml .",
"test": "lerna run test --concurrency=1 --ignore @napi-rs/cli",
"test:bun": "bun test examples/napi/__tests__/values.spec.ts",
"test:cli": "yarn workspace @napi-rs/cli test",
"test:electron": "electron examples/napi/electron.js",
"test:electron": "electron examples/napi/electron.cjs",
"test:macro": "cargo test -p napi-examples",
"test:memory": "node memory-testing/index.mjs",
"postinstall": "husky install",
@ -75,9 +76,10 @@
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
"ava": "^5.3.1",
"bun-types": "^1.0.2",
"c8": "^8.0.0",
"cross-env": "^7.0.3",
"electron": "26.2.1",
"electron": "^26.2.1",
"esbuild": "^0.19.0",
"eslint": "^8.45.0",
"eslint-config-prettier": "^9.0.0",

View file

@ -338,6 +338,8 @@ __metadata:
"@napi-rs/cli": "workspace:*"
"@types/lodash": ^4.14.195
ava: ^5.3.1
cross-env: 7.0.3
electron: ^26.2.1
lodash: ^4.17.21
sinon: ^15.2.0
languageName: unknown
@ -2309,6 +2311,13 @@ __metadata:
languageName: node
linkType: hard
"bun-types@npm:^1.0.2":
version: 1.0.2
resolution: "bun-types@npm:1.0.2"
checksum: 02f9b6b37a7c3d6199e9cb990a635f374967a4a9708e525ca5f895268ced52e3bdb90d3252f3fd90d85287fd75a07b0ecbc5bcb9e003cba63c10aaa99ac9e070
languageName: node
linkType: hard
"byte-size@npm:8.1.1":
version: 8.1.1
resolution: "byte-size@npm:8.1.1"
@ -2983,7 +2992,7 @@ __metadata:
languageName: node
linkType: hard
"cross-env@npm:^7.0.3":
"cross-env@npm:7.0.3, cross-env@npm:^7.0.3":
version: 7.0.3
resolution: "cross-env@npm:7.0.3"
dependencies:
@ -3306,7 +3315,7 @@ __metadata:
languageName: node
linkType: hard
"electron@npm:26.2.1":
"electron@npm:^26.2.1":
version: 26.2.1
resolution: "electron@npm:26.2.1"
dependencies:
@ -6493,9 +6502,10 @@ __metadata:
"@typescript-eslint/eslint-plugin": ^6.0.0
"@typescript-eslint/parser": ^6.0.0
ava: ^5.3.1
bun-types: ^1.0.2
c8: ^8.0.0
cross-env: ^7.0.3
electron: 26.2.1
electron: ^26.2.1
esbuild: ^0.19.0
eslint: ^8.45.0
eslint-config-prettier: ^9.0.0