UNPKG

7.26 kBJavaScriptView Raw
1const {compile, chain, root, arg0, arg1, setter, splice, withName} = require('../../index');
2const {
3 describeCompilers,
4 evalOrLoad,
5 currentValues,
6 funcLibrary,
7 expectTapFunctionToHaveBeenCalled,
8 rand
9} = require('../test-utils');
10const _ = require('lodash');
11const path = require('path')
12
13describe('Tests for usability and debugging carmi', () => {
14 describeCompilers(['simple', 'optimizing'], compiler => {
15 it('should store source files and ast in debug mode', () => {
16 const makeSureThisCanBeFound = root.map(item => item.mult(2));
17 const res = makeSureThisCanBeFound.map(item => item.plus(80));
18 const model = {res, set: setter(arg0)}
19 const optCode = evalOrLoad(compile(model, {compiler, debug: true}));
20 const inst = optCode([1, 2, 3], funcLibrary);
21 expect(inst.res).toEqual([82, 84, 86]);
22 const sources = JSON.stringify(inst.$source());
23 const ast = JSON.stringify(inst.$ast());
24 // expect(sources.indexOf('makeSureThisCanBeFound')).toBeGreaterThan(-1)
25 expect(ast.indexOf('80')).toBeGreaterThan(-1)
26 });
27
28 it('withName', () => {
29 const negated = withName('negated', root.map(val => val.not()));
30 const model = {doubleNegated: negated.map(val => val.not().call('tap')), set: setter(arg0)};
31 const optCode = evalOrLoad(compile(model, {compiler, debug: true}));
32 const inst = optCode([true, 1, 0, false, null], funcLibrary);
33 expect(inst.doubleNegated).toEqual([true, true, false, false, false]);
34 expectTapFunctionToHaveBeenCalled(inst.$model.length, compiler);
35 inst.set(1, null);
36 const nameGiven = Object.keys(inst).find(k => k.indexOf('negated') !== -1);
37 expect(nameGiven).toContain('negated');
38 expect(inst.doubleNegated).toEqual([true, false, false, false, false]);
39 expectTapFunctionToHaveBeenCalled(1, compiler);
40 });
41 it('chain should work in loop and on primitives', () => {
42 const model = {
43 test1: chain({test: true}),
44 test2: chain({test: chain(true)}),
45 test3: chain({test: chain(true).not()})
46 }
47 const optCode = evalOrLoad(compile(model, {compiler}));
48 const inst = optCode([], funcLibrary);
49 expect(inst.test1).toEqual({test: true});
50 expect(inst.test2).toEqual({test: true});
51 expect(inst.test3).toEqual({test: false});
52 });
53 it('throw on invalid arguments in setter function', () => {
54 const args = ['store', arg0, true]
55 expect(() => setter(...args)).toThrowError(`Invalid arguments for setter/splice/push - can only accept path (use arg0/arg1/arg2 - to define placeholders in the path), received [${args}]`);
56 });
57 it('throw on invalid arguments in splice function', () => {
58 const args = ['store', arg0, true]
59 expect(() => splice(...args)).toThrowError(`Invalid arguments for setter/splice/push - can only accept path (use arg0/arg1/arg2 - to define placeholders in the path), received [${args}]`);
60 });
61 it('throw on invalids reuse of key/val/loop/context inside other functions', () => {
62 expect(() => {
63 root.map(item => item.map(child => child.eq(item)))
64 }).toThrowError();
65 expect(() => {
66 root.map((item, val) => item.map(child => child.eq(val)))
67 }).toThrowError();
68 expect(() => {
69 root.map((item, val, context) => item.map(child => child.eq(context)), root.get(1))
70 }).toThrowError();
71 })
72 it('expect to hoist shared expressions', () => {
73 const once = root.map(val => val.call('tap'));
74 const twice = root.map(val => val.call('tap')).filter(val => val);
75 const model = {once, twice, set: setter(arg0)};
76 const optCode = evalOrLoad(compile(model, {compiler}));
77 const inst = optCode([false, 1, 0], funcLibrary);
78 expect(inst.once).toEqual([false, 1, 0]);
79 expect(inst.twice).toEqual([1]);
80 expectTapFunctionToHaveBeenCalled(inst.$model.length, compiler);
81 inst.set(2, true);
82 expect(inst.once).toEqual([false, 1, true]);
83 expect(inst.twice).toEqual([1, true]);
84 expectTapFunctionToHaveBeenCalled(1, compiler);
85 })
86 it('passing item between functions should throw nicer error message', () => {
87 expect(() => root.mapValues(item =>
88 root.filterBy(innerItem => innerItem.eq(item))
89 )).toThrow(/eq(.|\n)+filterBy/gm)
90 })
91
92 it('when using non-numbers with number functions, throw a nicer error', () => {
93 const model = {three: chain({a: 1}).ceil()}
94 const optCode = evalOrLoad(compile(model, {compiler, debug: true}));
95 expect(() => optCode([], funcLibrary)).toThrow('}.ceil')
96 })
97
98 it('throw more readable error when trying to chain an object with underfined', () => {
99 expect(() => chain({a: {b: [1, undefined]}})).toThrow('a.b[1]')
100 })
101
102 it('when calling a non-existent function, throw a readable error', () => {
103 const model = {three: chain({a: 1}).call('nonExistentFunction')}
104 const optCode = evalOrLoad(compile(model, {compiler, debug: true}));
105
106 expect(() => optCode([], funcLibrary)).toThrow('nonExistentFunction')
107 })
108
109 it('when calling a function with undefined args, throw a readable error', () => {
110 const model = {three: chain({a: () => 123}).call('func')}
111 expect(() => compile(model, {compiler, debug: true})).toThrow('() => 123')
112 })
113
114 it('allow primitives on the model', () => {
115 const model = {three: chain(3)}
116 const optCode = evalOrLoad(compile(model, {compiler}));
117 const inst = optCode([], funcLibrary);
118 expect(inst.three).toEqual(3);
119 })
120
121 it('should include relative paths in code', () => {
122 const model = {three: chain(3).mapValues('func').call('func')}
123 const src = compile(model, {compiler, debug: true});
124 expect(src).not.toContain(__dirname)
125 })
126 });
127
128 describeCompilers(['optimizing'], compiler => {
129 it('when using non-objects with object functions, throw a nicer error', () => {
130 const model = {three: chain(3).mapValues(a => a)}
131 const src = compile(model, {compiler, debug: true, cwd: path.resolve(__dirname, '../..')});
132 const optCode = evalOrLoad(src)
133 expect(() => optCode([], funcLibrary)).toThrow('3.mapValues')
134 })
135
136 it('when using arrays with object functions, throw an error', () => {
137 const model = {bad: root.get('data').mapValues(a => a)}
138 const src = compile(model, {compiler, debug: true});
139 const optCode = evalOrLoad(src)
140
141 expect(() => optCode({data: [0]}, funcLibrary)).toThrow('[0].mapValues')
142 })
143
144 it('values should only work woth object', () => {
145 const model = {
146 original: root.get('list'),
147 valued: root.get('list').values()
148 }
149
150 const src = compile(model, {compiler, debug: true})
151 const optModel = evalOrLoad(src)
152 const initialData = {list: [1, 2, 3, 4]}
153
154 expect(() => optModel(initialData)).toThrow('values expects object. valued at')
155 })
156
157 it('when using objects with array functions, throw an error', () => {
158 const model = {bad: root.get('data').filter(a => a)}
159 const src = compile(model, {compiler, debug: true});
160 const optCode = evalOrLoad(src)
161
162 expect(() => optCode({data: {a: 0}}, funcLibrary)).toThrow('0}.filter')
163 })
164 })
165});