From 689ea29afc7b9bfe92238a45f5a5589b2848b554 Mon Sep 17 00:00:00 2001 From: "shigeru.nakajima" Date: Sat, 15 Aug 2026 22:05:42 +0900 Subject: [PATCH] Test the Node.js example --- CONTRIBUTING.md | 28 ++++++++++++- .../ruby-head-wasm-wasi/package.json | 2 +- .../npm-packages/ruby-wasm-wasi/package.json | 1 + .../test-node-examples/examples.test.js | 22 ++++++++++ .../test-node-examples/support.js | 41 +++++++++++++++++++ 5 files changed, 91 insertions(+), 3 deletions(-) create mode 100644 packages/npm-packages/ruby-wasm-wasi/test-node-examples/examples.test.js create mode 100644 packages/npm-packages/ruby-wasm-wasi/test-node-examples/support.js diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 87859d867..6ceaabdcc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -23,14 +23,38 @@ $ rake build:download_prebuilt # Build Ruby (if you need to build Ruby by yourself) $ rake build:head-wasm32-unknown-wasip1-full -# Build npm package +# Build and test the WASI Preview 1 npm package +$ rake npm:ruby-head-wasm-wasi:build +$ rake npm:ruby-head-wasm-wasi:check + +# Build and test the WASI Preview 2 npm package $ rake npm:ruby-head-wasm-wasip2:build -# Test npm package $ rake npm:ruby-head-wasm-wasip2:check ``` If you need to re-build Ruby, please clean `./rubies` directory, and run `rake npm:ruby-head-wasm-wasi` again. +### npm package test structure + +The `ruby-wasm-wasi` package contains the shared JavaScript bindings and test suites, but it does not contain a Ruby WebAssembly binary. Version-specific packages provide the binary and invoke the shared tests with `RUBY_NPM_PACKAGE_ROOT`: + +```text +ruby-head-wasm-wasi +└─ test + ├─ ruby-wasm-wasi:test:run + └─ ruby-wasm-wasi:test:node-examples + +ruby-4.0-wasm-wasi, ruby-3.x-wasm-wasi +└─ test + └─ ruby-wasm-wasi:test:run + +ruby-head-wasm-wasip2 +└─ test + └─ ruby-wasm-wasi:test:run +``` + +The shared `test:run` script runs the unit, Vitest, and browser E2E suites. The `test:node-examples` script is invoked separately and only by `ruby-head-wasm-wasi` because the Node.js examples use `@ruby/head-wasm-wasi`. + ## Building CRuby from source If you want to build CRuby for WebAssembly from source yourself, follow the below instructions. diff --git a/packages/npm-packages/ruby-head-wasm-wasi/package.json b/packages/npm-packages/ruby-head-wasm-wasi/package.json index bedb2ea30..ad5a8fede 100644 --- a/packages/npm-packages/ruby-head-wasm-wasi/package.json +++ b/packages/npm-packages/ruby-head-wasm-wasi/package.json @@ -29,7 +29,7 @@ "README.md" ], "scripts": { - "test": "RUBY_NPM_PACKAGE_ROOT=../ruby-head-wasm-wasi npm -C ../ruby-wasm-wasi run test:run", + "test": "RUBY_NPM_PACKAGE_ROOT=../ruby-head-wasm-wasi npm -C ../ruby-wasm-wasi run test:run && RUBY_NPM_PACKAGE_ROOT=../ruby-head-wasm-wasi npm -C ../ruby-wasm-wasi run test:node-examples", "build:deps": "cd ../ruby-wasm-wasi && npm run build", "build:static:files": "../ruby-wasm-wasi/tools/pack-static-files.sh ./dist", "build:static:compat": "../ruby-wasm-wasi/tools/pack-compat-shim.mjs --dist=./dist --pkg=ruby-head-wasm-wasi", diff --git a/packages/npm-packages/ruby-wasm-wasi/package.json b/packages/npm-packages/ruby-wasm-wasi/package.json index 6761506af..e87f868ac 100644 --- a/packages/npm-packages/ruby-wasm-wasi/package.json +++ b/packages/npm-packages/ruby-wasm-wasi/package.json @@ -42,6 +42,7 @@ "test:run": "npm run test:unit && npm run test:vitest -- --run && npm run test:e2e", "test:vitest": "vitest ./test/", "test:unit": "./tools/run-test-unit.mjs", + "test:node-examples": "vitest ./test-node-examples/ --run", "test:e2e": "playwright install && npm run test:e2e:examples && npm run test:e2e:integrations", "test:e2e:examples": "playwright test -c test-e2e/playwright.examples.config.ts", "test:e2e:integrations": "playwright test -c test-e2e/playwright.integrations.config.ts", diff --git a/packages/npm-packages/ruby-wasm-wasi/test-node-examples/examples.test.js b/packages/npm-packages/ruby-wasm-wasi/test-node-examples/examples.test.js new file mode 100644 index 000000000..a2a6a344f --- /dev/null +++ b/packages/npm-packages/ruby-wasm-wasi/test-node-examples/examples.test.js @@ -0,0 +1,22 @@ +import { afterEach, beforeEach, describe, expect, test } from "vitest"; +import { setupNodeExampleTest } from "./support.js"; + +describe("Node.js examples", () => { + let context; + + beforeEach(async () => { + context = await setupNodeExampleTest(); + }); + + afterEach(async () => { + await context?.cleanup(); + }); + + test("index.node.js is healthy", async () => { + const { stdout } = await context.run("index.node.js"); + + expect(stdout).toMatch(/ruby .* \[wasm32-wasi\]/); + expect(stdout).toContain("NoMethodError"); + expect(stdout).toContain("RuntimeError"); + }, 70_000); +}); diff --git a/packages/npm-packages/ruby-wasm-wasi/test-node-examples/support.js b/packages/npm-packages/ruby-wasm-wasi/test-node-examples/support.js new file mode 100644 index 000000000..96431e3e3 --- /dev/null +++ b/packages/npm-packages/ruby-wasm-wasi/test-node-examples/support.js @@ -0,0 +1,41 @@ +import { execFile } from "child_process"; +import fs from "fs/promises"; +import os from "os"; +import path from "path"; +import { promisify } from "util"; +import { fileURLToPath } from "url"; + +const execFileAsync = promisify(execFile); +const exampleDir = fileURLToPath(new URL("../example/", import.meta.url)); + +export const setupNodeExampleTest = async () => { + if (!process.env.RUBY_NPM_PACKAGE_ROOT) { + throw new Error("RUBY_NPM_PACKAGE_ROOT must be set"); + } + + const rubyPackageRoot = path.resolve(process.env.RUBY_NPM_PACKAGE_ROOT); + const temporaryDir = await fs.mkdtemp( + path.join(os.tmpdir(), "ruby-wasm-node-example-"), + ); + const packageLink = path.join( + temporaryDir, + "node_modules/@ruby/head-wasm-wasi", + ); + + try { + await fs.mkdir(path.dirname(packageLink), { recursive: true }); + await fs.symlink(rubyPackageRoot, packageLink, "dir"); + } catch (error) { + await fs.rm(temporaryDir, { recursive: true, force: true }); + throw error; + } + + return { + run: (exampleFile) => + execFileAsync(process.execPath, [path.join(exampleDir, exampleFile)], { + cwd: temporaryDir, + timeout: 60_000, + }), + cleanup: () => fs.rm(temporaryDir, { recursive: true, force: true }), + }; +};