@joist/templating 4.2.4-next.2 → 4.2.4-next.4
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.
- package/README.md +43 -155
- package/package.json +1 -1
- package/src/lib/elements/bind.element.ts +1 -5
- package/src/lib/elements/if.element.test.ts +176 -0
- package/src/lib/elements/if.element.ts +1 -5
- package/src/lib/elements/value.element.ts +1 -7
- package/src/lib/token.test.ts +141 -11
- package/src/lib/token.ts +143 -7
- package/target/lib/elements/bind.element.js +1 -4
- package/target/lib/elements/bind.element.js.map +1 -1
- package/target/lib/elements/if.element.js +1 -6
- package/target/lib/elements/if.element.js.map +1 -1
- package/target/lib/elements/if.element.test.js +154 -0
- package/target/lib/elements/if.element.test.js.map +1 -1
- package/target/lib/elements/value.element.js +1 -7
- package/target/lib/elements/value.element.js.map +1 -1
- package/target/lib/token.d.ts +6 -1
- package/target/lib/token.js +62 -5
- package/target/lib/token.js.map +1 -1
- package/target/lib/token.test.js +119 -4
- package/target/lib/token.test.js.map +1 -1
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ import { bind } from "@joist/templating";
|
|
|
20
20
|
|
|
21
21
|
class MyElement extends HTMLElement {
|
|
22
22
|
@bind()
|
|
23
|
-
accessor myProperty
|
|
23
|
+
accessor myProperty = "";
|
|
24
24
|
}
|
|
25
25
|
```
|
|
26
26
|
|
|
@@ -36,33 +36,13 @@ class MyElement extends HTMLElement {
|
|
|
36
36
|
@observe()
|
|
37
37
|
assessor value = "Hello World";
|
|
38
38
|
|
|
39
|
-
@bind(
|
|
39
|
+
@bind({
|
|
40
|
+
compute: (i) => i.value.toUpperCase()
|
|
41
|
+
})
|
|
40
42
|
accessor formattedValue = "";
|
|
41
43
|
}
|
|
42
44
|
```
|
|
43
45
|
|
|
44
|
-
### Token System (`token.ts`)
|
|
45
|
-
|
|
46
|
-
The `JToken` class handles parsing and evaluation of binding expressions. It supports:
|
|
47
|
-
|
|
48
|
-
NOTE: Most of the time you will not be using this yourself.
|
|
49
|
-
|
|
50
|
-
- Simple property bindings: `propertyName`
|
|
51
|
-
- Nested property access: `user.profile.name`
|
|
52
|
-
- Negation operator: `!isVisible`
|
|
53
|
-
- Array access: `items.0.name`
|
|
54
|
-
|
|
55
|
-
Example usage:
|
|
56
|
-
|
|
57
|
-
```typescript
|
|
58
|
-
const token = new JToken("user.name");
|
|
59
|
-
const value = token.readTokenValueFrom(context);
|
|
60
|
-
|
|
61
|
-
// With negation
|
|
62
|
-
const negatedToken = new JToken("!isVisible");
|
|
63
|
-
const isHidden = negatedToken.readTokenValueFrom(context);
|
|
64
|
-
```
|
|
65
|
-
|
|
66
46
|
## Built-in Template Elements
|
|
67
47
|
|
|
68
48
|
Joist provides several built-in template elements for common templating needs:
|
|
@@ -82,7 +62,7 @@ Displays a bound value as text content:
|
|
|
82
62
|
<j-val bind="user.profile.address.city"></j-val>
|
|
83
63
|
|
|
84
64
|
<!-- With array access -->
|
|
85
|
-
<j-val bind="items
|
|
65
|
+
<j-val bind="items.0.name"></j-val>
|
|
86
66
|
```
|
|
87
67
|
|
|
88
68
|
### Conditional Rendering (`j-if`)
|
|
@@ -104,6 +84,38 @@ Conditionally renders content based on a boolean expression:
|
|
|
104
84
|
</template>
|
|
105
85
|
</j-if>
|
|
106
86
|
|
|
87
|
+
<!-- With comparison operators -->
|
|
88
|
+
<j-if bind="status == active">
|
|
89
|
+
<template>
|
|
90
|
+
<div>Status is active</div>
|
|
91
|
+
</template>
|
|
92
|
+
</j-if>
|
|
93
|
+
|
|
94
|
+
<j-if bind="status != active">
|
|
95
|
+
<template>
|
|
96
|
+
<div>Status is not active</div>
|
|
97
|
+
</template>
|
|
98
|
+
</j-if>
|
|
99
|
+
|
|
100
|
+
<j-if bind="count > 5">
|
|
101
|
+
<template>
|
|
102
|
+
<div>Count is greater than 5</div>
|
|
103
|
+
</template>
|
|
104
|
+
</j-if>
|
|
105
|
+
|
|
106
|
+
<j-if bind="score < 100">
|
|
107
|
+
<template>
|
|
108
|
+
<div>Score is less than 100</div>
|
|
109
|
+
</template>
|
|
110
|
+
</j-if>
|
|
111
|
+
|
|
112
|
+
<!-- With nested paths -->
|
|
113
|
+
<j-if bind="user.score > 100">
|
|
114
|
+
<template>
|
|
115
|
+
<div>User's score is above 100</div>
|
|
116
|
+
</template>
|
|
117
|
+
</j-if>
|
|
118
|
+
|
|
107
119
|
<!-- With else template -->
|
|
108
120
|
<j-if bind="isLoggedIn">
|
|
109
121
|
<template>
|
|
@@ -119,6 +131,12 @@ The `j-if` element supports:
|
|
|
119
131
|
|
|
120
132
|
- Boolean expressions for conditional rendering
|
|
121
133
|
- Negation operator (`!`) for inverse conditions
|
|
134
|
+
- Comparison operators:
|
|
135
|
+
- Equality (`==`): `status == active`
|
|
136
|
+
- Inequality (`!=`): `status != active`
|
|
137
|
+
- Greater than (`>`): `count > 5`
|
|
138
|
+
- Less than (`<`): `score < 100`
|
|
139
|
+
- Nested property paths: `user.score > 100`
|
|
122
140
|
- Optional `else` template for fallback content
|
|
123
141
|
- Automatic cleanup of removed content
|
|
124
142
|
|
|
@@ -266,133 +284,3 @@ The `j-async` element supports:
|
|
|
266
284
|
1. **Binding Not Updating**
|
|
267
285
|
|
|
268
286
|
- Check if the property is decorated with `@bind()`
|
|
269
|
-
- Verify the binding expression is correct
|
|
270
|
-
- Ensure the property is being updated correctly
|
|
271
|
-
|
|
272
|
-
2. **List Rendering Issues**
|
|
273
|
-
|
|
274
|
-
- Verify the `key` attribute is unique and stable
|
|
275
|
-
- Check if the list items are properly structured
|
|
276
|
-
- Ensure the binding expression matches the data structure
|
|
277
|
-
|
|
278
|
-
3. **Async State Problems**
|
|
279
|
-
- Verify the Promise is properly resolved/rejected
|
|
280
|
-
- Check if all required templates are present
|
|
281
|
-
- Ensure error handling is implemented
|
|
282
|
-
|
|
283
|
-
## Manual Value Handling
|
|
284
|
-
|
|
285
|
-
You can manually handle value requests and updates by listening for the `joist::value` event. This is useful when you need more control over the binding process or want to implement custom binding logic:
|
|
286
|
-
|
|
287
|
-
```typescript
|
|
288
|
-
class MyElement extends HTMLElement {
|
|
289
|
-
connectedCallback() {
|
|
290
|
-
// Listen for value requests
|
|
291
|
-
this.addEventListener("joist::value", (e) => {
|
|
292
|
-
const token = e.token;
|
|
293
|
-
|
|
294
|
-
// Handle the value request
|
|
295
|
-
if (token.bindTo === "myValue") {
|
|
296
|
-
// Update the value
|
|
297
|
-
e.update({
|
|
298
|
-
oldValue: this.myValue,
|
|
299
|
-
newValue: this.myValue,
|
|
300
|
-
});
|
|
301
|
-
}
|
|
302
|
-
});
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
```
|
|
306
|
-
|
|
307
|
-
## Complete Example
|
|
308
|
-
|
|
309
|
-
Here's a complete todo application in a single component:
|
|
310
|
-
|
|
311
|
-
```typescript
|
|
312
|
-
import { bind } from "@joist/templating";
|
|
313
|
-
import { element, html, css, listen, query } from "@joist/element";
|
|
314
|
-
|
|
315
|
-
interface Todo {
|
|
316
|
-
id: string;
|
|
317
|
-
text: string;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
@element({
|
|
321
|
-
tagName: "todo-list",
|
|
322
|
-
shadowDom: [
|
|
323
|
-
css`
|
|
324
|
-
:host {
|
|
325
|
-
display: block;
|
|
326
|
-
max-width: 600px;
|
|
327
|
-
margin: 2rem auto;
|
|
328
|
-
}
|
|
329
|
-
.form {
|
|
330
|
-
display: flex;
|
|
331
|
-
gap: 1rem;
|
|
332
|
-
}
|
|
333
|
-
.todo-item {
|
|
334
|
-
align-items: center;
|
|
335
|
-
display: flex;
|
|
336
|
-
gap: 0.5rem;
|
|
337
|
-
margin: 0.5rem 0;
|
|
338
|
-
}
|
|
339
|
-
.todo-text {
|
|
340
|
-
flex: 1;
|
|
341
|
-
}
|
|
342
|
-
`,
|
|
343
|
-
html`
|
|
344
|
-
<form class="form">
|
|
345
|
-
<input type="text" placeholder="What needs to be done?" />
|
|
346
|
-
<button type="submit">Add</button>
|
|
347
|
-
</form>
|
|
348
|
-
|
|
349
|
-
<j-if bind="!todos.length">
|
|
350
|
-
<template>
|
|
351
|
-
<p>No todos yet!</p>
|
|
352
|
-
</template>
|
|
353
|
-
</j-if>
|
|
354
|
-
|
|
355
|
-
<j-for id="todos" bind="todos" key="id">
|
|
356
|
-
<template>
|
|
357
|
-
<div class="todo-item">
|
|
358
|
-
<j-val class="todo-text" bind="each.value.text"></j-val>
|
|
359
|
-
|
|
360
|
-
<j-bind attrs="data-id:each.value.id">
|
|
361
|
-
<button>×</button>
|
|
362
|
-
</j-bind>
|
|
363
|
-
</div>
|
|
364
|
-
</template>
|
|
365
|
-
</j-for>
|
|
366
|
-
|
|
367
|
-
<j-val bind="todos.length"></j-val> remaining
|
|
368
|
-
`,
|
|
369
|
-
],
|
|
370
|
-
})
|
|
371
|
-
export class TodoList extends HTMLElement {
|
|
372
|
-
@bind()
|
|
373
|
-
accessor todos: Todo[] = [];
|
|
374
|
-
|
|
375
|
-
#nextId = 1;
|
|
376
|
-
#input = query("input");
|
|
377
|
-
|
|
378
|
-
@listen("submit", "form")
|
|
379
|
-
onSubmit(e: SubmitEvent) {
|
|
380
|
-
e.preventDefault();
|
|
381
|
-
|
|
382
|
-
const input = this.#input();
|
|
383
|
-
|
|
384
|
-
this.todos = [...this.todos, { id: String(this.#nextId++), text: input.value.trim() }];
|
|
385
|
-
|
|
386
|
-
input.value = "";
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
@listen("click", "#todos")
|
|
390
|
-
onDelete(e: Event) {
|
|
391
|
-
if (e.target instanceof HTMLButtonElement) {
|
|
392
|
-
const id = Number(e.target.dataset.id);
|
|
393
|
-
|
|
394
|
-
this.todos = this.todos.filter((todo) => todo.id !== id);
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
```
|
package/package.json
CHANGED
|
@@ -87,11 +87,7 @@ export class JoistBindElement extends HTMLElement {
|
|
|
87
87
|
return;
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
let valueToWrite = newValue;
|
|
91
|
-
|
|
92
|
-
if (typeof newValue === "object" && newValue !== null) {
|
|
93
|
-
valueToWrite = token.readTokenValueFrom(newValue);
|
|
94
|
-
}
|
|
90
|
+
let valueToWrite = token.readTokenValueFrom(newValue);
|
|
95
91
|
|
|
96
92
|
if (token.isNegated) {
|
|
97
93
|
valueToWrite = !valueToWrite;
|
|
@@ -88,3 +88,179 @@ it("should switch between if and else templates", () => {
|
|
|
88
88
|
|
|
89
89
|
assert.equal(element.textContent?.trim(), "If Content");
|
|
90
90
|
});
|
|
91
|
+
|
|
92
|
+
it("should handle equality comparison", () => {
|
|
93
|
+
const element = fixtureSync(html`
|
|
94
|
+
<div
|
|
95
|
+
@joist::value=${(e: JoistValueEvent) => {
|
|
96
|
+
e.update({ oldValue: null, newValue: { status: "active" } });
|
|
97
|
+
}}
|
|
98
|
+
>
|
|
99
|
+
<j-if bind="example.status == active">
|
|
100
|
+
<template>Status is Active</template>
|
|
101
|
+
</j-if>
|
|
102
|
+
</div>
|
|
103
|
+
`);
|
|
104
|
+
|
|
105
|
+
assert.equal(element.textContent?.trim(), "Status is Active");
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("should handle greater than comparison", () => {
|
|
109
|
+
const element = fixtureSync(html`
|
|
110
|
+
<div
|
|
111
|
+
@joist::value=${(e: JoistValueEvent) => {
|
|
112
|
+
e.update({ oldValue: null, newValue: { count: 10 } });
|
|
113
|
+
}}
|
|
114
|
+
>
|
|
115
|
+
<j-if bind="example.count > 5">
|
|
116
|
+
<template>Count is Greater Than 5</template>
|
|
117
|
+
</j-if>
|
|
118
|
+
</div>
|
|
119
|
+
`);
|
|
120
|
+
|
|
121
|
+
assert.equal(element.textContent?.trim(), "Count is Greater Than 5");
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("should handle less than comparison", () => {
|
|
125
|
+
const element = fixtureSync(html`
|
|
126
|
+
<div
|
|
127
|
+
@joist::value=${(e: JoistValueEvent) => {
|
|
128
|
+
e.update({ oldValue: null, newValue: { score: 75 } });
|
|
129
|
+
}}
|
|
130
|
+
>
|
|
131
|
+
<j-if bind="example.score < 100">
|
|
132
|
+
<template>Score is Less Than 100</template>
|
|
133
|
+
</j-if>
|
|
134
|
+
</div>
|
|
135
|
+
`);
|
|
136
|
+
|
|
137
|
+
assert.equal(element.textContent?.trim(), "Score is Less Than 100");
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("should handle nested path comparisons", () => {
|
|
141
|
+
const element = fixtureSync(html`
|
|
142
|
+
<div
|
|
143
|
+
@joist::value=${(e: JoistValueEvent) => {
|
|
144
|
+
e.update({ oldValue: null, newValue: { user: { score: 150 } } });
|
|
145
|
+
}}
|
|
146
|
+
>
|
|
147
|
+
<j-if bind="example.user.score > 100">
|
|
148
|
+
<template>User Score is Above 100</template>
|
|
149
|
+
</j-if>
|
|
150
|
+
</div>
|
|
151
|
+
`);
|
|
152
|
+
|
|
153
|
+
assert.equal(element.textContent?.trim(), "User Score is Above 100");
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("should handle negated comparisons", () => {
|
|
157
|
+
const element = fixtureSync(html`
|
|
158
|
+
<div
|
|
159
|
+
@joist::value=${(e: JoistValueEvent) => {
|
|
160
|
+
e.update({ oldValue: null, newValue: { status: "inactive" } });
|
|
161
|
+
}}
|
|
162
|
+
>
|
|
163
|
+
<j-if bind="!example.status == active">
|
|
164
|
+
<template>Status is Not Active</template>
|
|
165
|
+
</j-if>
|
|
166
|
+
</div>
|
|
167
|
+
`);
|
|
168
|
+
|
|
169
|
+
assert.equal(element.textContent?.trim(), "Status is Not Active");
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it("should handle string number comparisons", () => {
|
|
173
|
+
const element = fixtureSync(html`
|
|
174
|
+
<div
|
|
175
|
+
@joist::value=${(e: JoistValueEvent) => {
|
|
176
|
+
e.update({ oldValue: null, newValue: { count: "10" } });
|
|
177
|
+
}}
|
|
178
|
+
>
|
|
179
|
+
<j-if bind="example.count > 5">
|
|
180
|
+
<template>String Count is Greater Than 5</template>
|
|
181
|
+
</j-if>
|
|
182
|
+
</div>
|
|
183
|
+
`);
|
|
184
|
+
|
|
185
|
+
assert.equal(element.textContent?.trim(), "String Count is Greater Than 5");
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it("should handle undefined values in comparisons", () => {
|
|
189
|
+
const element = fixtureSync(html`
|
|
190
|
+
<div
|
|
191
|
+
@joist::value=${(e: JoistValueEvent) => {
|
|
192
|
+
e.update({ oldValue: null, newValue: { count: undefined } });
|
|
193
|
+
}}
|
|
194
|
+
>
|
|
195
|
+
<j-if bind="example.count > 5">
|
|
196
|
+
<template>Count is Greater Than 5</template>
|
|
197
|
+
</j-if>
|
|
198
|
+
</div>
|
|
199
|
+
`);
|
|
200
|
+
|
|
201
|
+
assert.equal(element.textContent?.trim(), "");
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it("should handle not equal comparison", () => {
|
|
205
|
+
const element = fixtureSync(html`
|
|
206
|
+
<div
|
|
207
|
+
@joist::value=${(e: JoistValueEvent) => {
|
|
208
|
+
e.update({ oldValue: null, newValue: { status: "inactive" } });
|
|
209
|
+
}}
|
|
210
|
+
>
|
|
211
|
+
<j-if bind="example.status != active">
|
|
212
|
+
<template>Status is Not Active</template>
|
|
213
|
+
</j-if>
|
|
214
|
+
</div>
|
|
215
|
+
`);
|
|
216
|
+
|
|
217
|
+
assert.equal(element.textContent?.trim(), "Status is Not Active");
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it("should handle not equal comparison with matching value", () => {
|
|
221
|
+
const element = fixtureSync(html`
|
|
222
|
+
<div
|
|
223
|
+
@joist::value=${(e: JoistValueEvent) => {
|
|
224
|
+
e.update({ oldValue: null, newValue: { status: "active" } });
|
|
225
|
+
}}
|
|
226
|
+
>
|
|
227
|
+
<j-if bind="example.status != active">
|
|
228
|
+
<template>Status is Not Active</template>
|
|
229
|
+
</j-if>
|
|
230
|
+
</div>
|
|
231
|
+
`);
|
|
232
|
+
|
|
233
|
+
assert.equal(element.textContent?.trim(), "");
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it("should handle not equal comparison with string numbers", () => {
|
|
237
|
+
const element = fixtureSync(html`
|
|
238
|
+
<div
|
|
239
|
+
@joist::value=${(e: JoistValueEvent) => {
|
|
240
|
+
e.update({ oldValue: null, newValue: { count: "10" } });
|
|
241
|
+
}}
|
|
242
|
+
>
|
|
243
|
+
<j-if bind="example.count != 5">
|
|
244
|
+
<template>Count is Not 5</template>
|
|
245
|
+
</j-if>
|
|
246
|
+
</div>
|
|
247
|
+
`);
|
|
248
|
+
|
|
249
|
+
assert.equal(element.textContent?.trim(), "Count is Not 5");
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it("should handle not equal comparison with undefined", () => {
|
|
253
|
+
const element = fixtureSync(html`
|
|
254
|
+
<div
|
|
255
|
+
@joist::value=${(e: JoistValueEvent) => {
|
|
256
|
+
e.update({ oldValue: null, newValue: { status: undefined } });
|
|
257
|
+
}}
|
|
258
|
+
>
|
|
259
|
+
<j-if bind="example.status != active">
|
|
260
|
+
<template>Status is Not Active</template>
|
|
261
|
+
</j-if>
|
|
262
|
+
</div>
|
|
263
|
+
`);
|
|
264
|
+
|
|
265
|
+
assert.equal(element.textContent?.trim(), "Status is Not Active");
|
|
266
|
+
});
|
|
@@ -48,11 +48,7 @@ export class JoistIfElement extends HTMLElement {
|
|
|
48
48
|
this.dispatchEvent(
|
|
49
49
|
new JoistValueEvent(token, ({ newValue, oldValue }) => {
|
|
50
50
|
if (newValue !== oldValue) {
|
|
51
|
-
|
|
52
|
-
this.apply(token.readTokenValueFrom(newValue), token.isNegated);
|
|
53
|
-
} else {
|
|
54
|
-
this.apply(newValue, token.isNegated);
|
|
55
|
-
}
|
|
51
|
+
this.apply(token.readTokenValueFrom(newValue), token.isNegated);
|
|
56
52
|
}
|
|
57
53
|
}),
|
|
58
54
|
);
|
|
@@ -22,13 +22,7 @@ export class JoistValueElement extends HTMLElement {
|
|
|
22
22
|
|
|
23
23
|
this.dispatchEvent(
|
|
24
24
|
new JoistValueEvent(token, (value) => {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (typeof value.newValue === "object" && value.newValue !== null) {
|
|
28
|
-
valueToWrite = String(token.readTokenValueFrom(value.newValue));
|
|
29
|
-
} else {
|
|
30
|
-
valueToWrite = String(value.newValue);
|
|
31
|
-
}
|
|
25
|
+
const valueToWrite = String(token.readTokenValueFrom(value.newValue));
|
|
32
26
|
|
|
33
27
|
if (this.textContent !== valueToWrite) {
|
|
34
28
|
this.textContent = valueToWrite;
|
package/src/lib/token.test.ts
CHANGED
|
@@ -33,6 +33,45 @@ describe("JToken", () => {
|
|
|
33
33
|
const token = new JToken("!example.token");
|
|
34
34
|
assert.equal(token.bindTo, "example");
|
|
35
35
|
});
|
|
36
|
+
|
|
37
|
+
it("should parse equals operator value", () => {
|
|
38
|
+
const token = new JToken("example==value");
|
|
39
|
+
assert.equal(token.equalsValue, "value");
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("should handle equals operator with negation", () => {
|
|
43
|
+
const token = new JToken("!example == value");
|
|
44
|
+
assert.equal(token.equalsValue, "value");
|
|
45
|
+
assert.isTrue(token.isNegated);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("should handle equals operator with nested paths", () => {
|
|
49
|
+
const token = new JToken("example.nested == value");
|
|
50
|
+
assert.equal(token.equalsValue, "value");
|
|
51
|
+
assert.deepEqual(token.path, ["nested"]);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("should parse greater than operator value", () => {
|
|
55
|
+
const token = new JToken("example > 5");
|
|
56
|
+
assert.equal(token.gtValue, "5");
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("should parse less than operator value", () => {
|
|
60
|
+
const token = new JToken("example < 10");
|
|
61
|
+
assert.equal(token.ltValue, "10");
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("should handle greater than operator with negation", () => {
|
|
65
|
+
const token = new JToken("!example > 5");
|
|
66
|
+
assert.equal(token.gtValue, "5");
|
|
67
|
+
assert.isTrue(token.isNegated);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("should handle less than operator with nested paths", () => {
|
|
71
|
+
const token = new JToken("example.count < 10");
|
|
72
|
+
assert.equal(token.ltValue, "10");
|
|
73
|
+
assert.deepEqual(token.path, ["count"]);
|
|
74
|
+
});
|
|
36
75
|
});
|
|
37
76
|
|
|
38
77
|
describe("readTokenValueFrom", () => {
|
|
@@ -58,17 +97,108 @@ describe("JToken", () => {
|
|
|
58
97
|
assert.deepEqual(value, { foo: 42 });
|
|
59
98
|
});
|
|
60
99
|
|
|
61
|
-
it("should
|
|
62
|
-
const token = new JToken("example.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
);
|
|
100
|
+
it("should parse values from strings", () => {
|
|
101
|
+
const token = new JToken("example.length");
|
|
102
|
+
const value = token.readTokenValueFrom("42");
|
|
103
|
+
|
|
104
|
+
assert.equal(value, 2);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("should return true when equals against primative", () => {
|
|
108
|
+
const token = new JToken("example == active");
|
|
109
|
+
const value = token.readTokenValueFrom<boolean>("active");
|
|
110
|
+
assert.isTrue(value);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("should return true when equals comparison matches", () => {
|
|
114
|
+
const token = new JToken("example.status==active");
|
|
115
|
+
const obj = { status: "active" };
|
|
116
|
+
const value = token.readTokenValueFrom<boolean>(obj);
|
|
117
|
+
assert.isTrue(value);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("should return false when equals comparison does not match", () => {
|
|
121
|
+
const token = new JToken("example.status==active");
|
|
122
|
+
const obj = { status: "inactive" };
|
|
123
|
+
const value = token.readTokenValueFrom<boolean>(obj);
|
|
124
|
+
assert.isFalse(value);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("should handle equals comparison with numbers", () => {
|
|
128
|
+
const token = new JToken("example.count == 5");
|
|
129
|
+
const obj = { count: 5 };
|
|
130
|
+
const value = token.readTokenValueFrom<boolean>(obj);
|
|
131
|
+
assert.isTrue(value);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it("should handle equals comparison with nested paths", () => {
|
|
135
|
+
const token = new JToken("example.user.status == active");
|
|
136
|
+
const obj = { user: { status: "active" } };
|
|
137
|
+
const value = token.readTokenValueFrom<boolean>(obj);
|
|
138
|
+
assert.isTrue(value);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("should handle equals comparison with undefined values", () => {
|
|
142
|
+
const token = new JToken("example.status == active");
|
|
143
|
+
const obj = { status: undefined };
|
|
144
|
+
const value = token.readTokenValueFrom<boolean>(obj);
|
|
145
|
+
assert.isFalse(value);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it("should return true when greater than comparison matches", () => {
|
|
149
|
+
const token = new JToken("example.count > 5");
|
|
150
|
+
const obj = { count: 10 };
|
|
151
|
+
const value = token.readTokenValueFrom<boolean>(obj);
|
|
152
|
+
assert.isTrue(value);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("should return false when greater than comparison does not match", () => {
|
|
156
|
+
const token = new JToken("example.count > 5");
|
|
157
|
+
const obj = { count: 3 };
|
|
158
|
+
const value = token.readTokenValueFrom<boolean>(obj);
|
|
159
|
+
assert.isFalse(value);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("should return true when less than comparison matches", () => {
|
|
163
|
+
const token = new JToken("example.count < 10");
|
|
164
|
+
const obj = { count: 5 };
|
|
165
|
+
const value = token.readTokenValueFrom<boolean>(obj);
|
|
166
|
+
assert.isTrue(value);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it("should return false when less than comparison does not match", () => {
|
|
170
|
+
const token = new JToken("example.count < 10");
|
|
171
|
+
const obj = { count: 15 };
|
|
172
|
+
const value = token.readTokenValueFrom<boolean>(obj);
|
|
173
|
+
assert.isFalse(value);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it("should handle greater than comparison with string numbers", () => {
|
|
177
|
+
const token = new JToken("example.count > 5");
|
|
178
|
+
const obj = { count: "10" };
|
|
179
|
+
const value = token.readTokenValueFrom<boolean>(obj);
|
|
180
|
+
assert.isTrue(value);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it("should handle less than comparison with string numbers", () => {
|
|
184
|
+
const token = new JToken("example.count < 10");
|
|
185
|
+
const obj = { count: "5" };
|
|
186
|
+
const value = token.readTokenValueFrom<boolean>(obj);
|
|
187
|
+
assert.isTrue(value);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it("should handle greater than comparison with undefined values", () => {
|
|
191
|
+
const token = new JToken("example.count > 5");
|
|
192
|
+
const obj = { count: undefined };
|
|
193
|
+
const value = token.readTokenValueFrom<boolean>(obj);
|
|
194
|
+
assert.isFalse(value);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it("should handle less than comparison with undefined values", () => {
|
|
198
|
+
const token = new JToken("example.count < 10");
|
|
199
|
+
const obj = { count: undefined };
|
|
200
|
+
const value = token.readTokenValueFrom<boolean>(obj);
|
|
201
|
+
assert.isFalse(value);
|
|
72
202
|
});
|
|
73
203
|
});
|
|
74
204
|
});
|