cx 22.1.4 → 22.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/manifest.js +547 -547
  2. package/dist/ui.js +1 -4
  3. package/dist/widgets.js +11 -1
  4. package/package.json +1 -1
  5. package/src/core.d.ts +41 -6
  6. package/src/data/Binding.d.ts +4 -3
  7. package/src/data/Binding.js +12 -8
  8. package/src/data/Store.d.ts +5 -5
  9. package/src/data/Store.js +2 -2
  10. package/src/data/StoreRef.js +2 -0
  11. package/src/data/StructuredSelector.js +43 -57
  12. package/src/data/StructuredSelector.spec.js +54 -39
  13. package/src/data/View.d.ts +40 -16
  14. package/src/data/View.js +27 -19
  15. package/src/data/computable.d.ts +27 -4
  16. package/src/data/computable.js +2 -1
  17. package/src/data/computable.spec.js +8 -0
  18. package/src/data/createAccessorModelProxy.d.ts +6 -0
  19. package/src/data/createAccessorModelProxy.js +24 -0
  20. package/src/data/createAccessorModelProxy.spec.tsx +23 -0
  21. package/src/data/createStructuredSelector.js +9 -8
  22. package/src/data/getAccessor.js +24 -15
  23. package/src/data/getSelector.js +4 -1
  24. package/src/data/getSelector.spec.js +7 -0
  25. package/src/data/index.d.ts +2 -0
  26. package/src/data/index.js +2 -0
  27. package/src/ui/Controller.d.ts +13 -8
  28. package/src/ui/Instance.d.ts +6 -6
  29. package/src/ui/Instance.js +6 -5
  30. package/src/ui/Repeater.d.ts +25 -15
  31. package/src/ui/Restate.spec.js +0 -1
  32. package/src/ui/adapter/ArrayAdapter.d.ts +9 -10
  33. package/src/ui/adapter/ArrayAdapter.js +30 -37
  34. package/src/ui/adapter/TreeAdapter.js +27 -34
  35. package/src/ui/bind.d.ts +3 -2
  36. package/src/ui/bind.js +4 -3
  37. package/src/ui/expr.d.ts +23 -2
  38. package/src/ui/expr.js +16 -4
  39. package/src/widgets/AccessorBindings.spec.tsx +66 -0
  40. package/src/widgets/HtmlElement.d.ts +1 -1
  41. package/src/widgets/List.d.ts +21 -23
  42. package/src/widgets/form/Checkbox.d.ts +22 -18
  43. package/src/widgets/form/Checkbox.js +6 -0
  44. package/src/widgets/form/ValidationGroup.spec.js +1 -1
  45. package/src/widgets/grid/Grid.d.ts +86 -68
  46. package/src/widgets/grid/Grid.js +9 -3
package/dist/ui.js CHANGED
@@ -5022,14 +5022,11 @@ var TreeAdapter = /*#__PURE__*/ (function(_ArrayAdapter) {
5022
5022
  _proto.processList = function processList(context, instance, level, parentKey, nodes, result) {
5023
5023
  var _this = this;
5024
5024
 
5025
- var nonLeafs = [],
5026
- leafs = [];
5027
5025
  nodes.forEach(function(record) {
5028
5026
  record.key = parentKey + record.key;
5029
5027
 
5030
- _this.processNode(context, instance, level, record.data.$leaf ? leafs : nonLeafs, record);
5028
+ _this.processNode(context, instance, level, result, record);
5031
5029
  });
5032
- result.push.apply(result, nonLeafs.concat(leafs));
5033
5030
  };
5034
5031
 
5035
5032
  _proto.processNode = function processNode(context, instance, level, result, record) {
package/dist/widgets.js CHANGED
@@ -8328,7 +8328,8 @@ var Checkbox = /*#__PURE__*/ (function(_Field) {
8328
8328
  readOnly: undefined,
8329
8329
  disabled: undefined,
8330
8330
  enabled: undefined,
8331
- required: undefined
8331
+ required: undefined,
8332
+ viewText: undefined
8332
8333
  }
8333
8334
  ].concat(Array.prototype.slice.call(arguments))
8334
8335
  );
@@ -8433,6 +8434,15 @@ var Checkbox = /*#__PURE__*/ (function(_Field) {
8433
8434
  ]);
8434
8435
  };
8435
8436
 
8437
+ _proto.renderValue = function renderValue(context, _ref) {
8438
+ var data = _ref.data;
8439
+ if (!data.viewText) return _Field.prototype.renderValue.apply(this, arguments);
8440
+ return /*#__PURE__*/ jsx("span", {
8441
+ className: this.CSS.element(this.baseClass, "view-text"),
8442
+ children: data.viewText
8443
+ });
8444
+ };
8445
+
8436
8446
  _proto.formatValue = function formatValue(context, instance) {
8437
8447
  var data = instance.data;
8438
8448
  return data.value && (data.text || this.renderChildren(context, instance));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cx",
3
- "version": "22.1.4",
3
+ "version": "22.3.2",
4
4
  "description": "Advanced JavaScript UI framework for admin and dashboard applications with ready to use grid, form and chart components.",
5
5
  "main": "index.js",
6
6
  "jsnext:main": "src/index.js",
package/src/core.d.ts CHANGED
@@ -13,12 +13,10 @@ declare namespace Cx {
13
13
 
14
14
  type Tpl = {
15
15
  tpl: string;
16
- defaultValue?: any;
17
16
  };
18
17
 
19
18
  type Expr = {
20
19
  expr: string;
21
- defaultValue?: any;
22
20
  };
23
21
 
24
22
  type Binding = Bind | Tpl | Expr;
@@ -29,7 +27,13 @@ declare namespace Cx {
29
27
  [prop: string]: Selector<any>;
30
28
  }
31
29
 
32
- type Prop<T> = Binding | T | Selector<T>;
30
+ type AccessorChain<M> = {
31
+ toString(): string;
32
+ } & {
33
+ [prop in keyof M]: AccessorChain<M[prop]>;
34
+ };
35
+
36
+ type Prop<T> = T | Binding | Selector<T> | AccessorChain<T>;
33
37
 
34
38
  interface Record {
35
39
  [prop: string]: any;
@@ -51,6 +55,8 @@ declare namespace Cx {
51
55
  type RecordsProp = Prop<Record[]>;
52
56
  type SortersProp = Prop<Sorter[]>;
53
57
 
58
+ type RecordAlias<V = any> = string | AccessorChain<V[]>;
59
+
54
60
  interface WidgetProps {
55
61
  /** Inner layout used to display children inside the widget. */
56
62
  layout?: any;
@@ -122,7 +128,7 @@ declare namespace Cx {
122
128
  style?: StyleProp;
123
129
 
124
130
  /** Style object applied to the element */
125
- styles?: Cx.StyleProp;
131
+ styles?: StyleProp;
126
132
  }
127
133
 
128
134
  interface HtmlElementProps extends StyledContainerProps {
@@ -130,7 +136,7 @@ declare namespace Cx {
130
136
  id?: string | number | Binding | Selector<string | number>;
131
137
 
132
138
  /** Inner text contents. */
133
- text?: string | number | Binding | Selector<string | number>;
139
+ text?: Cx.StringProp | Cx.NumberProp;
134
140
 
135
141
  /** Tooltip configuration. */
136
142
  tooltip?: StringProp | StructuredProp;
@@ -176,6 +182,35 @@ declare global {
176
182
  interface IntrinsicElements {
177
183
  cx: any;
178
184
  }
185
+
186
+ interface IntrinsicAttributes {
187
+ /** Inner layout used to display children inside the widget. */
188
+ layout?: any;
189
+
190
+ /** Outer (wrapper) layout used to display the widget in. */
191
+ outerLayout?: any;
192
+
193
+ /** Name of the ContentPlaceholder that should be used to display the widget. */
194
+ putInto?: string;
195
+
196
+ /** Name of the ContentPlaceholder that should be used to display the widget. */
197
+ contentFor?: string;
198
+
199
+ /** Controller. */
200
+ controller?: any;
201
+
202
+ /** Visibility of the widget. Defaults to `true`. */
203
+ visible?: Cx.BooleanProp;
204
+
205
+ /** Visibility of the widget. Defaults to `true`. */
206
+ if?: Cx.BooleanProp;
207
+
208
+ /** Appearance modifier. For example, mod="big" will add the CSS class `.cxm-big` to the block element. */
209
+ mod?: Cx.StringProp | Cx.Prop<string[]> | Cx.StructuredProp;
210
+
211
+ /** Cache render output. Default is `true`. */
212
+ memoize?: Cx.BooleanProp;
213
+ }
179
214
  }
180
215
  }
181
216
 
@@ -183,7 +218,7 @@ declare module "react" {
183
218
  interface ClassAttributes<T> extends Cx.PureContainerProps {
184
219
  class?: Cx.ClassProp;
185
220
  styles?: Cx.StyleProp;
186
- text?: Cx.StringProp;
221
+ text?: Cx.StringProp | Cx.NumberProp;
187
222
  innerText?: Cx.StringProp;
188
223
  html?: Cx.StringProp;
189
224
  innerHtml?: Cx.StringProp;
@@ -1,15 +1,16 @@
1
- import { Record } from "../core";
1
+ import { Record, AccessorChain } from "../core";
2
2
 
3
- export class Binding {
3
+ export class Binding<V = any> {
4
4
  constructor(path: string);
5
5
 
6
- set(state: Record, value: any): Record;
6
+ set(state: Record, value: V): Record;
7
7
 
8
8
  delete(state: Record): Record;
9
9
 
10
10
  value(state: Record): any;
11
11
 
12
12
  static get(path: string): Binding;
13
+ static get<V>(chain: AccessorChain<V>): Binding<V>;
13
14
  }
14
15
 
15
16
  export function isBinding(value: any): boolean;
@@ -1,24 +1,25 @@
1
1
  let bindingCache = {};
2
2
  import { isString } from "../util/isString";
3
3
  import { isObject } from "../util/isObject";
4
+ import { isAccessorChain } from "./createAccessorModelProxy";
4
5
 
5
6
  export class Binding {
6
7
  constructor(path) {
7
8
  this.path = path;
8
9
  this.parts = path.split(".");
9
- let fstr = "return (x";
10
- let cpath = "x";
10
+ let body = "return (x";
11
+ let selector = "x";
11
12
 
12
13
  for (let i = 0; i < this.parts.length; i++) {
13
- if (this.parts[i][0] >= "0" && this.parts[i][0] <= "9") cpath += "[" + this.parts[i] + "]";
14
- else cpath += "." + this.parts[i];
14
+ if (this.parts[i][0] >= "0" && this.parts[i][0] <= "9") selector += "[" + this.parts[i] + "]";
15
+ else selector += "." + this.parts[i];
15
16
 
16
- if (i + 1 < this.parts.length) fstr += " && " + cpath;
17
- else fstr += " ? " + cpath + " : undefined";
17
+ if (i + 1 < this.parts.length) body += " && " + selector;
18
+ else body += " ? " + selector + " : undefined";
18
19
  }
19
20
 
20
- fstr += ")";
21
- this.value = new Function("x", fstr);
21
+ body += ")";
22
+ this.value = new Function("x", body);
22
23
  }
23
24
 
24
25
  set(state, value) {
@@ -72,11 +73,14 @@ export class Binding {
72
73
 
73
74
  if (path instanceof Binding) return path;
74
75
 
76
+ if (isAccessorChain(path)) return this.get(path.toString());
77
+
75
78
  throw new Error("Invalid binding definition provided.");
76
79
  }
77
80
  }
78
81
 
79
82
  export function isBinding(value) {
80
83
  if (isObject(value) && isString(value.bind)) return true;
84
+ if (value && value.isAccessorChain) return true;
81
85
  return value instanceof Binding;
82
86
  }
@@ -1,12 +1,12 @@
1
- import {View, ViewConfig} from './View';
1
+ import { View, ViewConfig } from "./View";
2
2
 
3
- interface StoreConfig extends ViewConfig {
3
+ interface StoreConfig<D = any> extends ViewConfig {
4
4
  async?: boolean;
5
- data?: any;
5
+ data?: D;
6
6
  }
7
7
 
8
- export class Store extends View {
9
- constructor(config?: StoreConfig);
8
+ export class Store<D = any> extends View<D> {
9
+ constructor(config?: StoreConfig<D>);
10
10
 
11
11
  unsubscribeAll(): void;
12
12
 
package/src/data/Store.js CHANGED
@@ -24,7 +24,7 @@ export class Store extends SubscribableView {
24
24
  }
25
25
  return false;
26
26
  }
27
-
27
+
28
28
  deleteItem(path) {
29
29
  let next = Binding.get(path).delete(this.data);
30
30
  if (next != this.data) {
@@ -35,7 +35,7 @@ export class Store extends SubscribableView {
35
35
  }
36
36
  return false;
37
37
  }
38
-
38
+
39
39
  clear() {
40
40
  this.data = {};
41
41
  this.meta.version++;
@@ -1,8 +1,10 @@
1
+ import { isAccessorChain } from "./createAccessorModelProxy";
1
2
  import { Ref } from "./Ref";
2
3
 
3
4
  export class StoreRef extends Ref {
4
5
  constructor(config) {
5
6
  super(config);
7
+ if (isAccessorChain(this.path)) this.path = this.path.toString();
6
8
  }
7
9
 
8
10
  get() {
@@ -1,94 +1,83 @@
1
- import {Binding} from './Binding';
2
- import {Expression} from './Expression';
3
- import {StringTemplate} from './StringTemplate';
4
- import {createStructuredSelector} from '../data/createStructuredSelector';
5
- import {getSelector} from '../data/getSelector';
6
- import {isFunction} from '../util/isFunction';
7
- import {isUndefined} from '../util/isUndefined';
8
- import {isDefined} from '../util/isDefined';
9
- import {isArray} from '../util/isArray';
1
+ import { Binding } from "./Binding";
2
+ import { Expression } from "./Expression";
3
+ import { StringTemplate } from "./StringTemplate";
4
+ import { createStructuredSelector } from "../data/createStructuredSelector";
5
+ import { getSelector } from "../data/getSelector";
6
+ import { isFunction } from "../util/isFunction";
7
+ import { isUndefined } from "../util/isUndefined";
8
+ import { isDefined } from "../util/isDefined";
9
+ import { isArray } from "../util/isArray";
10
+ import { isAccessorChain } from "./createAccessorModelProxy";
10
11
 
11
12
  function defaultValue(pv) {
12
- if (typeof pv == 'object' && pv && pv.structured)
13
- return pv.defaultValue;
13
+ if (typeof pv == "object" && pv && pv.structured) return pv.defaultValue;
14
14
 
15
15
  return pv;
16
16
  }
17
17
 
18
18
  function getSelectorConfig(props, values, nameMap) {
19
-
20
19
  let functions = {},
21
20
  structures = {},
22
21
  defaultValues = {},
23
- constants, p, v, pv,
22
+ constants,
23
+ p,
24
+ v,
25
+ pv,
24
26
  constant = true;
25
27
 
26
28
  for (p in props) {
27
29
  v = values[p];
28
30
  pv = props[p];
29
31
 
30
- if (isUndefined(v) && pv && (pv.bind || pv.tpl || pv.expr))
31
- v = pv;
32
+ if (isUndefined(v) && pv && (pv.bind || pv.tpl || pv.expr)) v = pv;
32
33
 
33
34
  if (v === null) {
34
- if (!constants)
35
- constants = {};
35
+ if (!constants) constants = {};
36
36
  constants[p] = null;
37
- }
38
- else if (typeof v == 'object') {
37
+ } else if (typeof v == "object") {
39
38
  if (v.bind) {
40
- if (isUndefined(v.defaultValue) && v != pv)
41
- v.defaultValue = defaultValue(pv);
42
- if (isDefined(v.defaultValue))
43
- defaultValues[v.bind] = v.defaultValue;
39
+ if (isUndefined(v.defaultValue) && v != pv) v.defaultValue = defaultValue(pv);
40
+ if (isDefined(v.defaultValue)) defaultValues[v.bind] = v.defaultValue;
44
41
  nameMap[p] = v.bind;
45
42
  functions[p] = Binding.get(v.bind).value;
46
43
  constant = false;
47
- }
48
- else if (v.expr) {
44
+ } else if (v.expr) {
49
45
  functions[p] = Expression.get(v.expr);
50
46
  constant = false;
51
- }
52
- else if (v.get) {
47
+ } else if (v.get) {
53
48
  functions[p] = v.get;
54
49
  constant = false;
55
- }
56
- else if (v.tpl) {
50
+ } else if (v.tpl) {
57
51
  functions[p] = StringTemplate.get(v.tpl);
58
52
  constant = false;
59
- }
60
- else if (pv && typeof pv == 'object' && pv.structured) {
61
- if (isArray(v))
62
- functions[p] = getSelector(v);
53
+ } else if (pv && typeof pv == "object" && pv.structured) {
54
+ if (isArray(v)) functions[p] = getSelector(v);
63
55
  else {
64
56
  let s = getSelectorConfig(v, v, {});
65
57
  structures[p] = s;
66
58
  Object.assign(defaultValues, s.defaultValues);
67
59
  }
68
60
  constant = false;
69
- }
70
- else {
71
- if (!constants)
72
- constants = {};
61
+ } else {
62
+ if (!constants) constants = {};
73
63
  constants[p] = v;
74
64
  }
75
- }
76
- else if (isFunction(v)) {
77
- functions[p] = v;
65
+ } else if (isFunction(v)) {
66
+ if (isAccessorChain(v)) {
67
+ let path = v.toString();
68
+ nameMap[p] = path;
69
+ functions[p] = Binding.get(path).value;
70
+ } else functions[p] = v;
78
71
  constant = false;
79
- }
80
- else {
72
+ } else {
81
73
  if (isUndefined(v)) {
82
- if (isUndefined(pv))
83
- continue;
74
+ if (isUndefined(pv)) continue;
84
75
  v = defaultValue(pv);
85
76
  }
86
77
 
87
- if (isUndefined(v))
88
- continue;
78
+ if (isUndefined(v)) continue;
89
79
 
90
- if (!constants)
91
- constants = {};
80
+ if (!constants) constants = {};
92
81
 
93
82
  constants[p] = v;
94
83
  }
@@ -99,7 +88,7 @@ function getSelectorConfig(props, values, nameMap) {
99
88
  structures,
100
89
  defaultValues,
101
90
  constants,
102
- constant
91
+ constant,
103
92
  };
104
93
  }
105
94
 
@@ -110,15 +99,13 @@ function createSelector({ functions, structures, constants, defaultValues }) {
110
99
  selector[n] = functions[n];
111
100
  }
112
101
 
113
- for (let n in structures)
114
- selector[n] = createSelector(structures[n]);
102
+ for (let n in structures) selector[n] = createSelector(structures[n]);
115
103
 
116
104
  return createStructuredSelector(selector, constants);
117
105
  }
118
106
 
119
107
  export class StructuredSelector {
120
-
121
- constructor({props, values}) {
108
+ constructor({ props, values }) {
122
109
  this.nameMap = {};
123
110
  this.config = getSelectorConfig(props, values, this.nameMap);
124
111
  }
@@ -129,17 +116,16 @@ export class StructuredSelector {
129
116
 
130
117
  create() {
131
118
  let selector = createSelector(this.config);
132
- if (selector.memoize)
133
- return selector.memoize();
119
+ if (selector.memoize) return selector.memoize();
134
120
  return selector;
135
121
  }
136
122
 
137
123
  createStoreSelector() {
138
124
  if (this.config.constant) {
139
- let result = {...this.config.constants};
125
+ let result = { ...this.config.constants };
140
126
  return () => result;
141
127
  }
142
128
  let selector = this.create();
143
- return store => selector(store.getData());
129
+ return (store) => selector(store.getData());
144
130
  }
145
131
  }
@@ -1,93 +1,94 @@
1
- import {StructuredSelector} from './StructuredSelector';
2
- import assert from 'assert';
1
+ import { StructuredSelector } from "./StructuredSelector";
2
+ import assert from "assert";
3
+ import { createAccessorModelProxy } from "./createAccessorModelProxy";
3
4
 
4
- describe('StructuredSelector', function() {
5
- describe('#create()', function () {
6
- it('constants', function () {
5
+ describe("StructuredSelector", function () {
6
+ describe("#create()", function () {
7
+ it("constants", function () {
7
8
  var x = {};
8
9
  var s = new StructuredSelector({
9
10
  props: {
10
11
  a: undefined,
11
- b: undefined
12
+ b: undefined,
12
13
  },
13
14
  values: {
14
15
  a: 1,
15
- b: 2
16
- }
16
+ b: 2,
17
+ },
17
18
  }).create(x);
18
19
 
19
- assert.deepEqual(s(x), {a: 1, b: 2});
20
+ assert.deepEqual(s(x), { a: 1, b: 2 });
20
21
  });
21
22
 
22
- it('bindings', function () {
23
+ it("bindings", function () {
23
24
  var x = { a: 1, b: 2 };
24
25
  var s = new StructuredSelector({
25
26
  props: {
26
27
  a: undefined,
27
- b: undefined
28
+ b: undefined,
28
29
  },
29
30
  values: {
30
- a: { bind: 'b' },
31
- b: { bind: 'a' }
32
- }
31
+ a: { bind: "b" },
32
+ b: { bind: "a" },
33
+ },
33
34
  }).create(x);
34
35
 
35
- assert.deepEqual(s(x), {a: 2, b: 1});
36
+ assert.deepEqual(s(x), { a: 2, b: 1 });
36
37
  });
37
38
 
38
- it('templates', function () {
39
- var x = {a: 1, b: 2};
39
+ it("templates", function () {
40
+ var x = { a: 1, b: 2 };
40
41
  var s = new StructuredSelector({
41
42
  props: {
42
43
  a: undefined,
43
- b: undefined
44
+ b: undefined,
44
45
  },
45
46
  values: {
46
- a: {tpl: 'b{a}'},
47
- b: {tpl: 'a{b}'}
48
- }
47
+ a: { tpl: "b{a}" },
48
+ b: { tpl: "a{b}" },
49
+ },
49
50
  }).create(x);
50
51
 
51
- assert.deepEqual(s(x), {a: 'b1', b: 'a2'});
52
+ assert.deepEqual(s(x), { a: "b1", b: "a2" });
52
53
  });
53
54
 
54
- it('structured', function () {
55
- var x = {a: 1, b: 2};
55
+ it("structured", function () {
56
+ var x = { a: 1, b: 2 };
56
57
  var s = new StructuredSelector({
57
58
  props: {
58
59
  a: {
59
- structured: true
60
+ structured: true,
60
61
  },
61
- b: undefined
62
+ b: undefined,
62
63
  },
63
64
  values: {
64
65
  a: {
65
- x: { expr: '{a} == 1'},
66
- y: { expr: '{b} == 1'}
66
+ x: { expr: "{a} == 1" },
67
+ y: { expr: "{b} == 1" },
67
68
  },
68
- b: {tpl: 'a{b}'}
69
- }
69
+ b: { tpl: "a{b}" },
70
+ },
70
71
  }).create(x);
71
72
 
72
- assert.deepEqual(s(x), {a: { x: true, y: false }, b: 'a2'});
73
+ assert.deepEqual(s(x), { a: { x: true, y: false }, b: "a2" });
73
74
  });
74
75
  });
75
76
 
76
- it('structures do not change if data doesn\'t change', function () {
77
- var x = {a: 1, b: 2};
77
+ it("structures do not change if data doesn't change", function () {
78
+ var x = { a: 1, b: 2 };
78
79
  var s = new StructuredSelector({
79
80
  props: {
80
81
  a: {
81
- structured: true
82
- }
82
+ structured: true,
83
+ },
83
84
  },
84
85
  values: {
85
86
  a: {
86
- x: { expr: '{a} == 1'},
87
- y: { expr: '{b} == 1'}
87
+ x: { expr: "{a} == 1" },
88
+ y: { expr: "{b} == 1" },
88
89
  },
89
- b: {tpl: 'a{b}'}
90
- }
90
+ b: { tpl: "a{b}" },
91
+ },
91
92
  }).create(x);
92
93
 
93
94
  let r1 = s(x);
@@ -95,4 +96,18 @@ describe('StructuredSelector', function() {
95
96
 
96
97
  assert.equal(r1, r2);
97
98
  });
99
+
100
+ it("structures do not change if data doesn't change", function () {
101
+ var x = { a: { b: 2 } };
102
+ var m = createAccessorModelProxy();
103
+ var s = new StructuredSelector({
104
+ props: {
105
+ b: undefined,
106
+ },
107
+ values: {
108
+ b: m.a.b,
109
+ },
110
+ }).create(x);
111
+ assert.deepEqual(s(x), { b: 2 });
112
+ });
98
113
  });