@ripplo/testing 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -20
- package/dist/{chunk-X2FROZPN.js → chunk-GTHRSFXF.js} +1 -0
- package/dist/compiler.d.ts +1 -0
- package/dist/compiler.js +1 -1
- package/dist/express.d.ts +2 -1
- package/dist/express.js +5 -2
- package/dist/fastify.d.ts +2 -1
- package/dist/fastify.js +5 -0
- package/dist/index.js +5 -2
- package/dist/nextjs.d.ts +2 -1
- package/dist/nextjs.js +4 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,8 +11,8 @@ npm install @ripplo/testing
|
|
|
11
11
|
## Quick Start
|
|
12
12
|
|
|
13
13
|
1. Run `npx ripplo` to authenticate and scaffold a `.ripplo/` directory in your project
|
|
14
|
-
2. Define preconditions in `.ripplo/preconditions.ts
|
|
15
|
-
3. Write tests in `.ripplo/tests
|
|
14
|
+
2. Define preconditions in `.ripplo/preconditions/`, then add `import "./preconditions/<file>.js";` to `.ripplo/index.ts`. Exports set up test data (users, projects, etc.)
|
|
15
|
+
3. Write tests in `.ripplo/tests/`, then add `import "./tests/<id>.js";` to `.ripplo/index.ts`. The CLI only loads what that file imports.
|
|
16
16
|
4. Run `npx ripplo lint` to validate, `npx ripplo run` to execute
|
|
17
17
|
|
|
18
18
|
Every test gets a clean slate via preconditions — no shared state, no ordering dependencies, fully parallelizable.
|
|
@@ -23,7 +23,7 @@ Every test gets a clean slate via preconditions — no shared state, no ordering
|
|
|
23
23
|
|
|
24
24
|
```typescript
|
|
25
25
|
import ripplo from "../ripplo.js";
|
|
26
|
-
import { dataProject } from "../preconditions.js";
|
|
26
|
+
import { dataProject } from "../preconditions/index.js";
|
|
27
27
|
|
|
28
28
|
ripplo
|
|
29
29
|
.test("delete-project")
|
|
@@ -221,25 +221,27 @@ ripplo.implement(authLoggedIn, {
|
|
|
221
221
|
|
|
222
222
|
```bash
|
|
223
223
|
ripplo # Launch interactive dashboard
|
|
224
|
-
ripplo lint [
|
|
225
|
-
ripplo run [
|
|
226
|
-
ripplo
|
|
227
|
-
ripplo flake-detect <slug> --runs=10 # Run N times in parallel to detect flakiness
|
|
224
|
+
ripplo lint [ids..] # Compile + lint tests (all or specific ids)
|
|
225
|
+
ripplo run [ids..] # Run tests in parallel
|
|
226
|
+
ripplo flake-detect <id> --runs=10 # Run N times in parallel to detect flakiness
|
|
228
227
|
```
|
|
229
228
|
|
|
230
229
|
## Server Setup
|
|
231
230
|
|
|
232
|
-
Your application server must expose the precondition endpoints under a single path prefix (the value you pass to `createRipplo({ preconditionsUrl })`). Pick the adapter that matches your framework — each handles webhook signature verification, cookie forwarding, and request parsing for you.
|
|
231
|
+
Your application server must expose the precondition endpoints under a single path prefix (the value you pass to `createRipplo({ preconditionsUrl })`). Pick the adapter that matches your framework — each handles webhook signature verification, cookie forwarding, and request parsing for you. Every adapter takes a required `enabled: boolean` flag — bind it to an env var (e.g. `process.env.ENABLE_RIPPLO_TESTING === "true"`) so the endpoints never ship to production. When `enabled` is false the adapter mounts a no-op handler.
|
|
233
232
|
|
|
234
233
|
### Express
|
|
235
234
|
|
|
236
235
|
```ts
|
|
237
236
|
import express from "express";
|
|
238
237
|
import { createExpressHandler } from "@ripplo/testing/express";
|
|
239
|
-
import ripplo from "
|
|
238
|
+
import ripplo from "<path to .ripplo/ripplo>"; // import the existing instance — never call createRipplo() outside .ripplo/ripplo.ts
|
|
240
239
|
|
|
241
240
|
const app = express();
|
|
242
|
-
app.use(
|
|
241
|
+
app.use(
|
|
242
|
+
"/ripplo/preconditions",
|
|
243
|
+
createExpressHandler({ enabled: process.env.ENABLE_RIPPLO_TESTING === "true", ripplo }),
|
|
244
|
+
);
|
|
243
245
|
```
|
|
244
246
|
|
|
245
247
|
Mounts both `PUT /execute-batch` and `PUT /teardown` under the prefix you choose.
|
|
@@ -249,12 +251,13 @@ Mounts both `PUT /execute-batch` and `PUT /teardown` under the prefix you choose
|
|
|
249
251
|
```ts
|
|
250
252
|
import Fastify from "fastify";
|
|
251
253
|
import { registerFastifyHandler } from "@ripplo/testing/fastify";
|
|
252
|
-
import ripplo from "
|
|
254
|
+
import ripplo from "<path to .ripplo/ripplo>"; // import the existing instance — never call createRipplo() outside .ripplo/ripplo.ts
|
|
253
255
|
|
|
254
256
|
const app = Fastify();
|
|
255
|
-
await app.register(
|
|
256
|
-
|
|
257
|
-
}
|
|
257
|
+
await app.register(
|
|
258
|
+
registerFastifyHandler({ enabled: process.env.ENABLE_RIPPLO_TESTING === "true", ripplo }),
|
|
259
|
+
{ prefix: "/ripplo/preconditions" },
|
|
260
|
+
);
|
|
258
261
|
```
|
|
259
262
|
|
|
260
263
|
### Next.js (App Router)
|
|
@@ -262,11 +265,14 @@ await app.register(registerFastifyHandler({ ripplo }), {
|
|
|
262
265
|
The Next.js adapter exports a single catch-all handler. Create one dynamic route file:
|
|
263
266
|
|
|
264
267
|
```ts
|
|
265
|
-
// app/
|
|
268
|
+
// app/ripplo/preconditions/[action]/route.ts
|
|
266
269
|
import { createNextHandler } from "@ripplo/testing/nextjs";
|
|
267
270
|
import ripplo from "@/.ripplo/ripplo";
|
|
268
271
|
|
|
269
|
-
export const PUT = createNextHandler({
|
|
272
|
+
export const PUT = createNextHandler({
|
|
273
|
+
enabled: process.env.ENABLE_RIPPLO_TESTING === "true",
|
|
274
|
+
ripplo,
|
|
275
|
+
});
|
|
270
276
|
```
|
|
271
277
|
|
|
272
278
|
The handler dispatches on the last URL segment (`execute-batch` or `teardown`) and returns 404 for anything else. It depends only on the Web `Request` / `Response` types, so it runs on both the Node and Edge runtimes — no `next` import required.
|
|
@@ -282,12 +288,12 @@ import {
|
|
|
282
288
|
serializeCookie,
|
|
283
289
|
verifyWebhookSignature,
|
|
284
290
|
} from "@ripplo/testing";
|
|
285
|
-
import ripplo from "
|
|
291
|
+
import ripplo from "<path to .ripplo/ripplo>"; // import the existing instance — never call createRipplo() outside .ripplo/ripplo.ts
|
|
286
292
|
|
|
287
293
|
const engine = createEngine(ripplo);
|
|
288
294
|
const webhookSecret = ripplo.getConfig().webhookSecret;
|
|
289
295
|
|
|
290
|
-
// PUT /
|
|
296
|
+
// PUT /ripplo/preconditions/execute-batch
|
|
291
297
|
async function executeBatch(req: Request): Promise<Response> {
|
|
292
298
|
const body = await req.text();
|
|
293
299
|
const verified = verifyWebhookSignature(
|
|
@@ -314,7 +320,7 @@ async function executeBatch(req: Request): Promise<Response> {
|
|
|
314
320
|
return new Response(JSON.stringify(result), { headers });
|
|
315
321
|
}
|
|
316
322
|
|
|
317
|
-
// PUT /
|
|
323
|
+
// PUT /ripplo/preconditions/teardown
|
|
318
324
|
async function teardown(req: Request): Promise<Response> {
|
|
319
325
|
// ... same verify pattern, then:
|
|
320
326
|
// await engine.teardown(parsed.preconditions, parsed.data);
|
|
@@ -338,7 +344,7 @@ import { createRipplo } from "@ripplo/testing";
|
|
|
338
344
|
|
|
339
345
|
export default createRipplo({
|
|
340
346
|
appUrl: process.env.APP_URL,
|
|
341
|
-
preconditionsUrl: `${process.env.APP_URL}/
|
|
347
|
+
preconditionsUrl: `${process.env.APP_URL}/ripplo/preconditions`,
|
|
342
348
|
projectId: "...",
|
|
343
349
|
webhookSecret: process.env.RIPPLO_WEBHOOK_SECRET,
|
|
344
350
|
});
|
package/dist/compiler.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ interface CompiledTest {
|
|
|
12
12
|
readonly additionalChecks: ReadonlyArray<string>;
|
|
13
13
|
readonly description: string;
|
|
14
14
|
readonly expectedOutcome: string;
|
|
15
|
+
readonly implemented: boolean;
|
|
15
16
|
readonly name: string;
|
|
16
17
|
readonly slug: string;
|
|
17
18
|
readonly spec: WorkflowSpec;
|
package/dist/compiler.js
CHANGED
package/dist/express.d.ts
CHANGED
|
@@ -5,8 +5,9 @@ import './step-DLfkKI3V.js';
|
|
|
5
5
|
import '@ripplo/spec';
|
|
6
6
|
|
|
7
7
|
interface CreateExpressHandlerParams {
|
|
8
|
+
readonly enabled: boolean;
|
|
8
9
|
readonly ripplo: RipploBuilder;
|
|
9
10
|
}
|
|
10
|
-
declare function createExpressHandler({ ripplo }: CreateExpressHandlerParams): Router;
|
|
11
|
+
declare function createExpressHandler({ enabled, ripplo }: CreateExpressHandlerParams): Router;
|
|
11
12
|
|
|
12
13
|
export { type CreateExpressHandlerParams, createExpressHandler };
|
package/dist/express.js
CHANGED
|
@@ -8,10 +8,13 @@ import {
|
|
|
8
8
|
|
|
9
9
|
// src/adapters/express.ts
|
|
10
10
|
import { Router, json } from "express";
|
|
11
|
-
function createExpressHandler({ ripplo }) {
|
|
11
|
+
function createExpressHandler({ enabled, ripplo }) {
|
|
12
|
+
const router = Router();
|
|
13
|
+
if (!enabled) {
|
|
14
|
+
return router;
|
|
15
|
+
}
|
|
12
16
|
const engine = createEngine(ripplo);
|
|
13
17
|
const webhookSecret = ripplo.getConfig().webhookSecret;
|
|
14
|
-
const router = Router();
|
|
15
18
|
router.use(json());
|
|
16
19
|
router.use((req, res, next) => {
|
|
17
20
|
if (webhookSecret.length === 0) {
|
package/dist/fastify.d.ts
CHANGED
|
@@ -5,8 +5,9 @@ import './step-DLfkKI3V.js';
|
|
|
5
5
|
import '@ripplo/spec';
|
|
6
6
|
|
|
7
7
|
interface RegisterFastifyHandlerParams {
|
|
8
|
+
readonly enabled: boolean;
|
|
8
9
|
readonly ripplo: RipploBuilder;
|
|
9
10
|
}
|
|
10
|
-
declare function registerFastifyHandler({ ripplo, }: RegisterFastifyHandlerParams): (fastify: FastifyInstance) => Promise<void>;
|
|
11
|
+
declare function registerFastifyHandler({ enabled, ripplo, }: RegisterFastifyHandlerParams): (fastify: FastifyInstance) => Promise<void>;
|
|
11
12
|
|
|
12
13
|
export { type RegisterFastifyHandlerParams, registerFastifyHandler };
|
package/dist/fastify.js
CHANGED
|
@@ -9,8 +9,13 @@ import {
|
|
|
9
9
|
|
|
10
10
|
// src/adapters/fastify.ts
|
|
11
11
|
function registerFastifyHandler({
|
|
12
|
+
enabled,
|
|
12
13
|
ripplo
|
|
13
14
|
}) {
|
|
15
|
+
if (!enabled) {
|
|
16
|
+
return async () => {
|
|
17
|
+
};
|
|
18
|
+
}
|
|
14
19
|
const engine = createEngine(ripplo);
|
|
15
20
|
const webhookSecret = ripplo.getConfig().webhookSecret;
|
|
16
21
|
return async (fastify) => {
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
compile
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-GTHRSFXF.js";
|
|
4
4
|
import "./chunk-MGATMMCZ.js";
|
|
5
5
|
import {
|
|
6
6
|
buildSetCookieHeader,
|
|
@@ -413,7 +413,10 @@ function assertMatchesOutcome(nodes, _test, report) {
|
|
|
413
413
|
});
|
|
414
414
|
}
|
|
415
415
|
}
|
|
416
|
-
function noEmptySteps(nodes,
|
|
416
|
+
function noEmptySteps(nodes, test, report) {
|
|
417
|
+
if (!test.implemented) {
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
417
420
|
if (nodes.length === 0) {
|
|
418
421
|
report({
|
|
419
422
|
message: "Test has zero steps",
|
package/dist/nextjs.d.ts
CHANGED
|
@@ -4,9 +4,10 @@ import './step-DLfkKI3V.js';
|
|
|
4
4
|
import '@ripplo/spec';
|
|
5
5
|
|
|
6
6
|
interface CreateNextHandlerParams {
|
|
7
|
+
readonly enabled: boolean;
|
|
7
8
|
readonly ripplo: RipploBuilder;
|
|
8
9
|
}
|
|
9
10
|
type NextHandler = (req: Request) => Promise<Response>;
|
|
10
|
-
declare function createNextHandler({ ripplo }: CreateNextHandlerParams): NextHandler;
|
|
11
|
+
declare function createNextHandler({ enabled, ripplo }: CreateNextHandlerParams): NextHandler;
|
|
11
12
|
|
|
12
13
|
export { type CreateNextHandlerParams, createNextHandler };
|
package/dist/nextjs.js
CHANGED
|
@@ -8,7 +8,10 @@ import {
|
|
|
8
8
|
} from "./chunk-KWUKVAGI.js";
|
|
9
9
|
|
|
10
10
|
// src/adapters/nextjs.ts
|
|
11
|
-
function createNextHandler({ ripplo }) {
|
|
11
|
+
function createNextHandler({ enabled, ripplo }) {
|
|
12
|
+
if (!enabled) {
|
|
13
|
+
return async () => jsonResponse({ error: "Not found" }, 404);
|
|
14
|
+
}
|
|
12
15
|
const engine = createEngine(ripplo);
|
|
13
16
|
const webhookSecret = ripplo.getConfig().webhookSecret;
|
|
14
17
|
return async function handler(req) {
|