From 792686a2593f7e028ae676d553bbde9d58da50a1 Mon Sep 17 00:00:00 2001 From: Kevin Barabash Date: Thu, 13 Oct 2022 12:26:06 -0400 Subject: [PATCH] Add 'dataFactoryFor' function to help generate mock data (#428) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary: This PR extracts 'getCreateMockData' from https://github.com/Khan/webapp/pull/8976 so that we can use it other repo than just 'webapp'. This PR also: - renames this function to 'dataFactoryFor' - updates the examples in the documentation so that they show valid usage - fixes a bug in the code that could've resulting in mutation of the partial object being passed to the data factory Issue: None ## Test plan: - yarn test Author: kevinbarabash Reviewers: kevinbarabash, somewhatabstract, jeresig Required Reviewers: Approved By: jeresig Checks: ✅ codecov/project, ✅ Test (macOS-latest, 16.x), ✅ CodeQL, ✅ Lint, flow, and coverage check (ubuntu-latest, 16.x), ✅ Prime node_modules cache for primary configuration (ubuntu-latest, 16.x), ✅ gerald, ✅ Analyze (javascript), ⏭ dependabot Pull Request URL: https://github.com/Khan/wonder-stuff/pull/428 --- .changeset/sharp-houses-sip.md | 5 ++ .../src/__tests__/data-factory-for.test.js | 43 ++++++++++++++ .../src/data-factory-for.js | 58 +++++++++++++++++++ packages/wonder-stuff-testing/src/index.js | 1 + 4 files changed, 107 insertions(+) create mode 100644 .changeset/sharp-houses-sip.md create mode 100644 packages/wonder-stuff-testing/src/__tests__/data-factory-for.test.js create mode 100644 packages/wonder-stuff-testing/src/data-factory-for.js diff --git a/.changeset/sharp-houses-sip.md b/.changeset/sharp-houses-sip.md new file mode 100644 index 00000000..a6967c53 --- /dev/null +++ b/.changeset/sharp-houses-sip.md @@ -0,0 +1,5 @@ +--- +"@khanacademy/wonder-stuff-testing": minor +--- + +Add 'dataFactoryFor' function to help generate mock data diff --git a/packages/wonder-stuff-testing/src/__tests__/data-factory-for.test.js b/packages/wonder-stuff-testing/src/__tests__/data-factory-for.test.js new file mode 100644 index 00000000..19fa51eb --- /dev/null +++ b/packages/wonder-stuff-testing/src/__tests__/data-factory-for.test.js @@ -0,0 +1,43 @@ +// @flow +import {dataFactoryFor} from "../data-factory-for.js"; + +describe("dataFactoryFor", () => { + describe("returns a function that", () => { + it("should return a clone of the default data", () => { + const BASE_OBJECT = {x: 5, y: 10}; + + const dataFactory = dataFactoryFor(BASE_OBJECT); + + const data = dataFactory(); + expect(data).toEqual(BASE_OBJECT); + expect(data).not.toBe(BASE_OBJECT); + }); + + it("should merge object passed to factory with the base object", () => { + const BASE_OBJECT = {x: 5, y: 10}; + + const dataFactory = dataFactoryFor(BASE_OBJECT); + + const data = dataFactory({x: 100}); + expect(data).toEqual({ + x: 100, + y: 10, + }); + }); + + it("should return a copy of the merged data", () => { + const BASE_OBJECT = { + p: {x: 5, y: 10}, + q: {x: 0, y: 1}, + }; + + const dataFactory = dataFactoryFor(BASE_OBJECT); + + const p = {x: 20, y: 30}; + const data = dataFactory({p}); + data.p.x = 100; + + expect(p).toEqual({x: 20, y: 30}); + }); + }); +}); diff --git a/packages/wonder-stuff-testing/src/data-factory-for.js b/packages/wonder-stuff-testing/src/data-factory-for.js new file mode 100644 index 00000000..13e63485 --- /dev/null +++ b/packages/wonder-stuff-testing/src/data-factory-for.js @@ -0,0 +1,58 @@ +// @flow +import {clone} from "@khanacademy/wonder-stuff-core"; + +/** + * dataFactoryFor(defaultObject) + * + * Returns a new factory function that returns can be called to obtain + * a copy of `defaultObject`. Optionally, the factory function can be + * passed a `partialObject` in which case it will return a copy of the + * merged object. + * + * Any properties appearing in `partialObject` must be complete + * objects. If you want to update a deeply nested property by + * itself, you can do so by directly modifying the object. This is + * safe because + * + * This + * can still be a bit awkward since there may be a number of optionals + * that you have to check before setting the value. + * + * Example (shallow override): + * const BASE_OBJECT = { + * student: { ... }, + * teacher: { ... }, + * }; + * const dataFactory = dataFactoryFor(BASE_OBJECT); + * const data = dataFactory({ + * student: { + * __typename: "User", + * id: "new_kaid", + * kaid: "new_kaid", + * ... other properties on `user` + * } + * }); + * + * Example (deep update): + * const BASE_OBJECT = { + * student: { ... }, + * teacher: { ... }, + * }; + * const dataFactory = dataFactoryFor(BASE_OBJECT); + * const data = dataFactory(); + * if (data.user) { + * data.user.kaid = "new_kaid"; + * data.user.id = "new_kaid"; + * } + */ +export const dataFactoryFor = + (baseObject: T): ((partialObject?: $Partial) => T) => + ( + // $FlowIgnore[incompatible-type]: Flow thinks that {} can't be assigned to Partial + partialObject: $Partial = Object.freeze({}), + ): T => { + // NOTE: we clone the result to prevent tests from modifying + // either `defaultObject` or `partialObject` when performing + // deep updates on the return object returned by the factory. + return clone({...baseObject, ...partialObject}); + }; diff --git a/packages/wonder-stuff-testing/src/index.js b/packages/wonder-stuff-testing/src/index.js index edddf704..608c39c7 100644 --- a/packages/wonder-stuff-testing/src/index.js +++ b/packages/wonder-stuff-testing/src/index.js @@ -1,2 +1,3 @@ // @flow export * as jest from "./jest/index.js"; +export {dataFactoryFor} from "./data-factory-for.js";