UNPKG

6.34 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.ObjectPredicate = void 0;
4const is_1 = require("@sindresorhus/is");
5const dotProp = require("dot-prop");
6const isEqual = require("lodash.isequal");
7const has_items_1 = require("../utils/has-items");
8const of_type_1 = require("../utils/of-type");
9const of_type_deep_1 = require("../utils/of-type-deep");
10const match_shape_1 = require("../utils/match-shape");
11const predicate_1 = require("./predicate");
12class ObjectPredicate extends predicate_1.Predicate {
13 /**
14 @hidden
15 */
16 constructor(options) {
17 super('object', options);
18 }
19 /**
20 Test if an Object is a plain object.
21 */
22 get plain() {
23 return this.addValidator({
24 message: (_, label) => `Expected ${label} to be a plain object`,
25 validator: object => is_1.default.plainObject(object)
26 });
27 }
28 /**
29 Test an object to be empty.
30 */
31 get empty() {
32 return this.addValidator({
33 message: (object, label) => `Expected ${label} to be empty, got \`${JSON.stringify(object)}\``,
34 validator: object => Object.keys(object).length === 0
35 });
36 }
37 /**
38 Test an object to be not empty.
39 */
40 get nonEmpty() {
41 return this.addValidator({
42 message: (_, label) => `Expected ${label} to not be empty`,
43 validator: object => Object.keys(object).length > 0
44 });
45 }
46 /**
47 Test all the values in the object to match the provided predicate.
48
49 @param predicate - The predicate that should be applied against every value in the object.
50 */
51 valuesOfType(predicate) {
52 return this.addValidator({
53 message: (_, label, error) => `(${label}) ${error}`,
54 validator: object => of_type_1.default(Object.values(object), predicate)
55 });
56 }
57 /**
58 Test all the values in the object deeply to match the provided predicate.
59
60 @param predicate - The predicate that should be applied against every value in the object.
61 */
62 deepValuesOfType(predicate) {
63 return this.addValidator({
64 message: (_, label, error) => `(${label}) ${error}`,
65 validator: object => of_type_deep_1.default(object, predicate)
66 });
67 }
68 /**
69 Test an object to be deeply equal to the provided object.
70
71 @param expected - Expected object to match.
72 */
73 deepEqual(expected) {
74 return this.addValidator({
75 message: (object, label) => `Expected ${label} to be deeply equal to \`${JSON.stringify(expected)}\`, got \`${JSON.stringify(object)}\``,
76 validator: object => isEqual(object, expected)
77 });
78 }
79 /**
80 Test an object to be of a specific instance type.
81
82 @param instance - The expected instance type of the object.
83 */
84 instanceOf(instance) {
85 return this.addValidator({
86 message: (object, label) => {
87 let { name } = object.constructor;
88 if (!name || name === 'Object') {
89 name = JSON.stringify(object);
90 }
91 return `Expected ${label} \`${name}\` to be of type \`${instance.name}\``;
92 },
93 validator: object => object instanceof instance
94 });
95 }
96 /**
97 Test an object to include all the provided keys. You can use [dot-notation](https://github.com/sindresorhus/dot-prop) in a key to access nested properties.
98
99 @param keys - The keys that should be present in the object.
100 */
101 hasKeys(...keys) {
102 return this.addValidator({
103 message: (_, label, missingKeys) => `Expected ${label} to have keys \`${JSON.stringify(missingKeys)}\``,
104 validator: object => has_items_1.default({
105 has: item => dotProp.has(object, item)
106 }, keys)
107 });
108 }
109 /**
110 Test an object to include any of the provided keys. You can use [dot-notation](https://github.com/sindresorhus/dot-prop) in a key to access nested properties.
111
112 @param keys - The keys that could be a key in the object.
113 */
114 hasAnyKeys(...keys) {
115 return this.addValidator({
116 message: (_, label) => `Expected ${label} to have any key of \`${JSON.stringify(keys)}\``,
117 validator: object => keys.some(key => dotProp.has(object, key))
118 });
119 }
120 /**
121 Test an object to match the `shape` partially. This means that it ignores unexpected properties. The shape comparison is deep.
122
123 The shape is an object which describes how the tested object should look like. The keys are the same as the source object and the values are predicates.
124
125 @param shape - Shape to test the object against.
126
127 @example
128 ```
129 import ow from 'ow';
130
131 const object = {
132 unicorn: '🦄',
133 rainbow: '🌈'
134 };
135
136 ow(object, ow.object.partialShape({
137 unicorn: ow.string
138 }));
139 ```
140 */
141 partialShape(shape) {
142 return this.addValidator({
143 // TODO: Improve this when message handling becomes smarter
144 message: (_, label, message) => `${message.replace('Expected', 'Expected property')} in ${label}`,
145 validator: object => match_shape_1.partial(object, shape)
146 });
147 }
148 /**
149 Test an object to match the `shape` exactly. This means that will fail if it comes across unexpected properties. The shape comparison is deep.
150
151 The shape is an object which describes how the tested object should look like. The keys are the same as the source object and the values are predicates.
152
153 @param shape - Shape to test the object against.
154
155 @example
156 ```
157 import ow from 'ow';
158
159 ow({unicorn: '🦄'}, ow.object.exactShape({
160 unicorn: ow.string
161 }));
162 ```
163 */
164 exactShape(shape) {
165 // TODO [typescript@>=5] If higher-kinded types are supported natively by typescript, refactor `addValidator` to use them to avoid the usage of `any`. Otherwise, bump or remove this TODO.
166 return this.addValidator({
167 // TODO: Improve this when message handling becomes smarter
168 message: (_, label, message) => `${message.replace('Expected', 'Expected property')} in ${label}`,
169 validator: object => match_shape_1.exact(object, shape)
170 });
171 }
172}
173exports.ObjectPredicate = ObjectPredicate;