UNPKG

domain-objects

Version:

A simple, convenient way to represent domain objects, leverage domain knowledge, and add runtime validation in your code base.

246 lines 11.1 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const joi_1 = __importDefault(require("joi")); const uuid_1 = require("uuid"); const yup = __importStar(require("yup")); const zod_1 = require("zod"); const DomainObject_1 = require("./DomainObject"); const HelpfulJoiValidationError_1 = require("./validate/HelpfulJoiValidationError"); const HelpfulYupValidationError_1 = require("./validate/HelpfulYupValidationError"); const HelpfulZodValidationError_1 = require("./validate/HelpfulZodValidationError"); describe('DomainObject', () => { describe('domain modeling use cases', () => { it('should be able to represent a literal', () => { class ChatMessage extends DomainObject_1.DomainObject { } const message = new ChatMessage({ userUuid: (0, uuid_1.v4)(), conversationUuid: (0, uuid_1.v4)(), message: 'Hello, World!', }); expect(message).toBeInstanceOf(ChatMessage); // sanity check }); it('should be able to represent an entity', () => { class RocketShip extends DomainObject_1.DomainObject { } const ship = new RocketShip({ serialNumber: (0, uuid_1.v4)(), fuelQuantity: 9001, passengers: 21, }); expect(ship).toBeInstanceOf(RocketShip); // sanity check }); }); describe('instantiation', () => { class ChatMessage extends DomainObject_1.DomainObject { } it('should assign all properties in the constructor to the instance', () => { const message = new ChatMessage({ userUuid: '__USER_UUID__', conversationUuid: '__CONVO_UUID__', message: 'Hello, World!', }); expect(message.userUuid).toEqual('__USER_UUID__'); expect(message.conversationUuid).toEqual('__CONVO_UUID__'); expect(message.message).toEqual('Hello, World!'); }); it('should be able to spread into itself', () => { const message = new ChatMessage({ userUuid: '__USER_UUID__', conversationUuid: '__CONVO_UUID__', message: 'Hello, World!', }); const updatedMessage = new ChatMessage(Object.assign(Object.assign({}, message), { message: `Hello, World!\n Edit: You're great!` })); expect(updatedMessage.userUuid).toEqual('__USER_UUID__'); expect(updatedMessage.conversationUuid).toEqual('__CONVO_UUID__'); expect(updatedMessage.message).toEqual(`Hello, World!\n Edit: You're great!`); }); }); describe('validation', () => { describe('Joi schema', () => { const schema = joi_1.default.object().keys({ serialNumber: joi_1.default.string().uuid().required(), fuelQuantity: joi_1.default.number().required(), passengers: joi_1.default.number().max(42).required(), }); class RocketShip extends DomainObject_1.DomainObject { } RocketShip.schema = schema; it('should not throw error if when valid', () => { const ship = new RocketShip({ serialNumber: (0, uuid_1.v4)(), fuelQuantity: 9001, passengers: 21, }); expect(ship).toBeInstanceOf(RocketShip); // sanity check }); it('should throw a helpful error when does not pass joi schema', () => { try { // eslint-disable-next-line no-new new RocketShip({ serialNumber: '__SOME_UUID__', fuelQuantity: 9001, passengers: 50, }); throw new Error('should not reach here'); } catch (error) { if (!(error instanceof Error)) throw error; expect(error).toBeInstanceOf(HelpfulJoiValidationError_1.HelpfulJoiValidationError); expect(error.message).toMatchSnapshot(); } }); }); describe('Yup schema', () => { const schema = yup.object({ serialNumber: yup.string().required(), fuelQuantity: yup.number().required(), passengers: yup.number().max(42).required(), }); class RocketShip extends DomainObject_1.DomainObject { } RocketShip.schema = schema; it('should not throw error if when valid', () => { const ship = new RocketShip({ serialNumber: (0, uuid_1.v4)(), fuelQuantity: 9001, passengers: 21, }); expect(ship).toBeInstanceOf(RocketShip); // sanity check }); it('should throw a helpful error when does not pass schema', () => { try { // eslint-disable-next-line no-new new RocketShip({ serialNumber: '__SOME_UUID__', fuelQuantity: 9001, passengers: 50, }); throw new Error('should not reach here'); } catch (error) { if (!(error instanceof Error)) throw error; expect(error).toBeInstanceOf(HelpfulYupValidationError_1.HelpfulYupValidationError); expect(error.message).toMatchSnapshot(); } }); }); describe('Zod schema', () => { const schema = zod_1.z.object({ serialNumber: zod_1.z.string(), fuelQuantity: zod_1.z.number(), passengers: zod_1.z.number().max(42), }); class RocketShip extends DomainObject_1.DomainObject { } RocketShip.schema = schema; it('should not throw error if when valid', () => { const ship = new RocketShip({ serialNumber: (0, uuid_1.v4)(), fuelQuantity: 9001, passengers: 21, }); expect(ship).toBeInstanceOf(RocketShip); // sanity check }); it('should throw a helpful error when does not pass schema', () => { try { // eslint-disable-next-line no-new new RocketShip({ serialNumber: '__SOME_UUID__', fuelQuantity: 9001, passengers: 50, }); throw new Error('should not reach here'); } catch (error) { if (!(error instanceof Error)) throw error; expect(error).toBeInstanceOf(HelpfulZodValidationError_1.HelpfulZodValidationError); expect(error.message).toMatchSnapshot(); } }); }); describe('hydration', () => { it('should hydrate nested domain objects', () => { class PlantPot extends DomainObject_1.DomainObject { } class Plant extends DomainObject_1.DomainObject { } Plant.nested = { pot: PlantPot }; // now show that we hydrate the pot const plant = new Plant({ pot: { diameterInInches: 7 }, lastWatered: 'monday', }); expect(plant.pot).toBeInstanceOf(PlantPot); }); it('should hydrate nested array of domain objects', () => { class PlantOwner extends DomainObject_1.DomainObject { } class Plant extends DomainObject_1.DomainObject { } Plant.nested = { owners: PlantOwner }; // now show that we hydrate the pot const plant = new Plant({ owners: [{ name: 'bob' }], lastWatered: 'monday', }); plant.owners.forEach((owner) => expect(owner).toBeInstanceOf(PlantOwner)); }); it('should not hydrate nullable nested domain objects when null', () => { class PlantOwner extends DomainObject_1.DomainObject { } class Plant extends DomainObject_1.DomainObject { } Plant.nested = { owners: PlantOwner }; // now show that we hydrate the pot const plant = new Plant({ owners: null, lastWatered: 'monday' }); expect(plant.owners).toEqual(null); // should still be null - since should not have instantiated }); it('should hydrate nested domain objects correctly when given choice of different options', () => { class PlantPot extends DomainObject_1.DomainObject { } class PlantBed extends DomainObject_1.DomainObject { } class Plant extends DomainObject_1.DomainObject { } Plant.nested = { plantedIn: [PlantPot, PlantBed] }; // now show that we hydrate the pot correctly const plant = new Plant({ plantedIn: { _dobj: 'PlantPot', diameterInInches: 7 }, lastWatered: 'monday', }); expect(plant.plantedIn).toBeInstanceOf(PlantPot); }); }); }); }); //# sourceMappingURL=DomainObject.test.js.map