@donotdev/testing 0.1.42

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.
Files changed (38) hide show
  1. package/dist/client/debugC4all.d.ts +5 -0
  2. package/dist/client/debugC4all.d.ts.map +1 -0
  3. package/dist/client/debugC4all.js +2 -0
  4. package/dist/client/debugIsousou.d.ts +26 -0
  5. package/dist/client/debugIsousou.d.ts.map +1 -0
  6. package/dist/client/debugIsousou.js +1 -0
  7. package/dist/client/index.d.ts +5 -0
  8. package/dist/client/index.d.ts.map +1 -0
  9. package/dist/client/index.js +1 -0
  10. package/dist/client/testEntity.d.ts +23 -0
  11. package/dist/client/testEntity.d.ts.map +1 -0
  12. package/dist/client/testEntity.js +1 -0
  13. package/dist/client/testImagePipeline.d.ts +43 -0
  14. package/dist/client/testImagePipeline.d.ts.map +1 -0
  15. package/dist/client/testImagePipeline.js +1 -0
  16. package/dist/client/testImageUpload.d.ts +42 -0
  17. package/dist/client/testImageUpload.d.ts.map +1 -0
  18. package/dist/client/testImageUpload.js +1 -0
  19. package/dist/client/testStorage.d.ts +31 -0
  20. package/dist/client/testStorage.d.ts.map +1 -0
  21. package/dist/client/testStorage.js +1 -0
  22. package/dist/common/index.d.ts +4 -0
  23. package/dist/common/index.d.ts.map +1 -0
  24. package/dist/common/index.js +1 -0
  25. package/dist/common/payloadGenerator.d.ts +28 -0
  26. package/dist/common/payloadGenerator.d.ts.map +1 -0
  27. package/dist/common/payloadGenerator.js +1 -0
  28. package/dist/common/reporter.d.ts +24 -0
  29. package/dist/common/reporter.d.ts.map +1 -0
  30. package/dist/common/reporter.js +1 -0
  31. package/dist/common/types.d.ts +171 -0
  32. package/dist/common/types.d.ts.map +1 -0
  33. package/dist/common/types.js +0 -0
  34. package/dist/index.d.ts +42 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +1 -0
  37. package/dist/symbol-index.json +1 -0
  38. package/package.json +51 -0
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Debug c4all: test car entity CRUD via FunctionsAdapter
3
+ */
4
+ export declare function debugC4all(carEntity: any): Promise<void>;
5
+ //# sourceMappingURL=debugC4all.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"debugC4all.d.ts","sourceRoot":"","sources":["../../src/client/debugC4all.ts"],"names":[],"mappings":"AAgBA;;GAEG;AACH,wBAAsB,UAAU,CAAC,SAAS,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAoC9D"}
@@ -0,0 +1,2 @@
1
+ import{getProvider as e,hasProvider as i}from"@donotdev/core";import{testEntity as s}from"./testEntity";async function l(r){const a=e("crud"),t=e("callable");if(!i("storage"))throw new Error(`[debugC4all] Storage adapter required \u2014 car entity has required images field.
2
+ Ensure FirebaseStorageAdapter is registered via configureProviders().`);const o=e("storage");await s(r,{providers:{crud:a,storage:o,callable:t},payload:{make:"Toyota",model:"Corolla",year:2023,mileage:15e3,fuelType:"Petrol",transmission:"Manual",vin:`TEST${Date.now().toString(36).toUpperCase()}`,price:{amount:15e3,currency:"EUR",vatIncluded:!0,discountPercent:0}}})}export{l as debugC4all};
@@ -0,0 +1,26 @@
1
+ import type { ImagePipelineFns, CrudPipelineFns } from '../common/types';
2
+ export interface DebugIsousouOptions {
3
+ /** Pipeline functions for browser-side image upload test (processImage, uploadImage, deleteImage) */
4
+ pipeline?: ImagePipelineFns;
5
+ /** Pipeline functions for pure-data CRUD test (validate, createMetadata) */
6
+ crudFns?: CrudPipelineFns;
7
+ /** Pre-uploaded picture URLs to include in test payload */
8
+ pictures?: Array<{
9
+ fullUrl: string;
10
+ thumbUrl: string;
11
+ }>;
12
+ /** Real image files to test with in browser pipeline */
13
+ files?: File[];
14
+ /** Skip storage adapter test */
15
+ skipStorage?: boolean;
16
+ /** Skip pipeline tests */
17
+ skipPipeline?: boolean;
18
+ /** Skip entity CRUD test */
19
+ skipCrud?: boolean;
20
+ }
21
+ /**
22
+ * Debug isousou: test storage adapter, image pipeline, and apartment entity CRUD.
23
+ * Each layer isolated so you know exactly where it breaks.
24
+ */
25
+ export declare function debugIsousou(apartmentEntity: any, options?: DebugIsousouOptions): Promise<void>;
26
+ //# sourceMappingURL=debugIsousou.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"debugIsousou.d.ts","sourceRoot":"","sources":["../../src/client/debugIsousou.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAUzE,MAAM,WAAW,mBAAmB;IAClC,qGAAqG;IACrG,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B,4EAA4E;IAC5E,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxD,wDAAwD;IACxD,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;IACf,gCAAgC;IAChC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,0BAA0B;IAC1B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,4BAA4B;IAC5B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAMD;;;GAGG;AACH,wBAAsB,YAAY,CAChC,eAAe,EAAE,GAAG,EACpB,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,IAAI,CAAC,CA0Hf"}
@@ -0,0 +1 @@
1
+ import{getProvider as p}from"@donotdev/core";import{testStorage as d}from"./testStorage";import{testEntity as h}from"./testEntity";import{testImagePipeline as S}from"./testImagePipeline";import{testImageUpload as f}from"./testImageUpload";async function b(a,l={}){const{pipeline:i,crudFns:c,pictures:e,files:r,skipStorage:g=!1,skipPipeline:u=!1,skipCrud:m=!1}=l,o=p("storage"),w=p("crud"),P=p("callable"),n={reference:`TEST-${Date.now().toString(36).toUpperCase()}`,address:{formatted_address:"1 rue de Rivoli, 75001 Paris, France",latitude:48.8606,longitude:2.3376},district_code:1};if(!g){const t=await d({storage:o}),s=await d({storage:o,storagePath:"test"});if(!(!t.passed&&s.passed)&&!(t.passed&&!s.passed)&&!t.passed&&!s.passed)return}if(!u){const t={...n};if(e&&e.length>0&&(t.pictures=e),await S({entity:a,input:t,fns:c}),i&&(await f({fns:i}),r&&r.length>0))for(const s of r)await f({fns:i,file:s})}!m&&a&&await h(a,{providers:{crud:w,storage:o,callable:P},payload:{...n,...e&&e.length>0?{pictures:e}:{}}})}export{b as debugIsousou};
@@ -0,0 +1,5 @@
1
+ export { testEntity } from './testEntity';
2
+ export { testStorage, createTestImageBlob, createTestFileBlob, } from './testStorage';
3
+ export { debugIsousou } from './debugIsousou';
4
+ export { debugC4all } from './debugC4all';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EACL,WAAW,EACX,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1 @@
1
+ import{testEntity as e}from"./testEntity";import{testStorage as t,createTestImageBlob as s,createTestFileBlob as a}from"./testStorage";import{debugIsousou as o}from"./debugIsousou";import{debugC4all as r}from"./debugC4all";export{a as createTestFileBlob,s as createTestImageBlob,r as debugC4all,o as debugIsousou,e as testEntity,t as testStorage};
@@ -0,0 +1,23 @@
1
+ /**
2
+ * @fileoverview Full CRUD flow test for an entity: create → get → list → listCard → update → delete
3
+ * @version 0.1.0
4
+ * @since 0.1.0
5
+ * @author AMBROISE PARK Consulting
6
+ */
7
+ import { type Entity } from '@donotdev/core';
8
+ import type { TestEntityOptions, TestEntityResult } from '../common/types';
9
+ /**
10
+ * Run a full CRUD integration test on an entity.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * import { testEntity } from '@donotdev/testing';
15
+ * import { apartmentEntity } from '../../entities/apartment';
16
+ *
17
+ * const result = await testEntity(apartmentEntity, {
18
+ * providers: { crud, storage, callable },
19
+ * });
20
+ * ```
21
+ */
22
+ export declare function testEntity(entity: Entity, options: TestEntityOptions): Promise<TestEntityResult>;
23
+ //# sourceMappingURL=testEntity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testEntity.d.ts","sourceRoot":"","sources":["../../src/client/testEntity.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AAEH,OAAO,EAAY,KAAK,MAAM,EAAoB,MAAM,gBAAgB,CAAC;AAEzE,OAAO,KAAK,EACV,iBAAiB,EACjB,gBAAgB,EAGjB,MAAM,iBAAiB,CAAC;AAmHzB;;;;;;;;;;;;GAYG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,gBAAgB,CAAC,CA8K3B"}
@@ -0,0 +1 @@
1
+ import{EDITABLE as v}from"@donotdev/core";import{generatePayload as $,getFileFields as D,isMultiFileField as P}from"../common/payloadGenerator";import{reportEntity as F}from"../common/reporter";import{createTestImageBlob as M,createTestFileBlob as C}from"./testStorage";async function y(a,g){const r=performance.now();try{const o=await g();return{step:a,passed:!0,duration:Math.round(performance.now()-r),data:o}}catch(o){return{step:a,passed:!1,duration:Math.round(performance.now()-r),error:o instanceof Error?o.message:String(o),errorDetail:o}}}async function _(a,g,r,o){const w=[],i={};for(const l of a){const p=["image","images","avatar"].includes(l.type),u=P(l),d=l.options?.fieldSpecific?.storagePath??r,h=p?"upload-image":"upload-file",s=await y(h,async()=>{const t=p?M():C(),b=p?"png":"txt",f=`dndev_test_${l.name}_${Date.now()}.${b}`,m=await g.upload(t,{storagePath:d,filename:f,signal:o,onProgress:e=>{}});if(p){const e={fullUrl:m.url,thumbUrl:m.url};i[l.name]=u?[e]:e}else{const e={url:m.url,filename:f,size:t.size,mimeType:t.type};i[l.name]=u?[e]:e}return m});w.push({...s,step:`${h}`}),s.passed||(i[l.name]=u?[]:null)}return{results:w,values:i}}async function k(a,g){const{providers:r,payload:o,cleanup:w=!0,skip:i=[],storagePath:l,signal:p}=g,{crud:u}=r,d=a.collection,h=performance.now(),s=[];let t;const b=await y("generate-payload",async()=>$(a.fields,o));if(s.push(b),!b.passed)return T(a,d,s,t,void 0,h);let f=b.data;const m=D(a.fields);if(m.length>0&&r.storage){const{results:e,values:n}=await _(m,r.storage,l,p);s.push(...e),f={...f,...n}}else m.length>0&&r.storage;if(!i.includes("create")){const e=await y("create",async()=>{const n=await u.add(d,f);return t=n.id,n});if(s.push(e),!e.passed)return T(a,d,s,t,f,h)}if(!i.includes("get")&&t){const e=await y("get",async()=>{const n=await u.get(d,t);if(!n)throw new Error(`get returned null for id=${t}`);return n});s.push(e)}if(!i.includes("list")){const e=await y("list",async()=>{const n=await u.query(d,{limit:5});if(t){const E=n.items.some(c=>c.id===t)}return n});s.push(e)}if(!i.includes("listCard")){const e=await y("listCard",async()=>await u.query(d,{limit:5},void 0,"listCard"));s.push(e)}if(!i.includes("update")&&t){const e=await y("update",async()=>{const n=Object.values(a.fields).find(c=>c.type==="text"&&c.editable!==!1&&c.editable!==v.GENERATED&&c.editable!==v.COMPUTED&&c.editable!==v.CREATE_ONLY&&c.visibility!=="hidden"&&c.visibility!=="technical"),E=n?{[n.name]:`updated_${Date.now().toString(36)}`}:{};return Object.keys(E).length===0?{skipped:!0}:(await u.update(d,t,E),E)});s.push(e)}if(!i.includes("delete")&&t&&w){const e=await y("delete",async()=>{await u.delete(d,t)});s.push(e)}return T(a,d,s,t,f,h)}function T(a,g,r,o,w,i){const l={entity:a.name,collection:g,passed:r.every(p=>p.passed),steps:r,createdId:o,payload:w,duration:Math.round(performance.now()-i)};return F(l),l}export{k as testEntity};
@@ -0,0 +1,43 @@
1
+ /**
2
+ * @fileoverview Deterministic CRUD pipeline test - pure data, no browser APIs.
3
+ *
4
+ * Input: entity definition + form object (like the store/UI would give)
5
+ * Process: real framework code (generate → validate → metadata → adapter)
6
+ * Output: the exact payload that Supabase/Firebase would receive
7
+ *
8
+ * @version 0.2.0
9
+ * @since 0.1.0
10
+ * @author AMBROISE PARK Consulting
11
+ */
12
+ import type { TestImagePipelineOptions, TestImagePipelineResult } from '../common/types';
13
+ /**
14
+ * Test the CRUD pipeline as pure data: form object in → framework processing → adapter payload out.
15
+ *
16
+ * No browser APIs. No File/Blob. No uploads. Just objects.
17
+ *
18
+ * Steps:
19
+ * 1. `generate-payload` — generatePayload() from entity fields + your overrides
20
+ * 2. `upload-pipeline` — merge image/file URLs you provide (no actual upload)
21
+ * 3. `verify-full-url` — validate all Picture URLs are real https (not blob:)
22
+ * 4. `verify-thumb-url` — same for thumb URLs
23
+ * 5. `cleanup-image` — (skipped — nothing to clean in data-only mode)
24
+ *
25
+ * The output at each step is logged. You see exactly what goes in and what comes out.
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * import { testImagePipeline } from '@donotdev/testing';
30
+ * import { apartmentEntity } from '../entities/apartment';
31
+ *
32
+ * await testImagePipeline({
33
+ * entity: apartmentEntity,
34
+ * input: {
35
+ * reference: 'TEST-001',
36
+ * rent: 500,
37
+ * pictures: [{ fullUrl: 'https://storage.../full.webp', thumbUrl: 'https://storage.../thumb.webp' }],
38
+ * },
39
+ * });
40
+ * ```
41
+ */
42
+ export declare function testImagePipeline(options: TestImagePipelineOptions): Promise<TestImagePipelineResult>;
43
+ //# sourceMappingURL=testImagePipeline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testImagePipeline.d.ts","sourceRoot":"","sources":["../../src/client/testImagePipeline.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EACV,wBAAwB,EACxB,uBAAuB,EAIxB,MAAM,iBAAiB,CAAC;AAoCzB;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,uBAAuB,CAAC,CAqMlC"}
@@ -0,0 +1 @@
1
+ import{reportImagePipeline as U}from"../common/reporter";import{generatePayload as v,getFileFields as w}from"../common/payloadGenerator";async function d(t,a){const u=performance.now();try{const e=await a();return{step:t,passed:!0,duration:Math.round(performance.now()-u),data:e}}catch(e){return{step:t,passed:!1,duration:Math.round(performance.now()-u),error:e instanceof Error?e.message:String(e),errorDetail:e}}}async function j(t){const{entity:a,input:u={},fns:e}=t,y=performance.now(),c=[];let i;const m=await d("generate-payload",async()=>{const o=v(a.fields,u);for(const[r,s]of Object.entries(o)){const l=A(s),f=r in u?" (override)":""}const n=w(a.fields);if(n.length>0)for(const r of n){const s=o[r.name]}return o});if(c.push(m),!m.passed)return b(c,void 0,y);if(i=m.data,e?.validate){const o=await d("upload-pipeline",async()=>{const n=e.validate(i);return typeof n=="object"&&n!==null&&(i=n),n});if(c.push(o),!o.passed)return b(c,i,y)}const p=w(a.fields).filter(o=>["image","images","avatar"].includes(o.type));if(p.length>0){const o=await d("verify-full-url",async()=>{const r=[];for(const s of p){const l=i[s.name];l!=null&&(Array.isArray(l)?l:[l]).forEach((f,g)=>{const h=`${s.name}[${g}]`;f.fullUrl?f.fullUrl.startsWith("blob:")&&r.push(`${h}.fullUrl is blob: ${f.fullUrl}`):r.push(`${h}.fullUrl is empty`)})}if(r.length>0){for(const s of r);throw new Error(`${r.length} URL issue(s): ${r.join("; ")}`)}return{checked:p.length}});c.push(o);const n=await d("verify-thumb-url",async()=>{const r=[];for(const s of p){const l=i[s.name];l!=null&&(Array.isArray(l)?l:[l]).forEach((f,g)=>{const h=`${s.name}[${g}]`;f.thumbUrl?f.thumbUrl.startsWith("blob:")&&r.push(`${h}.thumbUrl is blob: ${f.thumbUrl}`):r.push(`${h}.thumbUrl is empty`)})}if(r.length>0){for(const s of r);throw new Error(`${r.length} thumbUrl issue(s): ${r.join("; ")}`)}return{checked:p.length}});c.push(n)}if(e?.createMetadata){const o=await d("cleanup-image",async()=>{const n=e.createMetadata();for(const[r,s]of Object.entries(n));return i={...i,...n},n});c.push(o)}for(const[o,n]of Object.entries(i));const $=b(c,i,y);return U($),$}function A(t){if(t===null)return"null";if(t===void 0)return"undefined";if(typeof t=="string")return t.length>60?`"${t.slice(0,57)}..."`:`"${t}"`;if(typeof t=="number"||typeof t=="boolean")return String(t);if(Array.isArray(t))return`Array(${t.length})`;if(typeof t=="object"){const a=Object.keys(t);return`{${a.slice(0,4).join(", ")}${a.length>4?", ...":""}}`}return String(t)}function b(t,a,u){return{passed:t.every(e=>e.passed),steps:t,payload:a,duration:Math.round(performance.now()-u)}}export{j as testImagePipeline};
@@ -0,0 +1,42 @@
1
+ /**
2
+ * @fileoverview Browser-side image upload pipeline test — processImage → uploadImage → verify → cleanup
3
+ *
4
+ * Exercises the REAL upload pipeline using injected functions from @donotdev/crud.
5
+ * Runs in browser (requires canvas, fetch). Not for Node/vitest.
6
+ *
7
+ * @version 0.1.0
8
+ * @since 0.1.0
9
+ * @author AMBROISE PARK Consulting
10
+ */
11
+ import type { TestImageUploadOptions, TestImageUploadResult } from '../common/types';
12
+ /**
13
+ * Create a 100x100 red PNG test image as a File object, with a white diagonal line.
14
+ * Uses canvas so the output is a valid decodable image (not a raw byte array).
15
+ */
16
+ export declare function createTestImageFile(name?: string): File;
17
+ /**
18
+ * Test the browser image upload pipeline: processImage → uploadImage → verify URLs → cleanup.
19
+ *
20
+ * Uses injection — consumer passes functions from @donotdev/crud. No static import
21
+ * of crud from testing package.
22
+ *
23
+ * Steps:
24
+ * 1. `process-image` — processImage(file) → { fullBlob, thumbBlob, width, height } (skipped if not provided)
25
+ * 2. `upload-pipeline` — uploadImage(fullBlob, thumbBlob, filename, opts) → Picture
26
+ * 3. `verify-full-url` — HEAD request to fullUrl (skipped if verifyUrls=false)
27
+ * 4. `verify-thumb-url` — HEAD request to thumbUrl (skipped if verifyUrls=false)
28
+ * 5. `cleanup-image` — deleteImage(picture) (skipped if cleanup=false or deleteImage not provided)
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * import { testImageUpload } from '@donotdev/testing';
33
+ * import { processImage, uploadImage, deleteImage } from '@donotdev/crud/utils/imageProcessing';
34
+ *
35
+ * await testImageUpload({
36
+ * fns: { processImage, uploadImage, deleteImage },
37
+ * storagePath: 'apartments',
38
+ * });
39
+ * ```
40
+ */
41
+ export declare function testImageUpload(options: TestImageUploadOptions): Promise<TestImageUploadResult>;
42
+ //# sourceMappingURL=testImageUpload.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testImageUpload.d.ts","sourceRoot":"","sources":["../../src/client/testImageUpload.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EACV,sBAAsB,EACtB,qBAAqB,EAKtB,MAAM,iBAAiB,CAAC;AAOzB;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,SAAmB,GAAG,IAAI,CA6BjE;AAkCD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,qBAAqB,CAAC,CAsMhC"}
@@ -0,0 +1 @@
1
+ import{reportImageUpload as I}from"../common/reporter";function U(l="dndev_test.png"){const r=document.createElement("canvas");r.width=100,r.height=100;const e=r.getContext("2d");if(!e)throw new Error("Failed to get canvas 2d context");e.fillStyle="#CC2200",e.fillRect(0,0,100,100),e.strokeStyle="#FFFFFF",e.lineWidth=3,e.beginPath(),e.moveTo(0,0),e.lineTo(100,100),e.stroke();const n=r.toDataURL("image/png").split(",")[1]??"",u=atob(n),c=new Uint8Array(u.length);for(let s=0;s<u.length;s++)c[s]=u.charCodeAt(s);return new File([c],l,{type:"image/png"})}async function p(l,r){const e=performance.now();try{const n=await r();return{step:l,passed:!0,duration:Math.round(performance.now()-e),data:n}}catch(n){return{step:l,passed:!1,duration:Math.round(performance.now()-e),error:n instanceof Error?n.message:String(n),errorDetail:n}}}async function E(l){const{fns:r,storagePath:e,verifyUrls:n=!0,cleanup:u=!0,signal:c}=l,s=l.file??U(),m=performance.now(),h=[];let o,f,d,g;if(r.processImage){const a=await p("process-image",async()=>{const t=await r.processImage(s);return t.fullBlob.size,t.thumbBlob.size,t.fullBlob.type,t.thumbBlob.type,d=t.fullBlob,g=t.thumbBlob,f={fullSize:t.fullBlob.size,thumbSize:t.thumbBlob.size,width:t.width,height:t.height},{fullSize:t.fullBlob.size,thumbSize:t.thumbBlob.size,width:t.width,height:t.height}});if(h.push(a),!a.passed)return w(h,o,f,m)}else d=s,g=s;const b=await p("upload-pipeline",async()=>{const a=await r.uploadImage(d,g,s.name,{storagePath:e,signal:c,onProgress:t=>{}});if(!a.fullUrl)throw new Error("uploadImage returned empty fullUrl");if(!a.thumbUrl)throw new Error("uploadImage returned empty thumbUrl");if(a.fullUrl.startsWith("blob:"))throw new Error(`fullUrl is a blob URL: ${a.fullUrl}`);if(a.thumbUrl.startsWith("blob:"))throw new Error(`thumbUrl is a blob URL: ${a.thumbUrl}`);return o=a,a});if(h.push(b),!b.passed)return w(h,o,f,m);if(n&&o){const a=await p("verify-full-url",async()=>{const i=await fetch(o.fullUrl,{method:"HEAD",signal:c});if(!i.ok)throw new Error(`fullUrl returned HTTP ${i.status}`);return{status:i.status,contentType:i.headers.get("content-type")}});h.push(a);const t=await p("verify-thumb-url",async()=>{const i=await fetch(o.thumbUrl,{method:"HEAD",signal:c});if(!i.ok)throw new Error(`thumbUrl returned HTTP ${i.status}`);return{status:i.status,contentType:i.headers.get("content-type")}});h.push(t)}if(u&&r.deleteImage&&o){const a=await p("cleanup-image",async()=>(await r.deleteImage(o),{deleted:[o.fullUrl,o.thumbUrl]}));h.push(a)}else u&&r.deleteImage;const y=w(h,o,f,m);return I(y),y}function w(l,r,e,n){return{passed:l.every(u=>u.passed),steps:l,picture:r,processed:e,duration:Math.round(performance.now()-n)}}export{U as createTestImageFile,E as testImageUpload};
@@ -0,0 +1,31 @@
1
+ /**
2
+ * @fileoverview Standalone storage adapter test — upload, getUrl, delete
3
+ * @version 0.1.0
4
+ * @since 0.1.0
5
+ * @author AMBROISE PARK Consulting
6
+ */
7
+ import type { TestStorageOptions, TestStorageResult } from '../common/types';
8
+ /**
9
+ * Generate a minimal valid PNG blob for testing image uploads
10
+ */
11
+ export declare function createTestImageBlob(): Blob;
12
+ /**
13
+ * Generate a minimal test file blob
14
+ */
15
+ export declare function createTestFileBlob(): Blob;
16
+ /**
17
+ * Test a storage adapter: upload → getUrl → delete
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * import { testStorage } from '@donotdev/testing';
22
+ * import { SupabaseStorageAdapter } from '@donotdev/supabase';
23
+ *
24
+ * const result = await testStorage({
25
+ * storage: new SupabaseStorageAdapter(supabase, 'uploads'),
26
+ * storagePath: 'test',
27
+ * });
28
+ * ```
29
+ */
30
+ export declare function testStorage(options: TestStorageOptions): Promise<TestStorageResult>;
31
+ //# sourceMappingURL=testStorage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testStorage.d.ts","sourceRoot":"","sources":["../../src/client/testStorage.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,kBAAkB,EAClB,iBAAiB,EAElB,MAAM,iBAAiB,CAAC;AAOzB;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CA0E1C;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAIzC;AAkCD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,iBAAiB,CAAC,CA2C5B"}
@@ -0,0 +1 @@
1
+ import{reportStorage as l}from"../common/reporter";function d(){const a=new Uint8Array([137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,1,0,0,0,1,8,2,0,0,0,144,119,83,222,0,0,0,12,73,68,65,84,8,215,99,248,207,192,0,0,0,2,0,1,226,33,188,51,0,0,0,0,73,69,78,68,174,66,96,130]);return new Blob([a],{type:"image/png"})}function f(){return new Blob(["test file content from @donotdev/testing"],{type:"text/plain"})}async function i(a,n){const s=performance.now();try{const t=await n();return{step:a,passed:!0,duration:Math.round(performance.now()-s),data:t}}catch(t){return{step:a,passed:!1,duration:Math.round(performance.now()-s),error:t instanceof Error?t.message:String(t),errorDetail:t}}}async function w(a){const{storage:n,storagePath:s,signal:t}=a,g=performance.now(),r=[];let o;const c=await i("storage-upload",async()=>{const e=d(),u=await n.upload(e,{storagePath:s,filename:`dndev_test_${Date.now()}.png`,signal:t});return o=u.path,u});if(r.push(c),c.passed&&o){const e=await i("storage-get-url",async()=>n.getUrl(o));r.push(e)}if(c.passed&&o){const e=await i("storage-delete",async()=>{await n.delete(o)});r.push(e)}const p={passed:r.every(e=>e.passed),steps:r,duration:Math.round(performance.now()-g)};return l(p),p}export{f as createTestFileBlob,d as createTestImageBlob,w as testStorage};
@@ -0,0 +1,4 @@
1
+ export type { TestProviders, TestEntityOptions, TestStorageOptions, TestStep, TestStepResult, TestEntityResult, TestStorageResult, } from './types';
2
+ export { generatePayload, getFileFields, isFileField, isMultiFileField, } from './payloadGenerator';
3
+ export { reportEntity, reportStorage } from './reporter';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/common/index.ts"],"names":[],"mappings":"AAEA,YAAY,EACV,aAAa,EACb,iBAAiB,EACjB,kBAAkB,EAClB,QAAQ,EACR,cAAc,EACd,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,SAAS,CAAC;AAEjB,OAAO,EACL,eAAe,EACf,aAAa,EACb,WAAW,EACX,gBAAgB,GACjB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1 @@
1
+ import{generatePayload as e,getFileFields as i,isFileField as a,isMultiFileField as t}from"./payloadGenerator";import{reportEntity as r,reportStorage as s}from"./reporter";export{e as generatePayload,i as getFileFields,a as isFileField,t as isMultiFileField,r as reportEntity,s as reportStorage};
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @fileoverview Generate realistic test payloads from entity field definitions
3
+ * @version 0.1.0
4
+ * @since 0.1.0
5
+ * @author AMBROISE PARK Consulting
6
+ */
7
+ import { type EntityField } from '@donotdev/core';
8
+ /**
9
+ * Returns true if the field holds a file/image that needs upload
10
+ */
11
+ export declare function isFileField(field: EntityField): boolean;
12
+ /**
13
+ * Returns true if the field is a multi-file field
14
+ */
15
+ export declare function isMultiFileField(field: EntityField): boolean;
16
+ /**
17
+ * Generate a test payload for an entity.
18
+ * Returns only writable, non-file fields. File fields are handled separately.
19
+ *
20
+ * @param fields - Entity field definitions (entity.fields)
21
+ * @param overrides - Partial payload to merge (takes precedence)
22
+ */
23
+ export declare function generatePayload(fields: Record<string, EntityField>, overrides?: Record<string, unknown>): Record<string, unknown>;
24
+ /**
25
+ * Extract file fields from entity definition
26
+ */
27
+ export declare function getFileFields(fields: Record<string, EntityField>): EntityField[];
28
+ //# sourceMappingURL=payloadGenerator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"payloadGenerator.d.ts","sourceRoot":"","sources":["../../src/common/payloadGenerator.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AAEH,OAAO,EAAY,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAoI5D;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAUvD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAE5D;AAMD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EACnC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAClC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA2CzB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,GAClC,WAAW,EAAE,CAIf"}
@@ -0,0 +1 @@
1
+ import{EDITABLE as d}from"@donotdev/core";const c={text:e=>{const t=e.validation?.maxLength??20;return`test_${e.name}_${Date.now().toString(36)}`.slice(0,t)},textarea:e=>`Test ${e.name} content \u2014 generated by @donotdev/testing`,richtext:()=>"<p>Test rich text content</p>",email:()=>`test_${Date.now().toString(36)}@example.com`,url:()=>"https://example.com/test",password:()=>"Test1234!@#$",tel:()=>"+33612345678",iban:()=>"FR7630006000011234567890189",number:e=>{const t=e.validation?.min??0,n=e.validation?.max??100;return Math.floor(t+(n-t)/2)},range:e=>c.number(e),rating:()=>3,year:e=>{const t=e.validation?.min??2e3,n=e.validation?.max??new Date().getFullYear();return Math.floor(t+(n-t)/2)},price:()=>({amount:9900,currency:"EUR",vatIncluded:!0,discountPercent:0}),currency:e=>{const t=e.validation?.min??0,n=e.validation?.max??1e4;return Math.floor(t+(n-t)/2)},select:e=>o(e),radio:e=>o(e),combobox:e=>o(e)??`test_${e.name}`,multiselect:e=>{const t=o(e);return t?[t]:[]},boolean:()=>!0,checkbox:()=>!0,switch:e=>{const t=e.options?.fieldSpecific?.checkedValue;return t!==void 0?t:!0},date:()=>new Date().toISOString().split("T")[0],"datetime-local":()=>new Date().toISOString().slice(0,16),time:()=>"12:00",month:()=>`${new Date().getFullYear()}-01`,week:()=>`${new Date().getFullYear()}-W01`,timestamp:()=>new Date().toISOString(),image:()=>null,images:()=>[],file:()=>null,files:()=>[],document:()=>null,documents:()=>[],avatar:()=>null,address:()=>({formatted_address:"1 rue de Rivoli, 75001 Paris, France",latitude:48.8606,longitude:2.3376}),geopoint:()=>({lat:48.8566,lng:2.3522}),reference:()=>"",hidden:()=>"test_hidden_value",color:()=>"#3B82F6",badge:()=>"test",map:()=>({key1:"value1"}),array:()=>[],"field-array":()=>[],duration:()=>3600,gdprConsent:()=>!0};function o(e){const t=e.validation?.options;return t?(typeof t=="function"?t({}):t)?.[0]?.value:void 0}function u(e){return!(new Set(["id","createdAt","updatedAt","createdById","updatedById","status"]).has(e.name)||e.editable===d.GENERATED||e.editable===d.COMPUTED||e.editable===!1||e.visibility==="hidden")}function r(e){return["image","images","file","files","document","documents","avatar"].includes(e.type)}function m(e){return["images","files","documents"].includes(e.type)}function f(e,t){const n={};for(const[i,a]of Object.entries(e)){if(!u(a)||r(a))continue;if(t&&i in t){n[i]=t[i];continue}const l=c[a.type];if(!l){a.validation?.required&&(n[i]=`test_${a.type}_${i}`);continue}const s=l(a);(a.validation?.required||s!==null)&&(n[i]=s)}if(t)for(const[i,a]of Object.entries(t))i in n||(n[i]=a);return n}function g(e){return Object.values(e).filter(t=>u(t)&&r(t))}export{f as generatePayload,g as getFileFields,r as isFileField,m as isMultiFileField};
@@ -0,0 +1,24 @@
1
+ /**
2
+ * @fileoverview Test result reporter — console output for browser + vitest
3
+ * @version 0.1.0
4
+ * @since 0.1.0
5
+ * @author AMBROISE PARK Consulting
6
+ */
7
+ import type { TestEntityResult, TestStorageResult, TestImagePipelineResult, TestImageUploadResult } from './types';
8
+ /**
9
+ * Print entity test results to console (works in browser + Node)
10
+ */
11
+ export declare function reportEntity(result: TestEntityResult): void;
12
+ /**
13
+ * Print storage test results to console
14
+ */
15
+ export declare function reportStorage(result: TestStorageResult): void;
16
+ /**
17
+ * Print image upload test results to console (real browser pipeline)
18
+ */
19
+ export declare function reportImageUpload(result: TestImageUploadResult): void;
20
+ /**
21
+ * Print image pipeline test results to console
22
+ */
23
+ export declare function reportImagePipeline(result: TestImagePipelineResult): void;
24
+ //# sourceMappingURL=reporter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reporter.d.ts","sourceRoot":"","sources":["../../src/common/reporter.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AAEH,OAAO,KAAK,EAEV,gBAAgB,EAChB,iBAAiB,EACjB,uBAAuB,EACvB,qBAAqB,EACtB,MAAM,SAAS,CAAC;AAyBjB;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAgC3D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAgB7D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,qBAAqB,GAAG,IAAI,CA2BrE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,uBAAuB,GAAG,IAAI,CAgBzE"}
@@ -0,0 +1 @@
1
+ function a(e){return e?"\x1B[32mPASS\x1B[0m":"\x1B[31mFAIL\x1B[0m"}function r(e){return e<1e3?`${e}ms`:`${(e/1e3).toFixed(2)}s`}function f(e){const t=e.passed?" +":" x",s=r(e.duration),n=e.error?` \u2014 ${e.error}`:"";return`${t} ${e.step} (${s})${n}`}function o(e){const t=`[${a(e.passed)}] ${e.entity} (${e.collection}) \u2014 ${r(e.duration)}`;e.createdId;for(const s of e.steps);if(!e.passed&&e.payload){const s=e.steps.find(n=>n.step==="create"&&!n.passed)}for(const s of e.steps)!s.passed&&s.errorDetail}function p(e){const t=`[${a(e.passed)}] Storage test \u2014 ${r(e.duration)}`;for(const s of e.steps);for(const s of e.steps)!s.passed&&s.errorDetail}function c(e){const t=`[${a(e.passed)}] Image Upload \u2014 ${r(e.duration)}`;e.processed,e.picture;for(const s of e.steps);for(const s of e.steps)!s.passed&&s.errorDetail}function i(e){const t=`[${a(e.passed)}] Image Pipeline \u2014 ${r(e.duration)}`;for(const s of e.steps);for(const s of e.steps)!s.passed&&s.errorDetail}export{o as reportEntity,i as reportImagePipeline,c as reportImageUpload,p as reportStorage};
@@ -0,0 +1,171 @@
1
+ /**
2
+ * @fileoverview Types for @donotdev/testing
3
+ * @version 0.1.0
4
+ * @since 0.1.0
5
+ * @author AMBROISE PARK Consulting
6
+ */
7
+ import type { ICrudAdapter, IStorageAdapter, ICallableProvider } from '@donotdev/core';
8
+ /** Providers needed to run entity tests */
9
+ export interface TestProviders {
10
+ /** CRUD adapter (required) */
11
+ crud: ICrudAdapter;
12
+ /** Storage adapter (required for image/file field tests) */
13
+ storage?: IStorageAdapter;
14
+ /** Callable provider (required for FunctionsAdapter) */
15
+ callable?: ICallableProvider;
16
+ }
17
+ /** Options for testEntity() */
18
+ export interface TestEntityOptions {
19
+ /** Provider instances to test against */
20
+ providers: TestProviders;
21
+ /** Partial payload overrides (merged with generated values) */
22
+ payload?: Record<string, unknown>;
23
+ /** Delete created entity after test (default: true) */
24
+ cleanup?: boolean;
25
+ /** Role to test as (affects field visibility) — default: 'admin' */
26
+ role?: 'guest' | 'user' | 'admin' | 'super';
27
+ /** Skip specific steps */
28
+ skip?: Array<'create' | 'get' | 'list' | 'listCard' | 'update' | 'delete'>;
29
+ /** Storage path override for file/image uploads */
30
+ storagePath?: string;
31
+ /** AbortSignal for cancellation */
32
+ signal?: AbortSignal;
33
+ }
34
+ /** Options for testStorage() */
35
+ export interface TestStorageOptions {
36
+ /** Storage adapter to test */
37
+ storage: IStorageAdapter;
38
+ /** Folder path within bucket */
39
+ storagePath?: string;
40
+ /** AbortSignal for cancellation */
41
+ signal?: AbortSignal;
42
+ }
43
+ export type TestStep = 'generate-payload' | 'upload-image' | 'upload-file' | 'create' | 'get' | 'list' | 'listCard' | 'update' | 'delete' | 'storage-upload' | 'storage-get-url' | 'storage-delete' | 'process-image' | 'upload-pipeline' | 'verify-full-url' | 'verify-thumb-url' | 'cleanup-image';
44
+ export interface TestStepResult {
45
+ /** Step name */
46
+ step: TestStep;
47
+ /** Whether the step passed */
48
+ passed: boolean;
49
+ /** Duration in ms */
50
+ duration: number;
51
+ /** Error message if failed */
52
+ error?: string;
53
+ /** Full error object for debugging */
54
+ errorDetail?: unknown;
55
+ /** Data returned by the step (for debugging) */
56
+ data?: unknown;
57
+ }
58
+ export interface TestEntityResult {
59
+ /** Entity name */
60
+ entity: string;
61
+ /** Collection name */
62
+ collection: string;
63
+ /** Overall pass/fail */
64
+ passed: boolean;
65
+ /** Per-step results */
66
+ steps: TestStepResult[];
67
+ /** ID of created entity (if any) */
68
+ createdId?: string;
69
+ /** Generated payload used for create */
70
+ payload?: Record<string, unknown>;
71
+ /** Total duration in ms */
72
+ duration: number;
73
+ }
74
+ export interface TestStorageResult {
75
+ /** Overall pass/fail */
76
+ passed: boolean;
77
+ /** Per-step results */
78
+ steps: TestStepResult[];
79
+ /** Total duration in ms */
80
+ duration: number;
81
+ }
82
+ /** Picture shape (matches @donotdev/core Picture) */
83
+ export interface PipelinePicture {
84
+ fullUrl: string;
85
+ thumbUrl: string;
86
+ }
87
+ /** Injectable pipeline functions for transformation steps */
88
+ export interface CrudPipelineFns {
89
+ /** Validate payload against entity schema — return validated data or throw */
90
+ validate?: (data: Record<string, unknown>) => Record<string, unknown> | boolean;
91
+ /** Create metadata fields (createdAt, createdById, etc.) */
92
+ createMetadata?: () => Record<string, unknown>;
93
+ }
94
+ /** Options for testImagePipeline() */
95
+ export interface TestImagePipelineOptions {
96
+ /** Entity definition */
97
+ entity: import('@donotdev/core').Entity;
98
+ /** Form input object — like what the store/UI would give. Merged with generated defaults. */
99
+ input?: Record<string, unknown>;
100
+ /** Optional pipeline functions for validation + metadata steps */
101
+ fns?: CrudPipelineFns;
102
+ }
103
+ /** Result from testImagePipeline() */
104
+ export interface TestImagePipelineResult {
105
+ /** Overall pass/fail */
106
+ passed: boolean;
107
+ /** Per-step results */
108
+ steps: TestStepResult[];
109
+ /** Final payload that adapter.add() would receive */
110
+ payload?: Record<string, unknown>;
111
+ /** Total duration in ms */
112
+ duration: number;
113
+ }
114
+ /** Processed image output shape (matches @donotdev/crud processImage return) */
115
+ export interface ProcessedImageResult {
116
+ fullBlob: Blob;
117
+ thumbBlob: Blob;
118
+ width: number;
119
+ height: number;
120
+ }
121
+ /** Injectable browser pipeline functions from @donotdev/crud */
122
+ export interface ImagePipelineFns {
123
+ /** processImage from crud — File → { fullBlob, thumbBlob, width, height } */
124
+ processImage?: (file: File) => Promise<ProcessedImageResult>;
125
+ /** uploadImage from crud — (fullBlob, thumbBlob, filename, opts) → Picture */
126
+ uploadImage: (fullBlob: Blob, thumbBlob: Blob, filename: string, options?: {
127
+ storagePath?: string;
128
+ onProgress?: (p: {
129
+ bytesTransferred: number;
130
+ totalBytes: number;
131
+ progress: number;
132
+ }) => void;
133
+ signal?: AbortSignal;
134
+ }) => Promise<PipelinePicture>;
135
+ /** deleteImage from crud — cleanup (optional) */
136
+ deleteImage?: (picture: PipelinePicture) => Promise<void>;
137
+ }
138
+ /** Options for testImageUpload() */
139
+ export interface TestImageUploadOptions {
140
+ /** Pipeline functions to test (injected from @donotdev/crud) */
141
+ fns: ImagePipelineFns;
142
+ /** Test image file — if not provided, uses generated 100x100 test image */
143
+ file?: File;
144
+ /** Storage path (folder within bucket) */
145
+ storagePath?: string;
146
+ /** Verify uploaded URLs are accessible via fetch HEAD (default: true) */
147
+ verifyUrls?: boolean;
148
+ /** Delete uploaded images after test (default: true) */
149
+ cleanup?: boolean;
150
+ /** AbortSignal */
151
+ signal?: AbortSignal;
152
+ }
153
+ /** Result from testImageUpload() */
154
+ export interface TestImageUploadResult {
155
+ /** Overall pass/fail */
156
+ passed: boolean;
157
+ /** Per-step results */
158
+ steps: TestStepResult[];
159
+ /** Picture returned by uploadImage (if succeeded) */
160
+ picture?: PipelinePicture;
161
+ /** Processed image info (if processImage was provided) */
162
+ processed?: {
163
+ fullSize: number;
164
+ thumbSize: number;
165
+ width: number;
166
+ height: number;
167
+ };
168
+ /** Total duration in ms */
169
+ duration: number;
170
+ }
171
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/common/types.ts"],"names":[],"mappings":"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"}
File without changes
@@ -0,0 +1,42 @@
1
+ /**
2
+ * @donotdev/testing — Entity CRUD and storage integration testing
3
+ *
4
+ * @example Browser console:
5
+ * ```ts
6
+ * import { testEntity } from '@donotdev/testing';
7
+ * import { apartmentEntity } from '../../entities/apartment';
8
+ * import { getProvider } from '@donotdev/core';
9
+ *
10
+ * await testEntity(apartmentEntity, {
11
+ * providers: {
12
+ * crud: getProvider('crud'),
13
+ * storage: getProvider('storage'),
14
+ * callable: getProvider('callable'),
15
+ * },
16
+ * });
17
+ * ```
18
+ *
19
+ * @example Image pipeline (full trace):
20
+ * ```ts
21
+ * import { testImagePipeline } from '@donotdev/testing';
22
+ * import { processImage } from '@donotdev/crud/utils/imageProcessing';
23
+ * import { uploadImage, deleteImage } from '@donotdev/crud/utils/imageStorage';
24
+ *
25
+ * await testImagePipeline({
26
+ * fns: { processImage, uploadImage, deleteImage },
27
+ * storagePath: 'apartments',
28
+ * });
29
+ * ```
30
+ *
31
+ * @packageDocumentation
32
+ */
33
+ export { testEntity } from './client/testEntity';
34
+ export { testStorage, createTestImageBlob, createTestFileBlob, } from './client/testStorage';
35
+ export { testImagePipeline } from './client/testImagePipeline';
36
+ export { testImageUpload, createTestImageFile } from './client/testImageUpload';
37
+ export { debugIsousou } from './client/debugIsousou';
38
+ export { debugC4all } from './client/debugC4all';
39
+ export { generatePayload, getFileFields, isFileField, isMultiFileField, } from './common/payloadGenerator';
40
+ export { reportEntity, reportStorage, reportImagePipeline, reportImageUpload, } from './common/reporter';
41
+ export type { TestProviders, TestEntityOptions, TestStorageOptions, TestImagePipelineOptions, TestImagePipelineResult, CrudPipelineFns, PipelinePicture, TestStep, TestStepResult, TestEntityResult, TestStorageResult, TestImageUploadOptions, TestImageUploadResult, ImagePipelineFns, ProcessedImageResult, } from './common/types';
42
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAGH,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EACL,WAAW,EACX,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAChF,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAGjD,OAAO,EACL,eAAe,EACf,aAAa,EACb,WAAW,EACX,gBAAgB,GACjB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,YAAY,EACZ,aAAa,EACb,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAG3B,YAAY,EACV,aAAa,EACb,iBAAiB,EACjB,kBAAkB,EAClB,wBAAwB,EACxB,uBAAuB,EACvB,eAAe,EACf,eAAe,EACf,QAAQ,EACR,cAAc,EACd,gBAAgB,EAChB,iBAAiB,EACjB,sBAAsB,EACtB,qBAAqB,EACrB,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,gBAAgB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ import{testEntity as e}from"./client/testEntity";import{testStorage as a,createTestImageBlob as t,createTestFileBlob as s}from"./client/testStorage";import{testImagePipeline as r}from"./client/testImagePipeline";import{testImageUpload as i,createTestImageFile as o}from"./client/testImageUpload";import{debugIsousou as l}from"./client/debugIsousou";import{debugC4all as m}from"./client/debugC4all";import{generatePayload as p,getFileFields as g,isFileField as d,isMultiFileField as F}from"./common/payloadGenerator";import{reportEntity as I,reportStorage as u,reportImagePipeline as n,reportImageUpload as b}from"./common/reporter";export{s as createTestFileBlob,t as createTestImageBlob,o as createTestImageFile,m as debugC4all,l as debugIsousou,p as generatePayload,g as getFileFields,d as isFileField,F as isMultiFileField,I as reportEntity,n as reportImagePipeline,b as reportImageUpload,u as reportStorage,e as testEntity,r as testImagePipeline,i as testImageUpload,a as testStorage};
@@ -0,0 +1 @@
1
+ {"testEntity":"/**\n * Run a full CRUD integration test on an entity.\n *\n * @example\n * ```ts\n * import { testEntity } from '@donotdev/testing';\n * import { apartmentEntity } from '../../entities/apartment';\n *\n * const result = await testEntity(apartmentEntity, {\n * providers: { crud, storage, callable },\n * });\n * ```\n */\nexport declare function testEntity(entity: Entity, options: TestEntityOptions): Promise<TestEntityResult>;","testStorage":"/**\n * Test a storage adapter: upload → getUrl → delete\n *\n * @example\n * ```ts\n * import { testStorage } from '@donotdev/testing';\n * import { SupabaseStorageAdapter } from '@donotdev/supabase';\n *\n * const result = await testStorage({\n * storage: new SupabaseStorageAdapter(supabase, 'uploads'),\n * storagePath: 'test',\n * });\n * ```\n */\nexport declare function testStorage(options: TestStorageOptions): Promise<TestStorageResult>;","createTestImageBlob":"/**\n * Generate a minimal valid PNG blob for testing image uploads\n */\nexport declare function createTestImageBlob(): Blob;","createTestFileBlob":"/**\n * Generate a minimal test file blob\n */\nexport declare function createTestFileBlob(): Blob;","testImagePipeline":"/**\n * Test the CRUD pipeline as pure data: form object in → framework processing → adapter payload out.\n *\n * No browser APIs. No File/Blob. No uploads. Just objects.\n *\n * Steps:\n * 1. `generate-payload` — generatePayload() from entity fields + your overrides\n * 2. `upload-pipeline` — merge image/file URLs you provide (no actual upload)\n * 3. `verify-full-url` — validate all Picture URLs are real https (not blob:)\n * 4. `verify-thumb-url` — same for thumb URLs\n * 5. `cleanup-image` — (skipped — nothing to clean in data-only mode)\n *\n * The output at each step is logged. You see exactly what goes in and what comes out.\n *\n * @example\n * ```ts\n * import { testImagePipeline } from '@donotdev/testing';\n * import { apartmentEntity } from '../entities/apartment';\n *\n * await testImagePipeline({\n * entity: apartmentEntity,\n * input: {\n * reference: 'TEST-001',\n * rent: 500,\n * pictures: [{ fullUrl: 'https://storage.../full.webp', thumbUrl: 'https://storage.../thumb.webp' }],\n * },\n * });\n * ```\n */\nexport declare function testImagePipeline(options: TestImagePipelineOptions): Promise<TestImagePipelineResult>;","testImageUpload":"/**\n * Test the browser image upload pipeline: processImage → uploadImage → verify URLs → cleanup.\n *\n * Uses injection — consumer passes functions from @donotdev/crud. No static import\n * of crud from testing package.\n *\n * Steps:\n * 1. `process-image` — processImage(file) → { fullBlob, thumbBlob, width, height } (skipped if not provided)\n * 2. `upload-pipeline` — uploadImage(fullBlob, thumbBlob, filename, opts) → Picture\n * 3. `verify-full-url` — HEAD request to fullUrl (skipped if verifyUrls=false)\n * 4. `verify-thumb-url` — HEAD request to thumbUrl (skipped if verifyUrls=false)\n * 5. `cleanup-image` — deleteImage(picture) (skipped if cleanup=false or deleteImage not provided)\n *\n * @example\n * ```ts\n * import { testImageUpload } from '@donotdev/testing';\n * import { processImage, uploadImage, deleteImage } from '@donotdev/crud/utils/imageProcessing';\n *\n * await testImageUpload({\n * fns: { processImage, uploadImage, deleteImage },\n * storagePath: 'apartments',\n * });\n * ```\n */\nexport declare function testImageUpload(options: TestImageUploadOptions): Promise<TestImageUploadResult>;","createTestImageFile":"/**\n * Create a 100x100 red PNG test image as a File object, with a white diagonal line.\n * Uses canvas so the output is a valid decodable image (not a raw byte array).\n */\nexport declare function createTestImageFile(name?: string): File;","debugIsousou":"/**\n * Debug isousou: test storage adapter, image pipeline, and apartment entity CRUD.\n * Each layer isolated so you know exactly where it breaks.\n */\nexport declare function debugIsousou(apartmentEntity: any, options?: DebugIsousouOptions): Promise<void>;","debugC4all":"/**\n * Debug c4all: test car entity CRUD via FunctionsAdapter\n */\nexport declare function debugC4all(carEntity: any): Promise<void>;","generatePayload":"/**\n * Generate a test payload for an entity.\n * Returns only writable, non-file fields. File fields are handled separately.\n *\n * @param fields - Entity field definitions (entity.fields)\n * @param overrides - Partial payload to merge (takes precedence)\n */\nexport declare function generatePayload(fields: Record<string, EntityField>, overrides?: Record<string, unknown>): Record<string, unknown>;","getFileFields":"/**\n * Extract file fields from entity definition\n */\nexport declare function getFileFields(fields: Record<string, EntityField>): EntityField[];","isFileField":"/**\n * Returns true if the field holds a file/image that needs upload\n */\nexport declare function isFileField(field: EntityField): boolean;","isMultiFileField":"/**\n * Returns true if the field is a multi-file field\n */\nexport declare function isMultiFileField(field: EntityField): boolean;","reportEntity":"/**\n * Print entity test results to console (works in browser + Node)\n */\nexport declare function reportEntity(result: TestEntityResult): void;","reportStorage":"/**\n * Print storage test results to console\n */\nexport declare function reportStorage(result: TestStorageResult): void;","reportImagePipeline":"/**\n * Print image pipeline test results to console\n */\nexport declare function reportImagePipeline(result: TestImagePipelineResult): void;","reportImageUpload":"/**\n * Print image upload test results to console (real browser pipeline)\n */\nexport declare function reportImageUpload(result: TestImageUploadResult): void;","TestProviders":"/** Providers needed to run entity tests */\nexport interface TestProviders {\n /** CRUD adapter (required) */\n crud: ICrudAdapter;\n /** Storage adapter (required for image/file field tests) */\n storage?: IStorageAdapter;\n /** Callable provider (required for FunctionsAdapter) */\n callable?: ICallableProvider;\n}","TestEntityOptions":"/** Options for testEntity() */\nexport interface TestEntityOptions {\n /** Provider instances to test against */\n providers: TestProviders;\n /** Partial payload overrides (merged with generated values) */\n payload?: Record<string, unknown>;\n /** Delete created entity after test (default: true) */\n cleanup?: boolean;\n /** Role to test as (affects field visibility) — default: 'admin' */\n role?: 'guest' | 'user' | 'admin' | 'super';\n /** Skip specific steps */\n skip?: Array<'create' | 'get' | 'list' | 'listCard' | 'update' | 'delete'>;\n /** Storage path override for file/image uploads */\n storagePath?: string;\n /** AbortSignal for cancellation */\n signal?: AbortSignal;\n}","TestStorageOptions":"/** Options for testStorage() */\nexport interface TestStorageOptions {\n /** Storage adapter to test */\n storage: IStorageAdapter;\n /** Folder path within bucket */\n storagePath?: string;\n /** AbortSignal for cancellation */\n signal?: AbortSignal;\n}","TestImagePipelineOptions":"/** Options for testImagePipeline() */\nexport interface TestImagePipelineOptions {\n /** Entity definition */\n entity: import('@donotdev/core').Entity;\n /** Form input object — like what the store/UI would give. Merged with generated defaults. */\n input?: Record<string, unknown>;\n /** Optional pipeline functions for validation + metadata steps */\n fns?: CrudPipelineFns;\n}","TestImagePipelineResult":"/** Result from testImagePipeline() */\nexport interface TestImagePipelineResult {\n /** Overall pass/fail */\n passed: boolean;\n /** Per-step results */\n steps: TestStepResult[];\n /** Final payload that adapter.add() would receive */\n payload?: Record<string, unknown>;\n /** Total duration in ms */\n duration: number;\n}","CrudPipelineFns":"/** Injectable pipeline functions for transformation steps */\nexport interface CrudPipelineFns {\n /** Validate payload against entity schema — return validated data or throw */\n validate?: (data: Record<string, unknown>) => Record<string, unknown> | boolean;\n /** Create metadata fields (createdAt, createdById, etc.) */\n createMetadata?: () => Record<string, unknown>;\n}","PipelinePicture":"/** Picture shape (matches @donotdev/core Picture) */\nexport interface PipelinePicture {\n fullUrl: string;\n thumbUrl: string;\n}","TestStep":"export type TestStep = 'generate-payload' | 'upload-image' | 'upload-file' | 'create' | 'get' | 'list' | 'listCard' | 'update' | 'delete' | 'storage-upload' | 'storage-get-url' | 'storage-delete' | 'process-image' | 'upload-pipeline' | 'verify-full-url' | 'verify-thumb-url' | 'cleanup-image';","TestStepResult":"export interface TestStepResult {\n /** Step name */\n step: TestStep;\n /** Whether the step passed */\n passed: boolean;\n /** Duration in ms */\n duration: number;\n /** Error message if failed */\n error?: string;\n /** Full error object for debugging */\n errorDetail?: unknown;\n /** Data returned by the step (for debugging) */\n data?: unknown;\n}","TestEntityResult":"export interface TestEntityResult {\n /** Entity name */\n entity: string;\n /** Collection name */\n collection: string;\n /** Overall pass/fail */\n passed: boolean;\n /** Per-step results */\n steps: TestStepResult[];\n /** ID of created entity (if any) */\n createdId?: string;\n /** Generated payload used for create */\n payload?: Record<string, unknown>;\n /** Total duration in ms */\n duration: number;\n}","TestStorageResult":"export interface TestStorageResult {\n /** Overall pass/fail */\n passed: boolean;\n /** Per-step results */\n steps: TestStepResult[];\n /** Total duration in ms */\n duration: number;\n}","TestImageUploadOptions":"/** Options for testImageUpload() */\nexport interface TestImageUploadOptions {\n /** Pipeline functions to test (injected from @donotdev/crud) */\n fns: ImagePipelineFns;\n /** Test image file — if not provided, uses generated 100x100 test image */\n file?: File;\n /** Storage path (folder within bucket) */\n storagePath?: string;\n /** Verify uploaded URLs are accessible via fetch HEAD (default: true) */\n verifyUrls?: boolean;\n /** Delete uploaded images after test (default: true) */\n cleanup?: boolean;\n /** AbortSignal */\n signal?: AbortSignal;\n}","TestImageUploadResult":"/** Result from testImageUpload() */\nexport interface TestImageUploadResult {\n /** Overall pass/fail */\n passed: boolean;\n /** Per-step results */\n steps: TestStepResult[];\n /** Picture returned by uploadImage (if succeeded) */\n picture?: PipelinePicture;\n /** Processed image info (if processImage was provided) */\n processed?: {\n fullSize: number;\n thumbSize: number;\n width: number;\n height: number;\n };\n /** Total duration in ms */\n duration: number;\n}","ImagePipelineFns":"/** Injectable browser pipeline functions from @donotdev/crud */\nexport interface ImagePipelineFns {\n /** processImage from crud — File → { fullBlob, thumbBlob, width, height } */\n processImage?: (file: File) => Promise<ProcessedImageResult>;\n /** uploadImage from crud — (fullBlob, thumbBlob, filename, opts) → Picture */\n uploadImage: (fullBlob: Blob, thumbBlob: Blob, filename: string, options?: {\n storagePath?: string;\n onProgress?: (p: {\n bytesTransferred: number;\n totalBytes: number;\n progress: number;\n }) => void;\n signal?: AbortSignal;\n }) => Promise<PipelinePicture>;\n /** deleteImage from crud — cleanup (optional) */\n deleteImage?: (picture: PipelinePicture) => Promise<void>;\n}","ProcessedImageResult":"/** Processed image output shape (matches @donotdev/crud processImage return) */\nexport interface ProcessedImageResult {\n fullBlob: Blob;\n thumbBlob: Blob;\n width: number;\n height: number;\n}"}
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@donotdev/testing",
3
+ "version": "0.1.42",
4
+ "private": false,
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "description": "Entity CRUD and storage integration testing for DoNotDev — generate payloads, run full CRUD flows, debug adapter chains",
8
+ "main": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "default": "./dist/index.js"
15
+ }
16
+ },
17
+ "scripts": {
18
+ "dev": "tsc --noEmit --watch --listFiles false --listEmittedFiles false",
19
+ "clean": "rimraf dist tsconfig.tsbuildinfo",
20
+ "type-check": "bunx tsc --noEmit",
21
+ "test": "vitest run",
22
+ "test:watch": "vitest"
23
+ },
24
+ "dependencies": {},
25
+ "peerDependencies": {
26
+ "@donotdev/core": "^0.1.42"
27
+ },
28
+ "files": [
29
+ "dist",
30
+ "package.json",
31
+ "README.md",
32
+ "LICENSE.md"
33
+ ],
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "https://github.com/donotdev/dndev.git"
37
+ },
38
+ "keywords": [
39
+ "donotdev",
40
+ "dndev",
41
+ "testing",
42
+ "crud",
43
+ "entity",
44
+ "integration-test",
45
+ "typescript"
46
+ ],
47
+ "publishConfig": {
48
+ "registry": "https://registry.npmjs.org",
49
+ "access": "public"
50
+ }
51
+ }