npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mostajs/mjs-unit

v0.10.0

Published

Micro-framework de test pour l'écosystème @mostajs/* : zéro dépendance, API minimale (test/expect/run), runner CLI (preuves HTML/JSON/JUnit XML via --report/--json/--junit ; compte rendu de validation CRA lié à la SBOM, CTRF, in-toto test-result via --com

Readme

@mostajs/mjs-unit

Auteur : Dr Hamid MADANI [email protected] · Licence : AGPL-3.0-or-later

Micro-framework de test zéro dépendance pour l'écosystème @mostajs/*. Trois apports :

  1. API minimale : test(), describe(), expect() + assertions (ok, equal, deepEqual, throwsAsync, doesNotThrowAsync).
  2. Runner CLI (mjs-unit <fichiers>) : importe les fichiers de test, exécute, affiche un rapport, code de sortie ≠ 0 si échec.
  3. Resolver ESM : exécute sous Node les dist/ à imports sans extension (convention moduleResolution: bundler des modules @mostajs) — sans ce resolver, node échoue sur import './x'.

Installation

npm i -D @mostajs/mjs-unit
# module local non publié : npm i -D "@mostajs/mjs-unit@file:../chemin/mosta-mjs-unit"

How to use (pas à pas, dans n'importe quelle application)

  1. Installer le module en devDependency (voir ci-dessus).
  2. Créer un dossier test-scripts/ et y écrire des fichiers *.test.mjs (un test = test(name, fn) ; ne pas appeler run(), le runner s'en charge).
  3. Versionner un runner test-scripts/run-tests.sh qui (re)bâtit puis lance le CLI.
  4. Brancher package.json : "scripts": { "test": "bash test-scripts/run-tests.sh" }.
  5. Lancer : npm test (code de sortie ≠ 0 si un test échoue → utilisable en CI).
// test-scripts/api.test.mjs — test d'unité + test web, dans la même suite
import { test, equal, ok } from "@mostajs/mjs-unit";
import { createHttpTester, waitForEvent } from "@mostajs/mjs-unit/web";
import { app } from "../dist/app.js";            // votre app Express / handler natif

const api = createHttpTester(app);

test("GET /healthz → 200", async () => {
  const res = await api.get("/healthz");
  equal(res.status, 200);
  ok(res.body.status === "ok");
});
# test-scripts/run-tests.sh
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")/.."
npm run build >/dev/null
node node_modules/@mostajs/mjs-unit/bin/cli.mjs test-scripts/*.test.mjs

Besoin d'un profil d'environnement (DB de test, secrets…) avant d'importer le dist/ ? Fixez-le avant les imports via un import dynamique : process.env.APP_ENV = "TEST"; const { app } = await import("../dist/app.js"); (les import statiques sont hoistés, donc exécutés avant le code du module).

Écrire un test (test-scripts/mon.test.mjs)

import { test, expect, ok } from "@mostajs/mjs-unit";
import { maFonction } from "../dist/lib/ma-fonction.js"; // le dist peut avoir des imports sans extension

test("addition", () => expect(maFonction(2, 2)).toBe(4));
test("async", async () => { ok(await maFonction.async()); });

Ne pas appeler run() dans le fichier : le runner s'en charge (les cas enregistrés via test() sont exécutés groupés par fichier).

Depuis 0.7.0, le runner relève ce qu'il a exécuté : par fichier, cas enregistrés contre cas exécutés, et il imprime le compte (✓ 49/49 test(s) exécutés sur 7 fichier(s)). Un fichier sans aucun test fait échouer la suite ; un fichier qui appelle run() lui-même reste au rapport ; et node fichier.test.mjs dont les cas ne sont jamais exécutés sort en code 1, avec un message — l'absence de résultat n'est pas un succès.

Lancer

mjs-unit test-scripts/*.test.mjs          # via le bin
# ou, programmatique :
node --import @mostajs/mjs-unit/register test-scripts/mon.test.mjs   # + appeler run() soi-même

Runner versionné conseillé (test-scripts/run-tests.sh) :

#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")/.."
npm run build >/dev/null
node node_modules/@mostajs/mjs-unit/bin/cli.mjs test-scripts/*.test.mjs

Rapport JUnit XML (0.8.0)

mjs-unit test-scripts/*.test.mjs --junit=test-scripts/.out/junit.xml --timeout=10000

Le fichier suit le schéma lu par Jenkins, GitLab (artifacts:reports:junit), GitHub et QATrax : une <testsuite> par fichier, un <testcase name classname file time> par cas, <failure> pour une assertion tombée, <error> pour une exception inattendue ou un délai dépassé, <skipped message> pour un cas ignoré, les étiquettes en <property name="tag">. --tag=securite ne joue que les cas étiquetés.

Compte rendu de validation — cycle CRA (0.9.0)

npx mjs-sbom                                            # la SBOM de la version publiée (@mostajs/mjs-sbom)
npx mjs-unit test-scripts/**/*.test.mjs \
  --sbom=sbom.cyclonedx.json --plan=docs/DEVTEST-PLAN.<projet>.json \
  --compte-rendu --ctrf --intoto
# 📄 Compte rendu : test-scripts/.out/compte-rendu.json — @acme/[email protected] — verdict : conforme
npx mjs-unit-menu                                       # ou le menu : essais, SBOM, compte rendu, envoi à qatrax.dev

Le compte rendu lie les résultats à la version (lue dans la SBOM), au plan et aux scripts (empreintes) ; son verdict est conforme, non conforme ou incomplet (avec les défauts) ; il s'écrit toujours. qatrax.dev l'enregistre, le relie à l'analyse des vulnérabilités de la même SBOM, et scelle le dossier.

Java, C#, Python, Go, C++… — le mode lecteur (0.10.0)

Vos essais restent joués par l'outil de votre langage ; mjs-unit lit leur rapport et produit le même compte rendu scellé (lié à la SBOM et au plan), avec CTRF et in-toto. Node ≥ 18 suffit dans la CI ; aucun JavaScript à écrire.

Formats lus tels quels, reconnus à leur contenu : JUnit XML · NUnit 2 et 3 · TRX (VSTest) · TestNG natif · Robot Framework (output.xml) · CTest (Test.xml) · Cucumber JSON · Behave JSON · Allure 2 (allure-results) · CTRF · TAP · JSON mjs-unit. Votre rapport natif suffit : aucune conversion.

| Langage | Rapport | Commande | |---|---|---| | Java (Maven) | target/surefire-reports/TEST-*.xml | npx @mostajs/mjs-unit --depuis=target/surefire-reports --sbom=target/bom.json --plan=plan.json --compte-rendu | | Java (Gradle) | build/test-results/test/TEST-*.xml | npx @mostajs/mjs-unit --depuis=build/test-results/test … | | C# (.NET) | dotnet test --logger "junit;LogFilePath=resultats.xml" | npx @mostajs/mjs-unit --depuis=resultats.xml … | | Python | pytest --junitxml=resultats.xml | npx @mostajs/mjs-unit --depuis=resultats.xml … | | Go | go test -v ./... 2>&1 \| go-junit-report > resultats.xml | npx @mostajs/mjs-unit --depuis=resultats.xml … | | C# (NUnit) | TestResult.xml (nunit3-console) | npx @mostajs/mjs-unit --depuis=TestResult.xml … | | C# (TRX) | dotnet test --logger trx → TestResults/*.trx | npx @mostajs/mjs-unit --depuis=TestResults … | | Java (TestNG) | target/surefire-reports/testng-results.xml | npx @mostajs/mjs-unit --depuis=target/surefire-reports/testng-results.xml … | | Robot Framework | output.xml | npx @mostajs/mjs-unit --depuis=output.xml … | | C/C++ (CTest) | ctest -T Test → Testing/<date>/Test.xml | npx @mostajs/mjs-unit --depuis Testing/*/Test.xml … | | BDD (Cucumber) | --format json:cucumber.json (JS, JVM, Ruby) | npx @mostajs/mjs-unit --depuis=cucumber.json … | | BDD (Behave) | behave -f json -o behave.json | npx @mostajs/mjs-unit --depuis=behave.json … | | Allure (tout langage) | allure-results/ | npx @mostajs/mjs-unit --depuis=allure-results … (conteneurs et pièces jointes écartés ; un essai rejoué : le dernier fait foi) |

Nommez vos essais d'après le plan : test_T_MOD_01_… est rattaché à T-MOD-01. En BDD, Robot ou Allure, une étiquette égale à la référence suffit (@T-MOD-01). Avec le menu : MJS_DEPUIS=target/surefire-reports npx mjs-unit-menu --choix=3, puis --choix=4 (plan, SBOM, compte rendu vers qatrax.dev). Les lecteurs sont exportés : import { lireRapportEssais, refDuPlan } from "@mostajs/mjs-unit/lecteurs".

Ce que le sceau prouve : le rapport n'a pas changé après sa lecture, avec cette SBOM et ce plan. Il ne prouve pas que l'outil du langage a joué les essais honnêtement.

API

| Fonction | Rôle | |---|---| | test(name, fn, { timeout?, tags? }) | Enregistre un cas (sync ou async) ; délai et étiquettes à la JUnit | | test.skip(name, fn?, raison?) | Cas ignoré, raison rapportée (@Disabled) | | test.each(table)(name, fn) | Un cas par ligne ; %s, %i, $cle dans le nom (@ParameterizedTest) | | describe(group, body) | Groupe : préfixe les noms, et devient le classname JUnit | | beforeAll/afterAll/beforeEach/afterEach(fn) | Crochets, au niveau du fichier ou d'un describe | | expect(v).toBe/toEqual/toBeTruthy/toBeFalsy/toContain | Assertions fluides | | ok/equal/deepEqual(...) | Assertions directes | | throwsAsync/doesNotThrowAsync(fn) | Vérifie qu'une promesse (ne) lève (pas) | | run(label?) | Exécute les cas en attente → {passed, failed, skipped} (appelé par le CLI) |

Zéro dépendance runtime ; @types/node en dev uniquement.

Tests web (@mostajs/mjs-unit/web)

Moteur générique pour tester des applications HTTP et événementielles — sans supertest, jest ni client socket dans le framework. Toujours zéro dépendance (uniquement node:http/node:net).

| Fonction | Rôle | |---|---| | startServer(target) | Démarre un serveur HTTP éphémère (port 0) autour d'un http.Server, d'un RequestListener ou d'une app Express → { url, port, close() } | | createHttpTester(target) | Client HTTP façon supertest : get/post/put/del/request → { status, headers, body }. target = URL d'un serveur démarré ou handler/serveur (éphémère par requête) | | waitForEvent(emitter, name, { timeout }) | Attend un événement sur tout émetteur (EventEmitter, client Socket.io, ws…) — teste le temps réel |

import { test, equal, ok } from "@mostajs/mjs-unit";
import { createHttpTester, startServer, waitForEvent } from "@mostajs/mjs-unit/web";

// HTTP : éphémère par requête (app Express ou handler natif (req,res)=>…)
const api = createHttpTester(app);
test("login", async () => {
  const res = await api.post("/login", { username: "admin", password: "admin" });
  equal(res.status, 200);
  ok(res.body.token);
});

// Temps réel : serveur durable + attente d'événement (ex. Socket.io)
test("push", async () => {
  const srv = await startServer(httpServer);          // serveur partagé
  const client = ioClient(srv.url);                   // socket.io-client fourni par l'app
  const received = waitForEvent(client, "new_ticket");
  await createHttpTester(srv.url).post("/api/tickets", { serviceType: "general" });
  const evt = await received;
  ok(evt.ticket);
  client.close();
  await srv.close();
});

Le client socket (socket.io-client, ws…) reste fourni par l'application : mjs-unit ne l'embarque pas, pour préserver le zéro-dépendance. waitForEvent fonctionne avec n'importe quel émetteur exposant on (idéalement once/off).