tutuca 0.9.71 → 0.9.72

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tutuca",
3
- "version": "0.9.71",
3
+ "version": "0.9.72",
4
4
  "type": "module",
5
5
  "description": "Zero-dependency SPA framework with immutable state and virtual DOM",
6
6
  "main": "./dist/tutuca.js",
@@ -13,6 +13,8 @@
13
13
  "./ext": "./dist/tutuca.ext.js",
14
14
  "./extra-ext": "./dist/tutuca-extra.ext.js",
15
15
  "./dev-ext": "./dist/tutuca-dev.ext.js",
16
+ "./immutable": "./dist/immutable.js",
17
+ "./chai": "./dist/chai.js",
16
18
  "./package.json": "./package.json"
17
19
  },
18
20
  "bin": {
@@ -49,6 +51,8 @@
49
51
  "dist/tutuca.ext.js",
50
52
  "dist/tutuca-extra.ext.js",
51
53
  "dist/tutuca-dev.ext.js",
54
+ "dist/immutable.js",
55
+ "dist/chai.js",
52
56
  "skill"
53
57
  ],
54
58
  "dependencies": {
@@ -56,9 +60,13 @@
56
60
  "prettier": "^3.0.0"
57
61
  },
58
62
  "peerDependencies": {
63
+ "chai": "^6.2.2",
59
64
  "immutable": "*"
60
65
  },
61
66
  "peerDependenciesMeta": {
67
+ "chai": {
68
+ "optional": true
69
+ },
62
70
  "immutable": {
63
71
  "optional": true
64
72
  }
@@ -158,25 +158,25 @@ export function getTests({ describe, test, expect }) {
158
158
  describe("inc()", () => { // method
159
159
  test("returns a Counter with count + 1", () => {
160
160
  const next = Counter.make().inc();
161
- expect(next).to.be.instanceOf(Counter.Class);
162
- expect(next.count).to.equal(1);
161
+ expect(next).toBeInstanceOf(Counter.Class);
162
+ expect(next.count).toBe(1);
163
163
  });
164
164
  test("does not mutate the original instance", () => {
165
165
  const c = Counter.make({ count: 7 });
166
166
  c.inc();
167
- expect(c.count).to.equal(7); // immutability
167
+ expect(c.count).toBe(7); // immutability
168
168
  });
169
169
  });
170
170
 
171
171
  describe("dec()", () => { // input handler
172
172
  test("returns a Counter with count - 1", () => {
173
173
  const next = Counter.input.dec.call(Counter.make());
174
- expect(next.count).to.equal(-1);
174
+ expect(next.count).toBe(-1);
175
175
  });
176
176
  });
177
177
 
178
178
  test("inc and dec round-trip", () => { // untagged path
179
- expect(Counter.input.dec.call(Counter.make().inc()).count).to.equal(0);
179
+ expect(Counter.input.dec.call(Counter.make().inc()).count).toBe(0);
180
180
  });
181
181
  });
182
182
  }
@@ -514,6 +514,17 @@ input: { onPick(detail) { return this.setCurrent(detail.unicode); } }
514
514
  view: html`<emoji-picker @on.emoji-click="onPick value"></emoji-picker>`,
515
515
  ```
516
516
 
517
+ Handle these events declaratively with `@on.<event-name>` in the view —
518
+ don't grab the node from host/glue code and `addEventListener` on it. A
519
+ listener attached from outside the component runs outside the handler
520
+ model: no `return this.set…()`, no transactor batching, and the mutation
521
+ is invisible to the component that owns the state (the same hazard as
522
+ reaching into `app.state` directly). For any event with a real element in
523
+ the tree, `@on.` is the only entry point you need. Genuinely external
524
+ inbound sources (WebSocket, `postMessage`, timers) have no element to bind
525
+ — route those through `app.sendAtRoot` instead (see
526
+ [request-response.md](./request-response.md)).
527
+
517
528
  Pitfall: binding camelCase JS properties on a custom element silently
518
529
  fails. `:mapId=".id"` does *not* invoke a `set mapId` setter
519
530
  — the HTML parser lowercased the attribute name, so the framework assigns
@@ -16,13 +16,19 @@ A module opts into `tutuca test` by exporting `getTests`:
16
16
  export function getTests({ describe, test, expect }) {
17
17
  describe(MyComp, () => {
18
18
  test("does the thing", () => {
19
- expect(MyComp.make().doTheThing().count).to.equal(1);
19
+ expect(MyComp.make().doTheThing().count).toBe(1);
20
20
  });
21
21
  });
22
22
  }
23
23
  ```
24
24
 
25
- - `expect` is chai.
25
+ - `expect` is chai, extended with **jest-style matchers** (`toBe`,
26
+ `toEqual`, `toContain`, `toThrow`, `.not.toBe`, …) — the recommended
27
+ style. Chai's BDD chain (`expect(x).to.equal(1)`) still works for those
28
+ who prefer it. Run `tutuca help` for the full matcher list (it's
29
+ surfaced from code). Asymmetric/mock matchers
30
+ (`expect.objectContaining`, `toHaveBeenCalled…`, `toMatchSnapshot`) are
31
+ **not** available — tutuca has no mocking layer.
26
32
  - `test` and `describe` are **Tutuca's own** subset of the common
27
33
  Mocha/Bun-style API, injected by `tutuca test` — not Bun's built-ins.
28
34
  Available calls: `describe(title, fn)`, `describe(Component, fn)`,
@@ -147,7 +153,7 @@ test("filters and enriches", () => {
147
153
  when: "keepEven",
148
154
  enrichWith: "addLabel",
149
155
  });
150
- expect(r).to.deep.equal([
156
+ expect(r).toEqual([
151
157
  { key: 0, value: 10, label: "0/4: 10" },
152
158
  { key: 2, value: 30, label: "2/4: 30" },
153
159
  ]);
@@ -211,8 +217,8 @@ input: { setCount(n) { return this.setCount(n); } }
211
217
  At test time, the "good" forms become trivial:
212
218
 
213
219
  ```js
214
- expect(MyComp.make().setName("Ada").name).to.equal("Ada");
215
- expect(MyComp.input.setCount.call(MyComp.make(), 42).count).to.equal(42);
220
+ expect(MyComp.make().setName("Ada").name).toBe("Ada");
221
+ expect(MyComp.input.setCount.call(MyComp.make(), 42).count).toBe(42);
216
222
  ```
217
223
 
218
224
  The "bad" forms force every test to construct
@@ -237,31 +243,31 @@ export function getTests({ describe, test, expect }) {
237
243
  describe(Counter, () => {
238
244
  describe("inc()", () => { // method
239
245
  test("returns a Counter with count + 1", () => {
240
- expect(Counter.make().inc().count).to.equal(1);
246
+ expect(Counter.make().inc().count).toBe(1);
241
247
  });
242
248
  test("does not mutate the original instance", () => {
243
249
  const c = Counter.make({ count: 7 });
244
250
  c.inc();
245
- expect(c.count).to.equal(7);
251
+ expect(c.count).toBe(7);
246
252
  });
247
253
  });
248
254
 
249
255
  describe("dec()", () => { // input handler, no args
250
256
  test("returns a Counter with count - 1", () => {
251
257
  const next = Counter.input.dec.call(Counter.make());
252
- expect(next.count).to.equal(-1);
258
+ expect(next.count).toBe(-1);
253
259
  });
254
260
  });
255
261
 
256
262
  describe("setCount()", () => { // input handler, valueAsInt
257
263
  test("sets the count from a parsed int", () => {
258
264
  const next = Counter.input.setCount.call(Counter.make(), 42);
259
- expect(next.count).to.equal(42);
265
+ expect(next.count).toBe(42);
260
266
  });
261
267
  });
262
268
 
263
269
  test("inc and dec round-trip", () => { // untagged, inherits Counter
264
- expect(Counter.input.dec.call(Counter.make().inc()).count).to.equal(0);
270
+ expect(Counter.input.dec.call(Counter.make().inc()).count).toBe(0);
265
271
  });
266
272
  });
267
273
  }