joist-test-utils 2.3.0-next.44 → 2.3.0-next.46

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/seed.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_runtime = require("./_virtual/_rolldown/runtime.cjs");
3
3
  let joist_core = require("joist-core");
4
- require("dotenv/config.js");
4
+ require("dotenv/config");
5
5
  let pg = require("pg");
6
6
  pg = require_runtime.__toESM(pg, 1);
7
7
  //#region src/seed.ts
@@ -1 +1 @@
1
- {"version":3,"file":"seed.cjs","names":["newPgConnectionConfig","EntityManager"],"sources":["../src/seed.ts"],"sourcesContent":["import \"dotenv/config.js\";\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
+ {"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"}
package/build/seed.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Driver, EntityManager } from "joist-core";
2
- import "dotenv/config.js";
2
+ import "dotenv/config";
3
3
  import pg from "pg";
4
4
  //#region src/seed.d.ts
5
5
  interface SeedConfig {
package/build/seed.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Driver, EntityManager } from "joist-core";
2
- import "dotenv/config.js";
2
+ import "dotenv/config";
3
3
  import pg from "pg";
4
4
  //#region src/seed.d.ts
5
5
  interface SeedConfig {
package/build/seed.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { EntityManager, newPgConnectionConfig } from "joist-core";
2
- import "dotenv/config.js";
2
+ import "dotenv/config";
3
3
  import pg from "pg";
4
4
  //#region src/seed.ts
5
5
  /**
package/build/seed.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"seed.js","names":[],"sources":["../src/seed.ts"],"sourcesContent":["import \"dotenv/config.js\";\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"}
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"}
@@ -3,9 +3,7 @@ require("./index.cjs");
3
3
  let joist_core = require("joist-core");
4
4
  let joist_utils = require("joist-utils");
5
5
  //#region src/toMatchEntity.ts
6
- const jestMatchers = globalThis[Symbol.for("$$jest-matchers-object")];
7
6
  const isVitest = typeof process !== "undefined" && process.env?.VITEST === "true";
8
- const matchers = isVitest ? void 0 : jestMatchers?.matchers;
9
7
  /**
10
8
  * Provides convenient `toMatchObject`-style matching for Joist entities.
11
9
  *
@@ -17,6 +15,7 @@ const matchers = isVitest ? void 0 : jestMatchers?.matchers;
17
15
  * recursively crawling into connection pools or other misc non-useful things in the diffs
18
16
  */
19
17
  function toMatchEntity(actual, expected) {
18
+ const matchers = isVitest ? void 0 : globalThis[Symbol.for("$$jest-matchers-object")]?.matchers;
20
19
  const cleanExpected = deepClone(expected);
21
20
  const cleanActual = deepClone(deepMirror(cleanExpected, actual));
22
21
  if (matchers) {
@@ -1 +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// This might be undefined if running outside of jest\nconst jestMatchers = (globalThis as any)[Symbol.for(\"$$jest-matchers-object\")];\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\";\nconst matchers = isVitest ? undefined : jestMatchers?.matchers;\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 // 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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"}
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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"}
@@ -1 +1 @@
1
- {"version":3,"file":"toMatchEntity.d.cts","names":[],"sources":["../src/toMatchEntity.ts"],"mappings":";;;;;;;;;;;;;iBAsCgB,cAAc,GAAG,WAAW,iBAAiB,UAAU,cAAc,KAAK;;;;;;;;;;KAiE9E,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"}
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"}
@@ -1 +1 @@
1
- {"version":3,"file":"toMatchEntity.d.mts","names":[],"sources":["../src/toMatchEntity.ts"],"mappings":";;;;;;;;;;;;;iBAsCgB,cAAc,GAAG,WAAW,iBAAiB,UAAU,cAAc,KAAK;;;;;;;;;;KAiE9E,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"}
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"}
@@ -2,9 +2,7 @@ import "./index.js";
2
2
  import { BaseEntity, isAsyncProperty, isCollection, isDefined, isEntity, isProperty, isReactiveField, isReference } from "joist-core";
3
3
  import { isPlainObject } from "joist-utils";
4
4
  //#region src/toMatchEntity.ts
5
- const jestMatchers = globalThis[Symbol.for("$$jest-matchers-object")];
6
5
  const isVitest = typeof process !== "undefined" && process.env?.VITEST === "true";
7
- const matchers = isVitest ? void 0 : jestMatchers?.matchers;
8
6
  /**
9
7
  * Provides convenient `toMatchObject`-style matching for Joist entities.
10
8
  *
@@ -16,6 +14,7 @@ const matchers = isVitest ? void 0 : jestMatchers?.matchers;
16
14
  * recursively crawling into connection pools or other misc non-useful things in the diffs
17
15
  */
18
16
  function toMatchEntity(actual, expected) {
17
+ const matchers = isVitest ? void 0 : globalThis[Symbol.for("$$jest-matchers-object")]?.matchers;
19
18
  const cleanExpected = deepClone(expected);
20
19
  const cleanActual = deepClone(deepMirror(cleanExpected, actual));
21
20
  if (matchers) {
@@ -1 +1 @@
1
- {"version":3,"file":"toMatchEntity.js","names":[],"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// This might be undefined if running outside of jest\nconst jestMatchers = (globalThis as any)[Symbol.for(\"$$jest-matchers-object\")];\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\";\nconst matchers = isVitest ? undefined : jestMatchers?.matchers;\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 // 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":";;;;AAoBA,MAAM,eAAgB,WAAmB,OAAO,IAAI,wBAAwB;AAK5E,MAAM,WAAW,OAAO,YAAY,eAAe,QAAQ,KAAK,WAAW;AAC3E,MAAM,WAAW,WAAW,KAAA,IAAY,cAAc;;;;;;;;;;;AAYtD,SAAgB,cAA4B,QAAiB,UAAiD;CAS5G,MAAM,gBAAgB,UAAU,QAAQ;CACxC,MAAM,cAAc,UAAU,WAAW,eAAe,MAAM,CAAC;CAI/D,IAAI,UAAU;EAEZ,IAAI,OAAO,gBAAgB,UAEzB,OAAO,SAAS,QAAQ,KAAK,MAAM,aAAa,aAAa;OAG7D,OAAO,SAAS,cAAc,KAAK,MAAM,aAAa,aAAa;CAEvE,OAAO,IAAI,UAOT,OAAO;EACL,MAFW,KAAK,OAAO,aAAa,eAAe,KAAK,aAErD;EACH,eAAe;GACb,MAAM,OAAO,KAAK,MAAM,YAAY,iBAAiB,KAAA,GAAW,KAAA,GAAW,EAAE,OAAO,KAAK,MAAM,CAAC;GAChG,MAAM,OAAO,KAAK,MAAM,KAAK,eAAe,WAAW;GACvD,OAAO,OAAO,GAAG,KAAK,MAAM,SAAS;EACvC;CACF;MAGA,OAAO;EACL,MAAM,KAAK,OAAO,aAAa,aAAa;EAC5C,eAAe,KAAK,MAAM,YAAY,iBAAiB,aAAa,aAAa;CACnF;AAEJ;AAEA,SAAS,YAAY,aAAuB;CAC1C,OAAO,SAAS,WAAW,IAAI,UAAU,YAAY,IAAI,WAAW,IAAI;AAC1E;;AAGA,SAAS,UAAU,GAAkB,QAAwB;CAC3D,OAAO,OAAO,eAAe;AAC/B;;;;;;;AAwCA,SAAS,WAAW,UAAe,QAAkB;CAKnD,IAAI,CAAC,cAAc,QAAQ,KAAK,CAAC,MAAM,QAAQ,QAAQ,GAAG,OAAO;CACjE,IAAI,CAAC,UAAU,MAAM,GAAG,OAAO;CAE/B,MAAM,SAAc,MAAM,QAAQ,QAAQ,IAAI,CAAC,IAAK,CAAC;CAErD,MAAM,OAAO,MAAM,QAAQ,MAAM,IAAI,OAAO,KAAK,MAAM,IAAI,OAAO,KAAK,QAAQ;CAC/E,KAAK,MAAM,OAAO,MAChB,IAAI,OAAO,QAET,OAAO,OAAO,WAAY,SAAiB,MAAM,iBAAiB,OAAO,IAAI,CAAC;CAGlF,OAAO;AACT;;AAGA,SAAS,UAAU,KAAc,sBAAM,IAAI,QAAQ,GAAY;CAC7D,IAAI,eAAe,YACjB,OAAO,YAAY,GAAG;MACjB,IAAI,OAAO,OAAO,QAAQ,aAAa,cAAc,GAAG,KAAK,MAAM,QAAQ,GAAG,IAAI;EACvF,IAAI,IAAI,IAAI,GAAG,GAAG,OAAO,IAAI,IAAI,GAAG;EACpC,MAAM,SAAS,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC;EAC1C,IAAI,IAAI,KAAK,MAAM;EACnB,OAAO,OAAO,QAAQ,GAAG,OAAO,KAAK,GAAG,CAAC,CAAC,KAAK,SAAS,GAAG,MAAM,UAAW,IAAY,MAAM,GAAG,EAAE,EAAE,CAAC;EACtG,OAAO;CACT,OACE,OAAO;AAEX;;AAGA,SAAS,iBAAiB,aAA+B;CACvD,IACE,YAAY,WAAW,KACvB,aAAa,WAAW,KACxB,WAAW,WAAW,KACtB,gBAAgB,WAAW,KAE3B,gBAAgB,WAAW,GAE3B,OAAO,mBAAmB,WAAW;CAEvC,OAAO;AACT;;;;;;;;;;;;;;AAeA,SAAS,mBAAmB,UAAoB;CAC9C,MAAM,IAAI,oBAAoB,WAAW,SAAS,iBAAiB,SAAS;CAC5E,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,QAAQ,MAAW,CAAC,EAAE,eAAe,IAAI;AACvE"}
1
+ {"version":3,"file":"toMatchEntity.js","names":[],"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":";;;;AAuBA,MAAM,WAAW,OAAO,YAAY,eAAe,QAAQ,KAAK,WAAW;;;;;;;;;;;AAY3E,SAAgB,cAA4B,QAAiB,UAAiD;CAE5G,MAAM,WAAW,WAAW,KAAA,IAAa,WAAmB,OAAO,IAAI,wBAAwB,EAAE,EAAE;CASnG,MAAM,gBAAgB,UAAU,QAAQ;CACxC,MAAM,cAAc,UAAU,WAAW,eAAe,MAAM,CAAC;CAI/D,IAAI,UAAU;EAEZ,IAAI,OAAO,gBAAgB,UAEzB,OAAO,SAAS,QAAQ,KAAK,MAAM,aAAa,aAAa;OAG7D,OAAO,SAAS,cAAc,KAAK,MAAM,aAAa,aAAa;CAEvE,OAAO,IAAI,UAOT,OAAO;EACL,MAFW,KAAK,OAAO,aAAa,eAAe,KAAK,aAErD;EACH,eAAe;GACb,MAAM,OAAO,KAAK,MAAM,YAAY,iBAAiB,KAAA,GAAW,KAAA,GAAW,EAAE,OAAO,KAAK,MAAM,CAAC;GAChG,MAAM,OAAO,KAAK,MAAM,KAAK,eAAe,WAAW;GACvD,OAAO,OAAO,GAAG,KAAK,MAAM,SAAS;EACvC;CACF;MAGA,OAAO;EACL,MAAM,KAAK,OAAO,aAAa,aAAa;EAC5C,eAAe,KAAK,MAAM,YAAY,iBAAiB,aAAa,aAAa;CACnF;AAEJ;AAEA,SAAS,YAAY,aAAuB;CAC1C,OAAO,SAAS,WAAW,IAAI,UAAU,YAAY,IAAI,WAAW,IAAI;AAC1E;;AAGA,SAAS,UAAU,GAAkB,QAAwB;CAC3D,OAAO,OAAO,eAAe;AAC/B;;;;;;;AAwCA,SAAS,WAAW,UAAe,QAAkB;CAKnD,IAAI,CAAC,cAAc,QAAQ,KAAK,CAAC,MAAM,QAAQ,QAAQ,GAAG,OAAO;CACjE,IAAI,CAAC,UAAU,MAAM,GAAG,OAAO;CAE/B,MAAM,SAAc,MAAM,QAAQ,QAAQ,IAAI,CAAC,IAAK,CAAC;CAErD,MAAM,OAAO,MAAM,QAAQ,MAAM,IAAI,OAAO,KAAK,MAAM,IAAI,OAAO,KAAK,QAAQ;CAC/E,KAAK,MAAM,OAAO,MAChB,IAAI,OAAO,QAET,OAAO,OAAO,WAAY,SAAiB,MAAM,iBAAiB,OAAO,IAAI,CAAC;CAGlF,OAAO;AACT;;AAGA,SAAS,UAAU,KAAc,sBAAM,IAAI,QAAQ,GAAY;CAC7D,IAAI,eAAe,YACjB,OAAO,YAAY,GAAG;MACjB,IAAI,OAAO,OAAO,QAAQ,aAAa,cAAc,GAAG,KAAK,MAAM,QAAQ,GAAG,IAAI;EACvF,IAAI,IAAI,IAAI,GAAG,GAAG,OAAO,IAAI,IAAI,GAAG;EACpC,MAAM,SAAS,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC;EAC1C,IAAI,IAAI,KAAK,MAAM;EACnB,OAAO,OAAO,QAAQ,GAAG,OAAO,KAAK,GAAG,CAAC,CAAC,KAAK,SAAS,GAAG,MAAM,UAAW,IAAY,MAAM,GAAG,EAAE,EAAE,CAAC;EACtG,OAAO;CACT,OACE,OAAO;AAEX;;AAGA,SAAS,iBAAiB,aAA+B;CACvD,IACE,YAAY,WAAW,KACvB,aAAa,WAAW,KACxB,WAAW,WAAW,KACtB,gBAAgB,WAAW,KAE3B,gBAAgB,WAAW,GAE3B,OAAO,mBAAmB,WAAW;CAEvC,OAAO;AACT;;;;;;;;;;;;;;AAeA,SAAS,mBAAmB,UAAoB;CAC9C,MAAM,IAAI,oBAAoB,WAAW,SAAS,iBAAiB,SAAS;CAC5E,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,QAAQ,MAAW,CAAC,EAAE,eAAe,IAAI;AACvE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "joist-test-utils",
3
- "version": "2.3.0-next.44",
3
+ "version": "2.3.0-next.46",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "build/index.cjs",
@@ -30,13 +30,16 @@
30
30
  "files": [
31
31
  "build"
32
32
  ],
33
+ "dependencies": {
34
+ "dotenv": "^17.4.2"
35
+ },
33
36
  "peerDependencies": {
34
- "joist-core": "2.3.0-next.44",
35
- "joist-utils": "2.3.0-next.44",
37
+ "joist-core": "2.3.0-next.46",
38
+ "joist-utils": "2.3.0-next.46",
36
39
  "pg": "^8.22.0"
37
40
  },
38
41
  "devDependencies": {
39
- "@swc/core": "^1.15.47",
42
+ "@swc/core": "^1.16.0",
40
43
  "@swc/jest": "^0.2.39",
41
44
  "@types/jest": "^30.0.0",
42
45
  "jest": "30.4.2",