Skip to content

Commit 9542a5e

Browse files
committed
Update tests for view
- Add test for hole - Merge stache with hole - Update other tests to reflect new view-based approach
1 parent e75b417 commit 9542a5e

File tree

9 files changed

+136
-122
lines changed

9 files changed

+136
-122
lines changed

typescript/packages/common-html/src/hole.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,12 @@ export type Hole = {
33
name: string;
44
};
55

6+
const holeNameRegex = /^(\w+)$/;
7+
68
export const create = (name: string): Hole => {
9+
if (name.match(holeNameRegex) == null) {
10+
throw TypeError("Template hole names must be alphanumeric");
11+
}
712
return {
813
type: "hole",
914
name,
@@ -18,4 +23,32 @@ export const isHole = (value: unknown): value is Hole => {
1823
);
1924
};
2025

21-
export const markup = (name: string) => `{{${name}}}`
26+
export const markup = (name: string) => {
27+
if (name.match(holeNameRegex) == null) {
28+
throw TypeError("Template hole names must be alphanumeric");
29+
}
30+
return `{{${name}}}`;
31+
};
32+
33+
const mustacheRegex = /{{(\w+)}}/g;
34+
35+
/** Parse mustaches in free text, returning an array of text and objects */
36+
export const parse = (text: string) => {
37+
const result = [];
38+
let lastIndex = 0;
39+
let match: RegExpMatchArray | null = null;
40+
41+
while ((match = mustacheRegex.exec(text)) !== null) {
42+
if (match.index > lastIndex) {
43+
result.push(text.slice(lastIndex, match.index));
44+
}
45+
result.push(create(match[1]));
46+
lastIndex = mustacheRegex.lastIndex;
47+
}
48+
49+
if (lastIndex < text.length) {
50+
result.push(text.slice(lastIndex));
51+
}
52+
53+
return result;
54+
};

typescript/packages/common-html/src/html.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import * as hole from "./hole.js";
2-
import { Reactive } from "./reactive.js";
32
import * as logger from "./logger.js";
43
import cid from "./cid.js";
54
import { view, View } from "./view.js";

typescript/packages/common-html/src/parser.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import sax from "sax";
2-
import parseMustaches from "./stache.js";
3-
import { isHole } from "./hole.js";
2+
import { parse as parseMustaches, isHole } from "./hole.js";
43
import { create as createVNode, VNode, Props } from "./vnode.js";
54
import * as logger from "./logger.js";
65

typescript/packages/common-html/src/stache.ts

Lines changed: 0 additions & 26 deletions
This file was deleted.
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import * as assert from "node:assert";
2+
import * as hole from "../hole.js";
3+
4+
describe("hole.markup()", () => {
5+
it("it wraps the key in curly braces", () => {
6+
const markup = hole.markup("key");
7+
assert.strictEqual(markup, "{{key}}");
8+
});
9+
10+
it("throws if key is not alphanumeric", () => {
11+
assert.throws(() => hole.markup("bad key with spaces"));
12+
});
13+
});
14+
15+
describe("hole.create()", () => {
16+
it("it creates a hole", () => {
17+
const keyHole = hole.create("key");
18+
assert.deepStrictEqual(keyHole, {
19+
type: "hole",
20+
name: "key",
21+
});
22+
});
23+
24+
it("throws if key is not alphanumeric", () => {
25+
assert.throws(() => hole.create("bad key with spaces"));
26+
});
27+
});
28+
29+
describe("hole.parse()", () => {
30+
it("parses", () => {
31+
const xml = `Hello {{world}}!`;
32+
const result = hole.parse(xml);
33+
assert.deepStrictEqual(result, ["Hello ", hole.create("world"), "!"]);
34+
});
35+
36+
it("does not parse staches with non-word characters in them", () => {
37+
const xml = `Hello {{ world }}!`;
38+
const result = hole.parse(xml);
39+
assert.deepStrictEqual(result, ["Hello {{ world }}!"]);
40+
});
41+
42+
it("handles broken staches", () => {
43+
const xml = `Hello {{world}!`;
44+
const result = hole.parse(xml);
45+
assert.deepStrictEqual(result, ["Hello {{world}!"]);
46+
});
47+
48+
it("handles broken staches 2", () => {
49+
const xml = `Hello {world}}!`;
50+
const result = hole.parse(xml);
51+
assert.deepStrictEqual(result, ["Hello {world}}!"]);
52+
});
53+
54+
it("handles broken staches 3", () => {
55+
const xml = `Hello {{wor}ld}}!`;
56+
const result = hole.parse(xml);
57+
assert.deepStrictEqual(result, ["Hello {{wor}ld}}!"]);
58+
});
59+
60+
it("handles broken staches 4", () => {
61+
const xml = `Hello {{wor}}ld}}!`;
62+
const result = hole.parse(xml);
63+
assert.deepStrictEqual(result, ["Hello ", hole.create("wor"), "ld}}!"]);
64+
});
65+
});
Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,26 @@
1-
import { deepStrictEqual } from "node:assert";
1+
import * as assert from "node:assert";
22
import html from "../html.js";
3-
import * as node from "../node.js";
43
import * as hole from "../hole.js";
5-
import state from "../state.js";
4+
import { state, stream } from "../state.js";
65

76
describe("html", () => {
87
it("parses tagged template string into a Renderable", () => {
9-
const clicks = state<Event | null>('clicks', null);
10-
const text = state('text', 'Hello world!');
8+
const clicks = stream<Event>();
9+
const text = state("Hello world!");
1110

12-
const renderable = html`
13-
<div class="container" hidden={{hidden}}>
11+
const view = html`
12+
<div class="container" hidden="{{hidden}}">
1413
<button id="foo" onclick=${clicks}>${text}</button>
1514
</div>
1615
`;
1716

18-
deepStrictEqual(
19-
renderable.template,
20-
node.create(
21-
"div",
22-
{ "class": "container", hidden: hole.create("hidden") },
23-
[
24-
node.create(
25-
"button",
26-
{ id: "foo", onclick: hole.create("clicks") },
27-
[
28-
hole.create("text"),
29-
]
30-
),
31-
],
32-
)
33-
);
17+
// @ts-ignore - ignore for test
18+
assert.strict(hole.isHole(view.template.props.hidden));
19+
20+
// @ts-ignore - ignore for test
21+
assert.strict(hole.isHole(view.template.children[0].props.onclick));
22+
23+
// @ts-ignore - ignore for test
24+
assert.strict(hole.isHole(view.template.children[0].children[0]));
3425
});
35-
});
26+
});
Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { deepStrictEqual } from "node:assert";
22
import parse from "../parser.js";
3-
import * as node from "../node.js";
3+
import * as vnode from "../vnode.js";
44
import * as hole from "../hole.js";
55

66
describe("parse", () => {
@@ -15,25 +15,19 @@ describe("parse", () => {
1515

1616
deepStrictEqual(
1717
root,
18-
node.create(
19-
"documentfragment",
20-
{},
21-
[
22-
node.create(
23-
"div",
24-
{ class: "container", hidden: hole.create("hidden") },
25-
[
26-
node.create(
27-
"button",
28-
{ id: "foo", onclick: hole.create("click") },
29-
[
30-
"Hello world!",
31-
]
32-
),
33-
]
34-
),
35-
],
36-
)
18+
vnode.create("documentfragment", {}, [
19+
vnode.create(
20+
"div",
21+
{ class: "container", hidden: hole.create("hidden") },
22+
[
23+
vnode.create(
24+
"button",
25+
{ id: "foo", onclick: hole.create("click") },
26+
["Hello world!"],
27+
),
28+
],
29+
),
30+
]),
3731
);
3832
});
39-
});
33+
});

typescript/packages/common-html/src/test/reactive.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ import { effect, isReactive } from "../reactive.js";
44

55
describe("isReactive", () => {
66
it("returns true for any object with a sink method", () => {
7-
const a = state("value", 0);
7+
const a = state(0);
88
assertEqual(isReactive(a), true);
99

1010
class B {
1111
sink() {}
1212
}
1313

1414
assertEqual(isReactive(new B()), true);
15-
})
15+
});
1616

1717
it("returns false for objects without a sink method", () => {
1818
assertEqual(isReactive({}), false);
@@ -29,7 +29,7 @@ describe("effect", () => {
2929
});
3030

3131
it("subscribes callback to `sink` for reactive value", () => {
32-
const value = state("value", 10);
32+
const value = state(10);
3333

3434
let valueMut = 0;
3535
effect(value, (value: number) => {
@@ -43,13 +43,13 @@ describe("effect", () => {
4343
});
4444

4545
it("ends subscription to reactive value when cancel is called", () => {
46-
const value = state("value", 10);
46+
const value = state(10);
4747

4848
let valueMut = 0;
4949
const cancel = effect(value, (value: number) => {
5050
valueMut = value;
5151
});
52-
52+
5353
value.send(11);
5454
cancel();
5555
value.send(12);
@@ -71,4 +71,4 @@ describe("effect", () => {
7171
cancel();
7272
assertEqual(calls, 1);
7373
});
74-
});
74+
});

typescript/packages/common-html/src/test/stache.test.ts

Lines changed: 0 additions & 41 deletions
This file was deleted.

0 commit comments

Comments
 (0)