UNPKG

10.1 kBJavaScriptView Raw
1import { ok, throws } from 'assert';
2
3import t from './globalContext';
4
5var no = function no(input) {
6 return ok(!input);
7};
8
9describe('Typed API', function () {
10 it('should check a string', function () {
11 var type = t.string();
12 ok(type.accepts('helo world'));
13 no(type.accepts(false));
14 });
15
16 it('should check a simple object', function () {
17 var type = t.object(t.property('foo', t.boolean()), t.property('bar', t.string('hello')));
18
19 ok(type.accepts({
20 foo: true,
21 bar: 'hello'
22 }));
23 });
24
25 it('should check nullable types', function () {
26 var type = t.nullable(t.string());
27
28 ok(type.accepts());
29 ok(type.accepts(null));
30 ok(type.accepts(undefined));
31 ok(type.accepts(''));
32 ok(type.accepts('foo'));
33 no(type.accepts(2));
34 no(type.accepts(true));
35 });
36
37 it('should assert nullable types', function () {
38 var type = t.nullable(t.string());
39
40 type.assert();
41 type.assert(null);
42 type.assert(undefined);
43 type.assert('');
44 type.assert('foo');
45 });
46
47 it('should check a simple object with shortcut syntax', function () {
48 var type = t.exactObject({
49 foo: t.boolean(),
50 bar: t.string()
51 });
52
53 ok(type.accepts({
54 foo: true,
55 bar: 'hello'
56 }));
57
58 no(type.accepts({
59 foo: true,
60 bar: 'hello',
61 baz: 44
62 }));
63
64 no(type.accepts({
65 foo: 123
66 }));
67 });
68
69 it('should check an exact object', function () {
70 var type = t.object({
71 foo: t.boolean(),
72 bar: t.string()
73 });
74
75 ok(type.accepts({
76 foo: true,
77 bar: 'hello'
78 }));
79
80 no(type.accepts({
81 foo: 123
82 }));
83 });
84
85 it('should make a tuple type', function () {
86 var type = t.tuple(t.string(), t.number(), t.boolean());
87
88 ok(type.accepts(['hello', 213, true]));
89 ok(type.accepts(['hello', 213, true, 'still ok']));
90 no(type.accepts(['hello', 213, 'nah']));
91 });
92
93 it('should declare a named type', function () {
94 var User = t.declare('User', t.object(t.property('id', t.number()), t.property('name', t.string())));
95
96 User.addConstraint(function (input) {
97 if (input.name.length <= 2) {
98 return "Name is too short!";
99 } else if (input.name.length >= 45) {
100 return "Name is too long!";
101 }
102 });
103
104 no(User.accepts({
105 id: 123,
106 name: false
107 }));
108
109 no(User.accepts({
110 id: 123,
111 name: ''
112 }));
113 ok(User.accepts({
114 id: 123,
115 name: 'this is valid'
116 }));
117 ok(User.accepts({
118 id: 123,
119 name: 'this is valid',
120 extra: 'okay'
121 }));
122 });
123
124 it('should use a Map<string, number>', function () {
125 var type = t.ref(Map, t.string(), t.number());
126 ok(type.accepts(new Map()));
127 ok(type.accepts(new Map([['valid', 123]])));
128 no(type.accepts(new Map([['valid', 123], ['notvalid', false]])));
129 });
130
131 it('should make a simple function type', function () {
132 var type = t.fn(t.param('input', t.boolean()), t.param('etc', t.boolean(), true), t.return(t.string()));
133
134 var good = function good(input) {
135 return input ? 'yes' : 'no';
136 };
137 var better = function better(input, etc) {
138 return input && etc ? 'yes' : 'no';
139 };
140 var bad = function bad() {
141 return undefined;
142 };
143 ok(type.accepts(good));
144 ok(type.accepts(better));
145 ok(type.accepts(bad)); // not enough type information to reject.
146 });
147
148 it('should make a parameterized function type', function () {
149 var type = t.fn(function (fn) {
150 var T = fn.typeParameter('T', t.union(t.string(), t.number()));
151 return [t.param('input', T), t.param('etc', t.boolean(), true), t.return(t.nullable(T))];
152 });
153
154 function good(input) {
155 return input;
156 }
157 function better(input, etc) {
158 return etc ? input : null;
159 }
160 function bad() {
161 return;
162 }
163 ok(type.accepts(good));
164 ok(type.accepts(better));
165 ok(type.accepts(bad)); // not enough type information to reject.
166 });
167
168 it('should build a tree-like object', function () {
169 var Tree = t.type('Tree', function (Tree) {
170 var T = Tree.typeParameter('T');
171 return t.object(t.property('value', T), t.property('left', t.nullable(t.ref(Tree, T))), t.property('right', t.nullable(t.ref(Tree, T))));
172 });
173 var candidate = {
174 value: 'hello world',
175 left: null,
176 right: {
177 value: 'foo',
178 left: null,
179 right: null
180 }
181 };
182 ok(Tree.assert(candidate));
183 });
184
185 it('should apply type parameters', function () {
186 var A = t.type("A", function (A) {
187 var T = A.typeParameter("T");
188 return T;
189 });
190 var B = t.type("B", t.ref(A, t.string()));
191
192 ok(B.assert("abc"));
193 ok(A.assert(123));
194 throws(function () {
195 return B.assert(123);
196 });
197 });
198
199 it('should handle named types', function () {
200 var UserEmailAddress = t.type('UserEmailAddress', t.string());
201 UserEmailAddress.addConstraint(function (input) {
202 if (!/@/.test(input)) {
203 return "must be a valid email address";
204 }
205 });
206
207 var User = t.type('User', t.object(t.property('id', t.number()), t.property('name', t.string()), t.property('email', UserEmailAddress)));
208
209 var sally = {
210 id: 123,
211 name: 'Sally',
212 email: 'invalid'
213 };
214
215 throws(function () {
216 return User.assert(sally);
217 });
218 sally.email = 'sally@example.com';
219 User.assert(sally);
220 });
221
222 it('should handle Class<User>', function () {
223 var _dec, _class, _dec2, _class2;
224
225 var User = (_dec = t.decorate(t.object(t.property('id', t.number()), t.property('name', t.string()), t.property('email', t.string()))), _dec(_class = function User() {
226 babelHelpers.classCallCheck(this, User);
227 }) || _class);
228
229 var AdminUser = function (_User) {
230 babelHelpers.inherits(AdminUser, _User);
231
232 function AdminUser() {
233 babelHelpers.classCallCheck(this, AdminUser);
234 return babelHelpers.possibleConstructorReturn(this, (AdminUser.__proto__ || Object.getPrototypeOf(AdminUser)).apply(this, arguments));
235 }
236
237 return AdminUser;
238 }(User);
239
240 var Role = (_dec2 = t.decorate(t.object(t.property('name', t.string()))), _dec2(_class2 = function Role() {
241 babelHelpers.classCallCheck(this, Role);
242 }) || _class2);
243
244
245 var INameable = t.type('Nameable', t.object(t.property('name', t.string())));
246 var INomable = t.type('Nameable', t.object(t.property('nom', t.string())));
247 var INameableClass = t.ref('Class', INameable);
248 var INomableClass = t.ref('Class', INomable);
249
250 var IUserClass = t.ref('Class', t.ref(User));
251 var IAdminUserClass = t.ref('Class', t.ref(AdminUser));
252 no(IUserClass.accepts(Role));
253 ok(IUserClass.accepts(User));
254 ok(IUserClass.accepts(AdminUser));
255
256 no(IAdminUserClass.accepts(Role));
257 no(IAdminUserClass.accepts(User));
258 ok(IAdminUserClass.accepts(AdminUser));
259
260 ok(INameableClass.accepts(User));
261 ok(INameableClass.accepts(Role));
262 ok(INameableClass.accepts(AdminUser));
263 no(INomableClass.accepts(User));
264
265 //t.ref(Map, t.string(), t.number()).assert(new Map([['hello', false]]));
266 });
267
268 it('should $Diff<A, B>', function () {
269 var A = t.object(t.property('name', t.string()), t.property('email', t.string()));
270 var B = t.object(t.property('email', t.string('example@example.com')));
271
272 var C = t.ref('$Diff', A, B);
273
274 no(C.accepts({}));
275 ok(C.accepts({ name: 'Alice' }));
276 ok(C.accepts({ name: 'Alice', email: 'alice@example.com' }));
277 no(C.accepts({ email: 'alice@example.com' }));
278 no(C.accepts({ name: false, email: 'alice@example.com' }));
279 });
280
281 it('should $Shape<A>', function () {
282 var A = t.object(t.property('name', t.string()), t.property('email', t.string()));
283 var B = t.ref('$Shape', A);
284
285 ok(B.accepts({}));
286 ok(B.accepts({ name: 'Alice' }));
287 ok(B.accepts({ name: 'Alice', email: 'alice@example.com' }));
288 no(B.accepts({ nope: false }));
289 no(B.accepts({ name: false, email: 'alice@example.com' }));
290 no(B.accepts({ name: 'Alice', email: 'alice@example.com', extra: true }));
291 });
292
293 it('should $Keys<A>', function () {
294 var A = t.object(t.property('name', t.string()), t.property('email', t.string()));
295 var B = t.ref('$Keys', A);
296
297 ok(B.accepts('name'));
298 ok(B.accepts('email'));
299 no(B.accepts('nope'));
300 no(B.accepts(false));
301 no(B.accepts({}));
302 });
303
304 it('should $Keys<typeOf A>', function () {
305 var A = t.typeOf({
306 name: 'Alice',
307 email: 'example@example.com'
308 });
309 var B = t.ref('$Keys', A);
310
311 ok(B.accepts('name'));
312 ok(B.accepts('email'));
313 no(B.accepts('nope'));
314 no(B.accepts(false));
315 no(B.accepts({}));
316 });
317
318 it.skip('should $ObjMap<K, V>', function () {
319 var K = t.object(t.property('name', t.string()), t.property('email', t.string()));
320 var V = t.fn(function (fn) {
321 var K = fn.typeParameter('K');
322 var V = fn.typeParameter('V');
323 return [t.param('key', K), t.param('value', V), t.return(t.tuple(K, V))];
324 });
325 var B = t.ref('$ObjMap', K, V);
326
327 B.assert({
328 name: ['name', 'Hello'],
329 email: ['email', 'World']
330 });
331 ok(B.accepts({}));
332 ok(B.accepts({ name: 'Alice' }));
333 ok(B.accepts({ name: 'Alice', email: 'alice@example.com' }));
334 no(B.accepts({ nope: false }));
335 no(B.accepts({ name: false, email: 'alice@example.com' }));
336 no(B.accepts({ name: 'Alice', email: 'alice@example.com', extra: true }));
337 });
338
339 it('should build an object', function () {
340 t.object(t.property('foo', t.string('bar')), t.property('qux', t.union(t.string(), t.number(), t.boolean())), t.property('nested', t.object(t.property('again', t.object(t.indexer('nom', t.string(), t.any()), t.property('hello', t.string('world')), t.property('bar', t.string()), t.property('meth', t.fn(t.param('a', t.boolean(false)), t.return(t.string()))), t.method('m', function (fn) {
341 var T = fn.typeParameter('T');
342 return [t.param('a', T), t.return(T)];
343 }), t.property('typed', t.fn(function (fn) {
344 var T = fn.typeParameter('T', t.string());
345 return [t.param('input', T), t.return(t.object(t.property('nn', T)))];
346 })))))));
347 });
348});
\No newline at end of file