joist-test-utils 2.3.0 → 2.4.0-next.2

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 (61) hide show
  1. package/README.md +1 -1
  2. package/build/RunPlugin.cjs +219 -0
  3. package/build/RunPlugin.cjs.map +1 -0
  4. package/build/RunPlugin.d.cts +12 -0
  5. package/build/RunPlugin.d.cts.map +1 -0
  6. package/build/RunPlugin.d.mts +12 -0
  7. package/build/RunPlugin.d.mts.map +1 -0
  8. package/build/RunPlugin.js +203 -341
  9. package/build/RunPlugin.js.map +1 -1
  10. package/build/_virtual/_rolldown/runtime.cjs +23 -0
  11. package/build/context.cjs +2 -0
  12. package/build/context.d.cts +9 -0
  13. package/build/context.d.cts.map +1 -0
  14. package/build/context.d.mts +9 -0
  15. package/build/context.d.mts.map +1 -0
  16. package/build/context.js +2 -3
  17. package/build/index.cjs +38 -0
  18. package/build/index.cjs.map +1 -0
  19. package/build/index.d.cts +52 -0
  20. package/build/index.d.cts.map +1 -0
  21. package/build/index.d.mts +52 -0
  22. package/build/index.d.mts.map +1 -0
  23. package/build/index.js +25 -41
  24. package/build/index.js.map +1 -1
  25. package/build/run.cjs +77 -0
  26. package/build/run.cjs.map +1 -0
  27. package/build/run.d.cts +17 -0
  28. package/build/run.d.cts.map +1 -0
  29. package/build/run.d.mts +17 -0
  30. package/build/run.d.mts.map +1 -0
  31. package/build/run.js +52 -76
  32. package/build/run.js.map +1 -1
  33. package/build/seed.cjs +54 -0
  34. package/build/seed.cjs.map +1 -0
  35. package/build/{seed.d.ts → seed.d.cts} +9 -6
  36. package/build/seed.d.cts.map +1 -0
  37. package/build/seed.d.mts +35 -0
  38. package/build/seed.d.mts.map +1 -0
  39. package/build/seed.js +47 -53
  40. package/build/seed.js.map +1 -1
  41. package/build/toMatchEntity.cjs +94 -0
  42. package/build/toMatchEntity.cjs.map +1 -0
  43. package/build/toMatchEntity.d.cts +28 -0
  44. package/build/toMatchEntity.d.cts.map +1 -0
  45. package/build/toMatchEntity.d.mts +28 -0
  46. package/build/toMatchEntity.d.mts.map +1 -0
  47. package/build/toMatchEntity.js +73 -109
  48. package/build/toMatchEntity.js.map +1 -1
  49. package/package.json +33 -13
  50. package/build/RunPlugin.d.ts +0 -9
  51. package/build/RunPlugin.d.ts.map +0 -1
  52. package/build/context.d.ts +0 -6
  53. package/build/context.d.ts.map +0 -1
  54. package/build/context.js.map +0 -1
  55. package/build/index.d.ts +0 -42
  56. package/build/index.d.ts.map +0 -1
  57. package/build/run.d.ts +0 -15
  58. package/build/run.d.ts.map +0 -1
  59. package/build/seed.d.ts.map +0 -1
  60. package/build/toMatchEntity.d.ts +0 -27
  61. package/build/toMatchEntity.d.ts.map +0 -1
package/build/run.js CHANGED
@@ -1,96 +1,72 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.run = run;
4
- exports.makeRun = makeRun;
5
- exports.runEach = runEach;
6
- exports.makeRunEach = makeRunEach;
7
- exports.newContext = newContext;
8
- const joist_core_1 = require("joist-core");
9
- const joist_utils_1 = require("joist-utils");
10
- const RunPlugin_1 = require("./RunPlugin");
11
- // Used to allow non-mutating run... calls to be run in parallel.
1
+ import { RunPlugin } from "./RunPlugin.js";
2
+ import "./context.js";
3
+ import { EntityManager, isEntity } from "joist-core";
4
+ import { fail } from "joist-utils";
5
+ //#region src/run.ts
12
6
  let flushPromise;
13
7
  let flushEm;
14
8
  /** Runs the `fn` in a dedicated / non-test Unit of Work . */
15
9
  async function run(ctx, fn, contextFn = newContext) {
16
- const { em } = ctx;
17
- // Ensure any test data we've set up is flushed. If another invocation of `run` is already flushing, then we can just
18
- // wait on its promise. Otherwise, we will get flushLock errors.
19
- try {
20
- flushPromise ??= em.flush();
21
- flushEm ??= em;
22
- if (flushEm !== em)
23
- (0, joist_utils_1.fail)("cannot use run in parallel with multiple entity managers");
24
- await flushPromise;
25
- }
26
- finally {
27
- flushPromise = undefined;
28
- flushEm = undefined;
29
- }
30
- const newCtx = await contextFn(ctx);
31
- const plugin = new RunPlugin_1.RunPlugin(em);
32
- newCtx.em.addPlugin(plugin);
33
- const result = await fn(newCtx);
34
- // We expect `fn` (i.e. a resolver) to do its own UoW management, so don't flush.
35
- return mapResultToOriginalEm(em, result);
10
+ const { em } = ctx;
11
+ try {
12
+ flushPromise ??= em.flush();
13
+ flushEm ??= em;
14
+ if (flushEm !== em) fail("cannot use run in parallel with multiple entity managers");
15
+ await flushPromise;
16
+ } finally {
17
+ flushPromise = void 0;
18
+ flushEm = void 0;
19
+ }
20
+ const newCtx = await contextFn(ctx);
21
+ const plugin = new RunPlugin(em);
22
+ newCtx.em.addPlugin(plugin);
23
+ return mapResultToOriginalEm(em, await fn(newCtx));
36
24
  }
37
25
  /** Creates a `run` with a custom `newContext` method. */
38
26
  function makeRun(contextFn) {
39
- return (ctx, fn) => run(ctx, fn, contextFn);
27
+ return (ctx, fn) => run(ctx, fn, contextFn);
40
28
  }
41
29
  /** Runs the `fn` in a dedicated / non-test Unit of Work for each value in `values */
42
30
  async function runEach(ctx, valuesFn, fn, contextFn = newContext) {
43
- const { em } = ctx;
44
- // Ensure any test data we've setup is flushed
45
- await em.flush();
46
- const plugin = new RunPlugin_1.RunPlugin(em);
47
- const results = await Promise.all(valuesFn().map(async (value) => {
48
- const newCtx = await contextFn(ctx);
49
- newCtx.em.addPlugin(plugin);
50
- return fn(newCtx, value);
51
- }));
52
- return mapResultToOriginalEm(em, results);
31
+ const { em } = ctx;
32
+ await em.flush();
33
+ const plugin = new RunPlugin(em);
34
+ return mapResultToOriginalEm(em, await Promise.all(valuesFn().map(async (value) => {
35
+ const newCtx = await contextFn(ctx);
36
+ newCtx.em.addPlugin(plugin);
37
+ return fn(newCtx, value);
38
+ })));
53
39
  }
54
40
  /** Creates a `runEach` with a custom `newContext` method. */
55
41
  function makeRunEach(contextFn) {
56
- return (ctx, valuesFn, fn) => runEach(ctx, valuesFn, fn, contextFn);
42
+ return (ctx, valuesFn, fn) => runEach(ctx, valuesFn, fn, contextFn);
57
43
  }
58
44
  /** A default `newContext` that assumes `ctx.em` is where the `EntityManager` lives. */
59
45
  function newContext(ctx) {
60
- const { em } = ctx;
61
- const newCtx = { ...ctx };
62
- const newEm = new joist_core_1.EntityManager(newCtx, em.driver);
63
- Object.assign(newCtx, { em: newEm });
64
- return newCtx;
46
+ const { em } = ctx;
47
+ const newCtx = { ...ctx };
48
+ const newEm = new EntityManager(newCtx, em.driver);
49
+ Object.assign(newCtx, { em: newEm });
50
+ return newCtx;
65
51
  }
66
- /* Returns a copy of `result` with all entities replaced with their instance from the original `em`. We assume that
67
- * RunPlugin has been installed prior to this call, so any entities we encounter should be present in both ems. */
68
52
  function mapResultToOriginalEm(em, result) {
69
- if ((0, joist_core_1.isEntity)(result)) {
70
- return (em.findExistingInstance(result.idTagged) ?? (0, joist_utils_1.fail)(`Could not find entity ${result.idTagged}`));
71
- }
72
- else if (Array.isArray(result)) {
73
- return result.map((r) => mapResultToOriginalEm(em, r));
74
- }
75
- else if (result instanceof Map) {
76
- const map = new Map();
77
- result
78
- .entries()
79
- .forEach(([key, value]) => map.set(mapResultToOriginalEm(em, key), mapResultToOriginalEm(em, value)));
80
- return map;
81
- }
82
- else if (result instanceof Set) {
83
- const set = new Set();
84
- result.forEach((r) => set.add(mapResultToOriginalEm(em, r)));
85
- return set;
86
- }
87
- else if (typeof result === "object" && result?.constructor === Object) {
88
- const obj = {};
89
- Object.entries(result).forEach(([key, value]) => (obj[key] = mapResultToOriginalEm(em, value)));
90
- return obj;
91
- }
92
- else {
93
- return result;
94
- }
53
+ if (isEntity(result)) return em.findExistingInstance(result.idTagged) ?? fail(`Could not find entity ${result.idTagged}`);
54
+ else if (Array.isArray(result)) return result.map((r) => mapResultToOriginalEm(em, r));
55
+ else if (result instanceof Map) {
56
+ const map = /* @__PURE__ */ new Map();
57
+ result.entries().forEach(([key, value]) => map.set(mapResultToOriginalEm(em, key), mapResultToOriginalEm(em, value)));
58
+ return map;
59
+ } else if (result instanceof Set) {
60
+ const set = /* @__PURE__ */ new Set();
61
+ result.forEach((r) => set.add(mapResultToOriginalEm(em, r)));
62
+ return set;
63
+ } else if (typeof result === "object" && result?.constructor === Object) {
64
+ const obj = {};
65
+ Object.entries(result).forEach(([key, value]) => obj[key] = mapResultToOriginalEm(em, value));
66
+ return obj;
67
+ } else return result;
95
68
  }
69
+ //#endregion
70
+ export { makeRun, makeRunEach, newContext, run, runEach };
71
+
96
72
  //# sourceMappingURL=run.js.map
package/build/run.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"run.js","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"run.js","names":[],"sources":["../src/run.ts"],"sourcesContent":["import { EntityManager, isEntity } from \"joist-core\";\nimport { fail } from \"joist-utils\";\n\nimport { type Context } from \"./context.ts\";\nimport { RunPlugin } from \"./RunPlugin.ts\";\n\ntype MaybePromise<T> = T | Promise<T>;\n\nexport type ContextFn<C> = (ctx: C) => MaybePromise<C>;\n\n// Used to allow non-mutating run... calls to be run in parallel.\nlet flushPromise: Promise<any> | undefined;\nlet flushEm: EntityManager | undefined;\n\n/** Runs the `fn` in a dedicated / non-test Unit of Work . */\nexport async function run<C extends Context, T>(\n ctx: C,\n fn: (ctx: C) => MaybePromise<T>,\n contextFn: ContextFn<C> = newContext,\n): Promise<T> {\n const { em } = ctx;\n // Ensure any test data we've set up is flushed. If another invocation of `run` is already flushing, then we can just\n // wait on its promise. Otherwise, we will get flushLock errors.\n try {\n flushPromise ??= em.flush();\n flushEm ??= em;\n if (flushEm !== em) fail(\"cannot use run in parallel with multiple entity managers\");\n await flushPromise;\n } finally {\n flushPromise = undefined;\n flushEm = undefined;\n }\n const newCtx = await contextFn(ctx);\n const plugin = new RunPlugin(em);\n newCtx.em.addPlugin(plugin);\n const result = await fn(newCtx);\n // We expect `fn` (i.e. a resolver) to do its own UoW management, so don't flush.\n return mapResultToOriginalEm(em, result);\n}\n\n/** Creates a `run` with a custom `newContext` method. */\nexport function makeRun<C extends Context>(\n contextFn: ContextFn<C>,\n): <T>(ctx: C, fn: (ctx: C) => MaybePromise<T>) => Promise<T> {\n return (ctx, fn) => run(ctx, fn, contextFn);\n}\n\n/** Runs the `fn` in a dedicated / non-test Unit of Work for each value in `values */\nexport async function runEach<C extends Context, T, U>(\n ctx: C,\n valuesFn: () => U[],\n fn: (ctx: C, value: U) => MaybePromise<T>,\n contextFn: ContextFn<C> = newContext,\n): Promise<T[]> {\n const { em } = ctx;\n // Ensure any test data we've setup is flushed\n await em.flush();\n const plugin = new RunPlugin(em);\n const results = await Promise.all(\n valuesFn().map(async (value) => {\n const newCtx = await contextFn(ctx);\n newCtx.em.addPlugin(plugin);\n return fn(newCtx, value);\n }),\n );\n return mapResultToOriginalEm(em, results);\n}\n\n/** Creates a `runEach` with a custom `newContext` method. */\nexport function makeRunEach<C extends Context>(\n contextFn: ContextFn<C>,\n): <T, U>(ctx: C, valuesFn: () => U[], fn: (ctx: C, value: U) => MaybePromise<T>) => Promise<T[]> {\n return (ctx, valuesFn, fn) => runEach(ctx, valuesFn, fn, contextFn);\n}\n\n/** A default `newContext` that assumes `ctx.em` is where the `EntityManager` lives. */\nexport function newContext<C extends Context>(ctx: C): C {\n const { em } = ctx;\n const newCtx = { ...ctx };\n const newEm = new EntityManager(newCtx, em.driver);\n Object.assign(newCtx, { em: newEm });\n return newCtx;\n}\n\n/* Returns a copy of `result` with all entities replaced with their instance from the original `em`. We assume that\n * RunPlugin has been installed prior to this call, so any entities we encounter should be present in both ems. */\nfunction mapResultToOriginalEm<R>(em: EntityManager, result: R): R {\n if (isEntity(result)) {\n return (em.findExistingInstance(result.idTagged) ?? fail(`Could not find entity ${result.idTagged}`)) as R;\n } else if (Array.isArray(result)) {\n return result.map((r) => mapResultToOriginalEm(em, r)) as any;\n } else if (result instanceof Map) {\n const map = new Map<any, any>();\n result\n .entries()\n .forEach(([key, value]) => map.set(mapResultToOriginalEm(em, key), mapResultToOriginalEm(em, value)));\n return map as R;\n } else if (result instanceof Set) {\n const set = new Set<any>();\n result.forEach((r) => set.add(mapResultToOriginalEm(em, r)));\n return set as R;\n } else if (typeof result === \"object\" && result?.constructor === Object) {\n const obj = {} as any;\n Object.entries(result).forEach(([key, value]: [string, any]) => (obj[key] = mapResultToOriginalEm(em, value)));\n return obj;\n } else {\n return result;\n }\n}\n"],"mappings":";;;;;AAWA,IAAI;AACJ,IAAI;;AAGJ,eAAsB,IACpB,KACA,IACA,YAA0B,YACd;CACZ,MAAM,EAAE,OAAO;CAGf,IAAI;EACF,iBAAiB,GAAG,MAAM;EAC1B,YAAY;EACZ,IAAI,YAAY,IAAI,KAAK,0DAA0D;EACnF,MAAM;CACR,UAAU;EACR,eAAe,KAAA;EACf,UAAU,KAAA;CACZ;CACA,MAAM,SAAS,MAAM,UAAU,GAAG;CAClC,MAAM,SAAS,IAAI,UAAU,EAAE;CAC/B,OAAO,GAAG,UAAU,MAAM;CAG1B,OAAO,sBAAsB,IAAI,MAFZ,GAAG,MAAM,CAES;AACzC;;AAGA,SAAgB,QACd,WAC4D;CAC5D,QAAQ,KAAK,OAAO,IAAI,KAAK,IAAI,SAAS;AAC5C;;AAGA,eAAsB,QACpB,KACA,UACA,IACA,YAA0B,YACZ;CACd,MAAM,EAAE,OAAO;CAEf,MAAM,GAAG,MAAM;CACf,MAAM,SAAS,IAAI,UAAU,EAAE;CAQ/B,OAAO,sBAAsB,IAAI,MAPX,QAAQ,IAC5B,SAAS,CAAC,CAAC,IAAI,OAAO,UAAU;EAC9B,MAAM,SAAS,MAAM,UAAU,GAAG;EAClC,OAAO,GAAG,UAAU,MAAM;EAC1B,OAAO,GAAG,QAAQ,KAAK;CACzB,CAAC,CACH,CACwC;AAC1C;;AAGA,SAAgB,YACd,WACgG;CAChG,QAAQ,KAAK,UAAU,OAAO,QAAQ,KAAK,UAAU,IAAI,SAAS;AACpE;;AAGA,SAAgB,WAA8B,KAAW;CACvD,MAAM,EAAE,OAAO;CACf,MAAM,SAAS,EAAE,GAAG,IAAI;CACxB,MAAM,QAAQ,IAAI,cAAc,QAAQ,GAAG,MAAM;CACjD,OAAO,OAAO,QAAQ,EAAE,IAAI,MAAM,CAAC;CACnC,OAAO;AACT;AAIA,SAAS,sBAAyB,IAAmB,QAAc;CACjE,IAAI,SAAS,MAAM,GACjB,OAAQ,GAAG,qBAAqB,OAAO,QAAQ,KAAK,KAAK,yBAAyB,OAAO,UAAU;MAC9F,IAAI,MAAM,QAAQ,MAAM,GAC7B,OAAO,OAAO,KAAK,MAAM,sBAAsB,IAAI,CAAC,CAAC;MAChD,IAAI,kBAAkB,KAAK;EAChC,MAAM,sBAAM,IAAI,IAAc;EAC9B,OACG,QAAQ,CAAC,CACT,SAAS,CAAC,KAAK,WAAW,IAAI,IAAI,sBAAsB,IAAI,GAAG,GAAG,sBAAsB,IAAI,KAAK,CAAC,CAAC;EACtG,OAAO;CACT,OAAO,IAAI,kBAAkB,KAAK;EAChC,MAAM,sBAAM,IAAI,IAAS;EACzB,OAAO,SAAS,MAAM,IAAI,IAAI,sBAAsB,IAAI,CAAC,CAAC,CAAC;EAC3D,OAAO;CACT,OAAO,IAAI,OAAO,WAAW,YAAY,QAAQ,gBAAgB,QAAQ;EACvE,MAAM,MAAM,CAAC;EACb,OAAO,QAAQ,MAAM,CAAC,CAAC,SAAS,CAAC,KAAK,WAA2B,IAAI,OAAO,sBAAsB,IAAI,KAAK,CAAE;EAC7G,OAAO;CACT,OACE,OAAO;AAEX"}
package/build/seed.cjs ADDED
@@ -0,0 +1,54 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_runtime = require("./_virtual/_rolldown/runtime.cjs");
3
+ let joist_core = require("joist-core");
4
+ require("dotenv/config");
5
+ let pg = require("pg");
6
+ pg = require_runtime.__toESM(pg, 1);
7
+ //#region src/seed.ts
8
+ /**
9
+ * Allows easily seeding your local database with test data from the factories.
10
+ *
11
+ * This will delete all data in the database, so is restricted to only `local` and
12
+ * `test` `NODE_ENV`s.
13
+ *
14
+ * We currently make a lot of assumptions:
15
+ *
16
+ * - That the project is using postgres (all Joist projects do atm)
17
+ * - That the project does not have a context, or at least does not require a context to seed
18
+ *
19
+ * If any of these assumptions are not true, you can copy this code into your project.
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * import { seed } from "joist-test-utils";
24
+ * import { PostgresDriver } from "joist-orm";
25
+ *
26
+ * seed({ createDriver: (pool) => new PostgresDriver(pool) }, async (em) => {
27
+ * // seed your data here
28
+ * });
29
+ * ```
30
+ */
31
+ function seed(config, fn) {
32
+ const env = process.env.NODE_ENV;
33
+ if (env !== "local" && env !== "test") throw new Error("seed will only run with NODE_ENV=local or NODE_ENV=test because it resets the database");
34
+ const pool = new pg.default.Pool((0, joist_core.newPgConnectionConfig)());
35
+ async function seed() {
36
+ await pool.query("SELECT flush_database()");
37
+ const driver = config.createDriver(pool);
38
+ const em = new joist_core.EntityManager({}, { driver });
39
+ await fn(em);
40
+ await em.flush();
41
+ }
42
+ seed().then(async () => {
43
+ console.log("Seeded!");
44
+ await pool.end();
45
+ }).catch(async (err) => {
46
+ console.error(err);
47
+ await pool.end();
48
+ process.exit(1);
49
+ });
50
+ }
51
+ //#endregion
52
+ exports.seed = seed;
53
+
54
+ //# sourceMappingURL=seed.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seed.cjs","names":["newPgConnectionConfig","EntityManager"],"sources":["../src/seed.ts"],"sourcesContent":["import \"dotenv/config\";\nimport { type Driver, EntityManager, newPgConnectionConfig } from \"joist-core\";\nimport pg from \"pg\";\n\nexport interface SeedConfig {\n /** A factory function to create a Driver from a pg Pool. */\n createDriver: (pool: pg.Pool) => Driver;\n}\n\n/**\n * Allows easily seeding your local database with test data from the factories.\n *\n * This will delete all data in the database, so is restricted to only `local` and\n * `test` `NODE_ENV`s.\n *\n * We currently make a lot of assumptions:\n *\n * - That the project is using postgres (all Joist projects do atm)\n * - That the project does not have a context, or at least does not require a context to seed\n *\n * If any of these assumptions are not true, you can copy this code into your project.\n *\n * @example\n * ```ts\n * import { seed } from \"joist-test-utils\";\n * import { PostgresDriver } from \"joist-orm\";\n *\n * seed({ createDriver: (pool) => new PostgresDriver(pool) }, async (em) => {\n * // seed your data here\n * });\n * ```\n */\nexport function seed<E extends EntityManager = EntityManager>(config: SeedConfig, fn: (em: E) => Promise<void>): void {\n const env = process.env.NODE_ENV;\n if (env !== \"local\" && env !== \"test\") {\n throw new Error(\"seed will only run with NODE_ENV=local or NODE_ENV=test because it resets the database\");\n }\n\n const pool = new pg.Pool(newPgConnectionConfig());\n\n async function seed() {\n await pool.query(\"SELECT flush_database()\");\n const driver = config.createDriver(pool);\n const em = new EntityManager({}, { driver }) as E;\n await fn(em);\n await em.flush();\n }\n\n seed()\n .then(async () => {\n console.log(\"Seeded!\");\n await pool.end();\n })\n .catch(async (err) => {\n console.error(err);\n await pool.end();\n process.exit(1);\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,SAAgB,KAA8C,QAAoB,IAAoC;CACpH,MAAM,MAAM,QAAQ,IAAI;CACxB,IAAI,QAAQ,WAAW,QAAQ,QAC7B,MAAM,IAAI,MAAM,wFAAwF;CAG1G,MAAM,OAAO,IAAI,GAAA,QAAG,MAAA,GAAKA,WAAAA,sBAAAA,CAAsB,CAAC;CAEhD,eAAe,OAAO;EACpB,MAAM,KAAK,MAAM,yBAAyB;EAC1C,MAAM,SAAS,OAAO,aAAa,IAAI;EACvC,MAAM,KAAK,IAAIC,WAAAA,cAAc,CAAC,GAAG,EAAE,OAAO,CAAC;EAC3C,MAAM,GAAG,EAAE;EACX,MAAM,GAAG,MAAM;CACjB;CAEA,KAAK,CAAC,CACH,KAAK,YAAY;EAChB,QAAQ,IAAI,SAAS;EACrB,MAAM,KAAK,IAAI;CACjB,CAAC,CAAC,CACD,MAAM,OAAO,QAAQ;EACpB,QAAQ,MAAM,GAAG;EACjB,MAAM,KAAK,IAAI;EACf,QAAQ,KAAK,CAAC;CAChB,CAAC;AACL"}
@@ -1,9 +1,10 @@
1
- import "dotenv/config";
2
1
  import { Driver, EntityManager } from "joist-core";
2
+ import "dotenv/config";
3
3
  import pg from "pg";
4
- export interface SeedConfig {
5
- /** A factory function to create a Driver from a pg Pool. */
6
- createDriver: (pool: pg.Pool) => Driver;
4
+ //#region src/seed.d.ts
5
+ interface SeedConfig {
6
+ /** A factory function to create a Driver from a pg Pool. */
7
+ createDriver: (pool: pg.Pool) => Driver;
7
8
  }
8
9
  /**
9
10
  * Allows easily seeding your local database with test data from the factories.
@@ -28,5 +29,7 @@ export interface SeedConfig {
28
29
  * });
29
30
  * ```
30
31
  */
31
- export declare function seed<E extends EntityManager = EntityManager>(config: SeedConfig, fn: (em: E) => Promise<void>): void;
32
- //# sourceMappingURL=seed.d.ts.map
32
+ declare function seed<E extends EntityManager = EntityManager>(config: SeedConfig, fn: (em: E) => Promise<void>): void;
33
+ //#endregion
34
+ export { SeedConfig, seed };
35
+ //# sourceMappingURL=seed.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seed.d.cts","names":[],"sources":["../src/seed.ts"],"mappings":";;;;UAIiB;;EAEf,eAAe,MAAM,GAAG,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;iBA0BnB,KAAK,UAAU,gBAAgB,eAAe,QAAQ,YAAY,KAAK,IAAI,MAAM"}
@@ -0,0 +1,35 @@
1
+ import { Driver, EntityManager } from "joist-core";
2
+ import "dotenv/config";
3
+ import pg from "pg";
4
+ //#region src/seed.d.ts
5
+ interface SeedConfig {
6
+ /** A factory function to create a Driver from a pg Pool. */
7
+ createDriver: (pool: pg.Pool) => Driver;
8
+ }
9
+ /**
10
+ * Allows easily seeding your local database with test data from the factories.
11
+ *
12
+ * This will delete all data in the database, so is restricted to only `local` and
13
+ * `test` `NODE_ENV`s.
14
+ *
15
+ * We currently make a lot of assumptions:
16
+ *
17
+ * - That the project is using postgres (all Joist projects do atm)
18
+ * - That the project does not have a context, or at least does not require a context to seed
19
+ *
20
+ * If any of these assumptions are not true, you can copy this code into your project.
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * import { seed } from "joist-test-utils";
25
+ * import { PostgresDriver } from "joist-orm";
26
+ *
27
+ * seed({ createDriver: (pool) => new PostgresDriver(pool) }, async (em) => {
28
+ * // seed your data here
29
+ * });
30
+ * ```
31
+ */
32
+ declare function seed<E extends EntityManager = EntityManager>(config: SeedConfig, fn: (em: E) => Promise<void>): void;
33
+ //#endregion
34
+ export { SeedConfig, seed };
35
+ //# sourceMappingURL=seed.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seed.d.mts","names":[],"sources":["../src/seed.ts"],"mappings":";;;;UAIiB;;EAEf,eAAe,MAAM,GAAG,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;iBA0BnB,KAAK,UAAU,gBAAgB,eAAe,QAAQ,YAAY,KAAK,IAAI,MAAM"}
package/build/seed.js CHANGED
@@ -1,57 +1,51 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.seed = seed;
7
- require("dotenv/config");
8
- const joist_core_1 = require("joist-core");
9
- const pg_1 = __importDefault(require("pg"));
1
+ import { EntityManager, newPgConnectionConfig } from "joist-core";
2
+ import "dotenv/config";
3
+ import pg from "pg";
4
+ //#region src/seed.ts
10
5
  /**
11
- * Allows easily seeding your local database with test data from the factories.
12
- *
13
- * This will delete all data in the database, so is restricted to only `local` and
14
- * `test` `NODE_ENV`s.
15
- *
16
- * We currently make a lot of assumptions:
17
- *
18
- * - That the project is using postgres (all Joist projects do atm)
19
- * - That the project does not have a context, or at least does not require a context to seed
20
- *
21
- * If any of these assumptions are not true, you can copy this code into your project.
22
- *
23
- * @example
24
- * ```ts
25
- * import { seed } from "joist-test-utils";
26
- * import { PostgresDriver } from "joist-orm";
27
- *
28
- * seed({ createDriver: (pool) => new PostgresDriver(pool) }, async (em) => {
29
- * // seed your data here
30
- * });
31
- * ```
32
- */
6
+ * Allows easily seeding your local database with test data from the factories.
7
+ *
8
+ * This will delete all data in the database, so is restricted to only `local` and
9
+ * `test` `NODE_ENV`s.
10
+ *
11
+ * We currently make a lot of assumptions:
12
+ *
13
+ * - That the project is using postgres (all Joist projects do atm)
14
+ * - That the project does not have a context, or at least does not require a context to seed
15
+ *
16
+ * If any of these assumptions are not true, you can copy this code into your project.
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * import { seed } from "joist-test-utils";
21
+ * import { PostgresDriver } from "joist-orm";
22
+ *
23
+ * seed({ createDriver: (pool) => new PostgresDriver(pool) }, async (em) => {
24
+ * // seed your data here
25
+ * });
26
+ * ```
27
+ */
33
28
  function seed(config, fn) {
34
- const env = process.env.NODE_ENV;
35
- if (env !== "local" && env !== "test") {
36
- throw new Error("seed will only run with NODE_ENV=local or NODE_ENV=test because it resets the database");
37
- }
38
- const pool = new pg_1.default.Pool((0, joist_core_1.newPgConnectionConfig)());
39
- async function seed() {
40
- await pool.query("SELECT flush_database()");
41
- const driver = config.createDriver(pool);
42
- const em = new joist_core_1.EntityManager({}, { driver });
43
- await fn(em);
44
- await em.flush();
45
- }
46
- seed()
47
- .then(async () => {
48
- console.log("Seeded!");
49
- await pool.end();
50
- })
51
- .catch(async (err) => {
52
- console.error(err);
53
- await pool.end();
54
- process.exit(1);
55
- });
29
+ const env = process.env.NODE_ENV;
30
+ if (env !== "local" && env !== "test") throw new Error("seed will only run with NODE_ENV=local or NODE_ENV=test because it resets the database");
31
+ const pool = new pg.Pool(newPgConnectionConfig());
32
+ async function seed() {
33
+ await pool.query("SELECT flush_database()");
34
+ const driver = config.createDriver(pool);
35
+ const em = new EntityManager({}, { driver });
36
+ await fn(em);
37
+ await em.flush();
38
+ }
39
+ seed().then(async () => {
40
+ console.log("Seeded!");
41
+ await pool.end();
42
+ }).catch(async (err) => {
43
+ console.error(err);
44
+ await pool.end();
45
+ process.exit(1);
46
+ });
56
47
  }
48
+ //#endregion
49
+ export { seed };
50
+
57
51
  //# sourceMappingURL=seed.js.map
package/build/seed.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"seed.js","sourceRoot":"","sources":["../src/seed.ts"],"names":[],"mappings":";;;;;AAgCA,oBA0BC;AA1DD,yBAAuB;AACvB,2CAA0E;AAC1E,4CAAoB;AAOpB;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,SAAgB,IAAI,CAA0C,MAAkB,EAAE,EAA4B;IAC5G,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;IACjC,IAAI,GAAG,KAAK,OAAO,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,wFAAwF,CAAC,CAAC;IAC5G,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,YAAE,CAAC,IAAI,CAAC,IAAA,kCAAqB,GAAE,CAAC,CAAC;IAElD,KAAK,UAAU,IAAI;QACjB,MAAM,IAAI,CAAC,KAAK,CAAC,yBAAyB,CAAC,CAAC;QAC5C,MAAM,MAAM,GAAG,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;QACzC,MAAM,EAAE,GAAG,IAAI,0BAAa,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,CAAM,CAAC;QAClD,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;QACb,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC;IACnB,CAAC;IAED,IAAI,EAAE;SACH,IAAI,CAAC,KAAK,IAAI,EAAE;QACf,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACvB,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC;IACnB,CAAC,CAAC;SACD,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACnB,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACnB,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC;QACjB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACP,CAAC"}
1
+ {"version":3,"file":"seed.js","names":[],"sources":["../src/seed.ts"],"sourcesContent":["import \"dotenv/config\";\nimport { type Driver, EntityManager, newPgConnectionConfig } from \"joist-core\";\nimport pg from \"pg\";\n\nexport interface SeedConfig {\n /** A factory function to create a Driver from a pg Pool. */\n createDriver: (pool: pg.Pool) => Driver;\n}\n\n/**\n * Allows easily seeding your local database with test data from the factories.\n *\n * This will delete all data in the database, so is restricted to only `local` and\n * `test` `NODE_ENV`s.\n *\n * We currently make a lot of assumptions:\n *\n * - That the project is using postgres (all Joist projects do atm)\n * - That the project does not have a context, or at least does not require a context to seed\n *\n * If any of these assumptions are not true, you can copy this code into your project.\n *\n * @example\n * ```ts\n * import { seed } from \"joist-test-utils\";\n * import { PostgresDriver } from \"joist-orm\";\n *\n * seed({ createDriver: (pool) => new PostgresDriver(pool) }, async (em) => {\n * // seed your data here\n * });\n * ```\n */\nexport function seed<E extends EntityManager = EntityManager>(config: SeedConfig, fn: (em: E) => Promise<void>): void {\n const env = process.env.NODE_ENV;\n if (env !== \"local\" && env !== \"test\") {\n throw new Error(\"seed will only run with NODE_ENV=local or NODE_ENV=test because it resets the database\");\n }\n\n const pool = new pg.Pool(newPgConnectionConfig());\n\n async function seed() {\n await pool.query(\"SELECT flush_database()\");\n const driver = config.createDriver(pool);\n const em = new EntityManager({}, { driver }) as E;\n await fn(em);\n await em.flush();\n }\n\n seed()\n .then(async () => {\n console.log(\"Seeded!\");\n await pool.end();\n })\n .catch(async (err) => {\n console.error(err);\n await pool.end();\n process.exit(1);\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,SAAgB,KAA8C,QAAoB,IAAoC;CACpH,MAAM,MAAM,QAAQ,IAAI;CACxB,IAAI,QAAQ,WAAW,QAAQ,QAC7B,MAAM,IAAI,MAAM,wFAAwF;CAG1G,MAAM,OAAO,IAAI,GAAG,KAAK,sBAAsB,CAAC;CAEhD,eAAe,OAAO;EACpB,MAAM,KAAK,MAAM,yBAAyB;EAC1C,MAAM,SAAS,OAAO,aAAa,IAAI;EACvC,MAAM,KAAK,IAAI,cAAc,CAAC,GAAG,EAAE,OAAO,CAAC;EAC3C,MAAM,GAAG,EAAE;EACX,MAAM,GAAG,MAAM;CACjB;CAEA,KAAK,CAAC,CACH,KAAK,YAAY;EAChB,QAAQ,IAAI,SAAS;EACrB,MAAM,KAAK,IAAI;CACjB,CAAC,CAAC,CACD,MAAM,OAAO,QAAQ;EACpB,QAAQ,MAAM,GAAG;EACjB,MAAM,KAAK,IAAI;EACf,QAAQ,KAAK,CAAC;CAChB,CAAC;AACL"}
@@ -0,0 +1,94 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ require("./index.cjs");
3
+ let joist_core = require("joist-core");
4
+ let joist_utils = require("joist-utils");
5
+ //#region src/toMatchEntity.ts
6
+ const isVitest = typeof process !== "undefined" && process.env?.VITEST === "true";
7
+ /**
8
+ * Provides convenient `toMatchObject`-style matching for Joist entities.
9
+ *
10
+ * The biggest differences over `toMatchObject` are:
11
+ *
12
+ * - We un-wrap relations like m2o/o2m to just the underlying object/arrays, to make
13
+ * asserting against the entities look like asserting against POJOs
14
+ * - We prune the expected/actual values to be as minimal as possible, to avoid Jest
15
+ * recursively crawling into connection pools or other misc non-useful things in the diffs
16
+ */
17
+ function toMatchEntity(actual, expected) {
18
+ const matchers = isVitest ? void 0 : globalThis[Symbol.for("$$jest-matchers-object")]?.matchers;
19
+ const cleanExpected = deepClone(expected);
20
+ const cleanActual = deepClone(deepMirror(cleanExpected, actual));
21
+ if (matchers) {
22
+ if (typeof cleanActual !== "object") return matchers.toEqual.call(this, cleanActual, cleanExpected);
23
+ else return matchers.toMatchObject.call(this, cleanActual, cleanExpected);
24
+ } else if (isVitest) return {
25
+ pass: this.equals(cleanActual, cleanExpected, this.customTesters),
26
+ message: () => {
27
+ const hint = this.utils.matcherHint("toMatchEntity", void 0, void 0, { isNot: this.isNot });
28
+ const diff = this.utils.diff(cleanExpected, cleanActual);
29
+ return diff ? `${hint}\n\n${diff}` : hint;
30
+ }
31
+ };
32
+ else return {
33
+ pass: this.equals(cleanActual, cleanExpected),
34
+ message: () => this.utils.matcherHint("toMatchEntity", cleanActual, cleanExpected)
35
+ };
36
+ }
37
+ function maybeTestId(maybeEntity) {
38
+ return (0, joist_core.isEntity)(maybeEntity) ? getTestId(maybeEntity.em, maybeEntity) : maybeEntity;
39
+ }
40
+ /** Returns either the persisted id or `tag#<offset-in-EntityManager>`. */
41
+ function getTestId(_, entity) {
42
+ return entity.toTaggedString();
43
+ }
44
+ /**
45
+ * Make a "new actual" based on the subset shape of expected.
46
+ *
47
+ * We don't have cycle detection, but that should be fine b/c the user types
48
+ * out the expected as an object literal, so it shouldn't have cycles
49
+ */
50
+ function deepMirror(expected, actual) {
51
+ if (!(0, joist_utils.isPlainObject)(expected) && !Array.isArray(expected)) return actual;
52
+ if (!(0, joist_core.isDefined)(actual)) return actual;
53
+ const subset = Array.isArray(expected) ? [] : {};
54
+ const keys = Array.isArray(actual) ? Object.keys(actual) : Object.keys(expected);
55
+ for (const key of keys) if (key in actual) subset[key] = deepMirror(expected[key], maybeGetRelation(actual[key]));
56
+ return subset;
57
+ }
58
+ /** Make a clone of `obj`, but only recurse into POJOs and Arrays, and replace entities if we find them. */
59
+ function deepClone(obj, map = /* @__PURE__ */ new WeakMap()) {
60
+ if (obj instanceof joist_core.BaseEntity) return maybeTestId(obj);
61
+ else if (obj && typeof obj === "object" && ((0, joist_utils.isPlainObject)(obj) || Array.isArray(obj))) {
62
+ if (map.has(obj)) return map.get(obj);
63
+ const result = Array.isArray(obj) ? [] : {};
64
+ map.set(obj, result);
65
+ Object.assign(result, ...Object.keys(obj).map((key) => ({ [key]: deepClone(obj[key], map) })));
66
+ return result;
67
+ } else return obj;
68
+ }
69
+ /** Flattens/simplifies relations into their actual value, i.e. "calls get". */
70
+ function maybeGetRelation(actualValue) {
71
+ if ((0, joist_core.isReference)(actualValue) || (0, joist_core.isCollection)(actualValue) || (0, joist_core.isProperty)(actualValue) || (0, joist_core.isReactiveField)(actualValue) || (0, joist_core.isAsyncProperty)(actualValue)) return getWithSoftDeleted(actualValue);
72
+ return actualValue;
73
+ }
74
+ /**
75
+ * Adds back soft-deleted to `.get`.
76
+ *
77
+ * In Joist, `.get` filters both soft and hard deleted entities, because `.get`
78
+ * is likely used by real business logic that doesn't want to see either.
79
+ *
80
+ * However, in tests with `toMatchEntity`, we take a stronger stance that the
81
+ * developer needs to know "this is _soft_ deleted and not hard deleted", so
82
+ * we add-back soft deleted entities.
83
+ *
84
+ * We do this by calling `getWithDeleted`, which returns all hard and soft
85
+ * deleted entities, and then filtering out the hard deletes.
86
+ */
87
+ function getWithSoftDeleted(relation) {
88
+ const r = "getWithDeleted" in relation ? relation.getWithDeleted : relation.get;
89
+ return Array.isArray(r) ? r.filter((e) => !e.isDeletedEntity) : r;
90
+ }
91
+ //#endregion
92
+ exports.toMatchEntity = toMatchEntity;
93
+
94
+ //# sourceMappingURL=toMatchEntity.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toMatchEntity.cjs","names":["isEntity","isPlainObject","isDefined","BaseEntity","isReference","isCollection","isProperty","isReactiveField","isAsyncProperty"],"sources":["../src/toMatchEntity.ts"],"sourcesContent":["import {\n BaseEntity,\n type Entity,\n type EntityManager,\n type Property,\n type ReadOnlyCollection,\n type Reference,\n isAsyncProperty,\n isCollection,\n isDefined,\n isEntity,\n isProperty,\n isReactiveField,\n isReference,\n} from \"joist-core\";\nimport { isPlainObject } from \"joist-utils\";\n\nimport { type CustomMatcherResult } from \"./index.ts\";\n\n// Vitest also populates the `$$jest-matchers-object` symbol, but the matchers it stores there are\n// chai-based and assume `this` is a chai Assertion (with a `this.assert` method), which our custom\n// matcher's `this` context is not--so calling them throws `this.assert is not a function`. Only take\n// the Jest fast-path under actual Jest, and route Vitest through the `this.equals` path below (like Bun).\nconst isVitest = typeof process !== \"undefined\" && process.env?.VITEST === \"true\";\n\n/**\n * Provides convenient `toMatchObject`-style matching for Joist entities.\n *\n * The biggest differences over `toMatchObject` are:\n *\n * - We un-wrap relations like m2o/o2m to just the underlying object/arrays, to make\n * asserting against the entities look like asserting against POJOs\n * - We prune the expected/actual values to be as minimal as possible, to avoid Jest\n * recursively crawling into connection pools or other misc non-useful things in the diffs\n */\nexport function toMatchEntity<T>(this: any, actual: unknown, expected: MatchedEntity<T>): CustomMatcherResult {\n // This module can load before Jest initializes its matcher state, so read the global when invoked.\n const matchers = isVitest ? undefined : (globalThis as any)[Symbol.for(\"$$jest-matchers-object\")]?.matchers;\n // We're given expected, which is an object literal of what the user wants to match\n // against (some intermixed POJOs & entities) and actual (which similarly could be\n // intermixed POJOs & entities, but will likely be more sprawling because it's the\n // full result, and not the subset we're asserting against).\n //\n // Given this, first make a safe clone of `expected`, i.e. turn entities into strings.\n // After that, use `expected` as a template to pick keys out of actual. Finally, do the\n // same \"clean clone\" of actual, and compare the two.\n const cleanExpected = deepClone(expected);\n const cleanActual = deepClone(deepMirror(cleanExpected, actual));\n\n // Watch for `expect(someAuthor).toMatchEntity(a1)` as we'll turn `someAuthor` into \"a:1\",\n // so can't use toMatchObject anymore.\n if (matchers) {\n // this is jest\n if (typeof cleanActual !== \"object\") {\n // @ts-ignore\n return matchers.toEqual.call(this, cleanActual, cleanExpected);\n } else {\n // @ts-ignore\n return matchers.toMatchObject.call(this, cleanActual, cleanExpected);\n }\n } else if (isVitest) {\n // `deepMirror` already pruned `cleanActual` down to the subset of keys that `cleanExpected`\n // declares, so a full `this.equals` deep-equality behaves like Jest's `toMatchObject`. We pass\n // `this.customTesters` so `areEntitiesEqual` still applies. Building the diff into `message`\n // (like Jest's `toMatchObject`) keeps the failure output readable both in the terminal and in\n // `toThrowErrorMatchingInlineSnapshot`, instead of the default `expect([object Object])...`.\n const pass = this.equals(cleanActual, cleanExpected, this.customTesters);\n return {\n pass,\n message: () => {\n const hint = this.utils.matcherHint(\"toMatchEntity\", undefined, undefined, { isNot: this.isNot });\n const diff = this.utils.diff(cleanExpected, cleanActual);\n return diff ? `${hint}\\n\\n${diff}` : hint;\n },\n };\n } else {\n // This is Bun, whose matcher `this.utils` only exposes `matcherHint`.\n return {\n pass: this.equals(cleanActual, cleanExpected),\n message: () => this.utils.matcherHint(\"toMatchEntity\", cleanActual, cleanExpected),\n };\n }\n}\n\nfunction maybeTestId(maybeEntity: any): any {\n return isEntity(maybeEntity) ? getTestId(maybeEntity.em, maybeEntity) : maybeEntity;\n}\n\n/** Returns either the persisted id or `tag#<offset-in-EntityManager>`. */\nfunction getTestId(_: EntityManager, entity: Entity): string {\n return entity.toTaggedString();\n}\n\n/**\n * Given a Joist entity `T`, \"flattens out\" the Reference/Collections.\n *\n * I.e. so that you can `toMatchEntity({ otherEntity: { name: \"foo\" } })` even though\n * `otherEntity` is technically a joist `Reference` or `Collection`.\n *\n * We allow `| null` so that `toMatchEntity` can work against optional fields\n * that are returned from GraphQL object resolvers.\n */\nexport type MatchedEntity<T> =\n T extends ReadonlyArray<infer U>\n ? ReadonlyArray<MatchedEntity<U>>\n :\n | undefined\n | T\n | {\n [K in keyof T]?: T[K] extends Reference<any, infer U, any>\n ? MatchedEntity<U> | U\n : T[K] extends ReadOnlyCollection<any, infer U>\n ? Array<MatchedEntity<U> | U>\n : T[K] extends Property<any, infer V>\n ? V\n : T[K] extends Entity | null | undefined\n ? MatchedEntity<T[K]> | T[K] | null | undefined\n : T[K] extends ReadonlyArray<(infer U) | undefined>\n ? readonly (MatchedEntity<U> | U | undefined)[]\n : T[K] extends ReadonlyArray<infer U> | null\n ? readonly (MatchedEntity<U> | U | null)[]\n : // We recurse similar to a DeepPartial\n MatchedEntity<T[K]> | null;\n };\n\n/**\n * Make a \"new actual\" based on the subset shape of expected.\n *\n * We don't have cycle detection, but that should be fine b/c the user types\n * out the expected as an object literal, so it shouldn't have cycles\n */\nfunction deepMirror(expected: any, actual: any): any {\n // If we've hit a point where the `expected` input is not a POJO object literal/array\n // declaring the subset of the shape to assert against, just return actual as-is.\n // This might be a Date (safe) or an Entity (unsafe) or something else, but if so it will\n // get cleaned up by the `deepClone` pass.\n if (!isPlainObject(expected) && !Array.isArray(expected)) return actual;\n if (!isDefined(actual)) return actual;\n // Make a new actual that is a subset that matches expected\n const subset: any = Array.isArray(expected) ? [] : ({} as any);\n // If both are arrays, fill out the whole array\n const keys = Array.isArray(actual) ? Object.keys(actual) : Object.keys(expected);\n for (const key of keys) {\n if (key in actual) {\n // Even if actualValue is an Entity, if expected has a key drilling in to it, we want to object literal-ize it\n subset[key] = deepMirror((expected as any)[key], maybeGetRelation(actual[key]));\n }\n }\n return subset;\n}\n\n/** Make a clone of `obj`, but only recurse into POJOs and Arrays, and replace entities if we find them. */\nfunction deepClone(obj: unknown, map = new WeakMap()): unknown {\n if (obj instanceof BaseEntity) {\n return maybeTestId(obj);\n } else if (obj && typeof obj === \"object\" && (isPlainObject(obj) || Array.isArray(obj))) {\n if (map.has(obj)) return map.get(obj);\n const result = Array.isArray(obj) ? [] : {};\n map.set(obj, result);\n Object.assign(result, ...Object.keys(obj).map((key) => ({ [key]: deepClone((obj as any)[key], map) })));\n return result;\n } else {\n return obj;\n }\n}\n\n/** Flattens/simplifies relations into their actual value, i.e. \"calls get\". */\nfunction maybeGetRelation(actualValue: unknown): unknown {\n if (\n isReference(actualValue) ||\n isCollection(actualValue) ||\n isProperty(actualValue) ||\n isReactiveField(actualValue) ||\n // hasAsyncPropertys will work if they've been already loaded, so let the user try it\n isAsyncProperty(actualValue)\n ) {\n return getWithSoftDeleted(actualValue);\n }\n return actualValue;\n}\n\n/**\n * Adds back soft-deleted to `.get`.\n *\n * In Joist, `.get` filters both soft and hard deleted entities, because `.get`\n * is likely used by real business logic that doesn't want to see either.\n *\n * However, in tests with `toMatchEntity`, we take a stronger stance that the\n * developer needs to know \"this is _soft_ deleted and not hard deleted\", so\n * we add-back soft deleted entities.\n *\n * We do this by calling `getWithDeleted`, which returns all hard and soft\n * deleted entities, and then filtering out the hard deletes.\n */\nfunction getWithSoftDeleted(relation: any): any {\n const r = \"getWithDeleted\" in relation ? relation.getWithDeleted : relation.get;\n return Array.isArray(r) ? r.filter((e: any) => !e.isDeletedEntity) : r;\n}\n"],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import { CustomMatcherResult } from "./index.cjs";
2
+ import { Entity, Property, ReadOnlyCollection, Reference } from "joist-core";
3
+ //#region src/toMatchEntity.d.ts
4
+ /**
5
+ * Provides convenient `toMatchObject`-style matching for Joist entities.
6
+ *
7
+ * The biggest differences over `toMatchObject` are:
8
+ *
9
+ * - We un-wrap relations like m2o/o2m to just the underlying object/arrays, to make
10
+ * asserting against the entities look like asserting against POJOs
11
+ * - We prune the expected/actual values to be as minimal as possible, to avoid Jest
12
+ * recursively crawling into connection pools or other misc non-useful things in the diffs
13
+ */
14
+ declare function toMatchEntity<T>(this: any, actual: unknown, expected: MatchedEntity<T>): CustomMatcherResult;
15
+ /**
16
+ * Given a Joist entity `T`, "flattens out" the Reference/Collections.
17
+ *
18
+ * I.e. so that you can `toMatchEntity({ otherEntity: { name: "foo" } })` even though
19
+ * `otherEntity` is technically a joist `Reference` or `Collection`.
20
+ *
21
+ * We allow `| null` so that `toMatchEntity` can work against optional fields
22
+ * that are returned from GraphQL object resolvers.
23
+ */
24
+ type MatchedEntity<T> = T extends ReadonlyArray<infer U> ? ReadonlyArray<MatchedEntity<U>> : undefined | T | { [K in keyof T]?: T[K] extends Reference<any, infer U, any> ? MatchedEntity<U> | U : T[K] extends ReadOnlyCollection<any, infer U> ? Array<MatchedEntity<U> | U> : T[K] extends Property<any, infer V> ? V : T[K] extends Entity | null | undefined ? MatchedEntity<T[K]> | T[K] | null | undefined : T[K] extends ReadonlyArray<(infer U) | undefined> ? readonly (MatchedEntity<U> | U | undefined)[] : T[K] extends ReadonlyArray<infer U> | null ? readonly (MatchedEntity<U> | U | null)[] // We recurse similar to a DeepPartial
25
+ : MatchedEntity<T[K]> | null; };
26
+ //#endregion
27
+ export { MatchedEntity, toMatchEntity };
28
+ //# sourceMappingURL=toMatchEntity.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toMatchEntity.d.cts","names":[],"sources":["../src/toMatchEntity.ts"],"mappings":";;;;;;;;;;;;;iBAmCgB,cAAc,GAAG,WAAW,iBAAiB,UAAU,cAAc,KAAK;;;;;;;;;;KAmE9E,cAAc,KACxB,UAAU,oBAAoB,KAC1B,cAAc,cAAc,kBAGxB,OAEG,WAAW,KAAK,EAAE,WAAW,qBAAqB,UAC/C,cAAc,KAAK,IACnB,EAAE,WAAW,8BAA8B,KACzC,MAAM,cAAc,KAAK,KACzB,EAAE,WAAW,oBAAoB,KAC/B,IACA,EAAE,WAAW,4BACX,cAAc,EAAE,MAAM,EAAE,wBACxB,EAAE,WAAW,qBAAqB,4BACtB,cAAc,KAAK,mBAC7B,EAAE,WAAW,oBAAoB,sBACrB,cAAc,KAAK;GAE7B,cAAc,EAAE"}
@@ -0,0 +1,28 @@
1
+ import { CustomMatcherResult } from "./index.mjs";
2
+ import { Entity, Property, ReadOnlyCollection, Reference } from "joist-core";
3
+ //#region src/toMatchEntity.d.ts
4
+ /**
5
+ * Provides convenient `toMatchObject`-style matching for Joist entities.
6
+ *
7
+ * The biggest differences over `toMatchObject` are:
8
+ *
9
+ * - We un-wrap relations like m2o/o2m to just the underlying object/arrays, to make
10
+ * asserting against the entities look like asserting against POJOs
11
+ * - We prune the expected/actual values to be as minimal as possible, to avoid Jest
12
+ * recursively crawling into connection pools or other misc non-useful things in the diffs
13
+ */
14
+ declare function toMatchEntity<T>(this: any, actual: unknown, expected: MatchedEntity<T>): CustomMatcherResult;
15
+ /**
16
+ * Given a Joist entity `T`, "flattens out" the Reference/Collections.
17
+ *
18
+ * I.e. so that you can `toMatchEntity({ otherEntity: { name: "foo" } })` even though
19
+ * `otherEntity` is technically a joist `Reference` or `Collection`.
20
+ *
21
+ * We allow `| null` so that `toMatchEntity` can work against optional fields
22
+ * that are returned from GraphQL object resolvers.
23
+ */
24
+ type MatchedEntity<T> = T extends ReadonlyArray<infer U> ? ReadonlyArray<MatchedEntity<U>> : undefined | T | { [K in keyof T]?: T[K] extends Reference<any, infer U, any> ? MatchedEntity<U> | U : T[K] extends ReadOnlyCollection<any, infer U> ? Array<MatchedEntity<U> | U> : T[K] extends Property<any, infer V> ? V : T[K] extends Entity | null | undefined ? MatchedEntity<T[K]> | T[K] | null | undefined : T[K] extends ReadonlyArray<(infer U) | undefined> ? readonly (MatchedEntity<U> | U | undefined)[] : T[K] extends ReadonlyArray<infer U> | null ? readonly (MatchedEntity<U> | U | null)[] // We recurse similar to a DeepPartial
25
+ : MatchedEntity<T[K]> | null; };
26
+ //#endregion
27
+ export { MatchedEntity, toMatchEntity };
28
+ //# sourceMappingURL=toMatchEntity.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toMatchEntity.d.mts","names":[],"sources":["../src/toMatchEntity.ts"],"mappings":";;;;;;;;;;;;;iBAmCgB,cAAc,GAAG,WAAW,iBAAiB,UAAU,cAAc,KAAK;;;;;;;;;;KAmE9E,cAAc,KACxB,UAAU,oBAAoB,KAC1B,cAAc,cAAc,kBAGxB,OAEG,WAAW,KAAK,EAAE,WAAW,qBAAqB,UAC/C,cAAc,KAAK,IACnB,EAAE,WAAW,8BAA8B,KACzC,MAAM,cAAc,KAAK,KACzB,EAAE,WAAW,oBAAoB,KAC/B,IACA,EAAE,WAAW,4BACX,cAAc,EAAE,MAAM,EAAE,wBACxB,EAAE,WAAW,qBAAqB,4BACtB,cAAc,KAAK,mBAC7B,EAAE,WAAW,oBAAoB,sBACrB,cAAc,KAAK;GAE7B,cAAc,EAAE"}