@roku-sdk/components 0.0.0-stage → 2.19.0
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/BREAKING_CHANGES.md +84 -0
- package/LICENSE +21 -0
- package/README.md +19 -2
- package/core-jsx.d.ts +53 -0
- package/eslint.config.mjs +3 -0
- package/jsx.d.ts +273 -0
- package/package.json +85 -3
- package/src/__tests__/container.test.tsx +150 -0
- package/src/__tests__/effect-gradient-validation.test.tsx +133 -0
- package/src/__tests__/glyphs.test.ts +202 -0
- package/src/__tests__/modal-boundary-export.test.ts +12 -0
- package/src/__tests__/native-modal.test.tsx +224 -0
- package/src/__tests__/tween-release.test.tsx +125 -0
- package/src/__tests__/tween-stops-shape.test.tsx +165 -0
- package/src/__tests__/tween-without-compat.test.tsx +64 -0
- package/src/__tests__/tween.test.tsx +195 -0
- package/src/blended-busy-spinner.props.ts +37 -0
- package/src/blended-busy-spinner.tsx +13 -0
- package/src/bounding-tracker.props.ts +18 -0
- package/src/bounding-tracker.tsx +13 -0
- package/src/button-row.tsx +126 -0
- package/src/channel-store.props.ts +425 -0
- package/src/channel-store.tsx +76 -0
- package/src/container.tsx +130 -0
- package/src/core/jsx-runtime.ts +39 -0
- package/src/focusable-group.props.ts +8 -0
- package/src/focusable-group.tsx +17 -0
- package/src/focusable.props.ts +14 -0
- package/src/focusable.tsx +17 -0
- package/src/glyphs.ts +116 -0
- package/src/helpers/index.tsx +4 -0
- package/src/helpers/resolution-context.tsx +68 -0
- package/src/helpers/scale-for-device-resolution.ts +31 -0
- package/src/index.tsx +70 -0
- package/src/jsx-props.ts +87 -0
- package/src/kdfs.d.ts +14 -0
- package/src/kdfs.ts +15 -0
- package/src/key-handler.props.ts +33 -0
- package/src/key-handler.tsx +24 -0
- package/src/modal-dialog.props.ts +38 -0
- package/src/modal-dialog.tsx +41 -0
- package/src/retain-asset.ts +24 -0
- package/src/sg-intrinsics.json +1153 -0
- package/src/sg-nodes/__tests__/__snapshots__/font.test.tsx.snap +13 -0
- package/src/sg-nodes/__tests__/__snapshots__/prop-forwarding.test.tsx.snap +4409 -0
- package/src/sg-nodes/__tests__/core-props.test.ts +301 -0
- package/src/sg-nodes/__tests__/font.test.tsx +141 -0
- package/src/sg-nodes/__tests__/imperative-methods.test.ts +276 -0
- package/src/sg-nodes/__tests__/prop-forwarding.test.tsx +1049 -0
- package/src/sg-nodes/__tests__/solid-integration.test.tsx +719 -0
- package/src/sg-nodes/animated-image.props.ts +64 -0
- package/src/sg-nodes/animated-image.tsx +23 -0
- package/src/sg-nodes/animation.props.ts +292 -0
- package/src/sg-nodes/animation.tsx +190 -0
- package/src/sg-nodes/array-grid.ts +602 -0
- package/src/sg-nodes/audio.props.ts +302 -0
- package/src/sg-nodes/audio.tsx +57 -0
- package/src/sg-nodes/busy-spinner.props.ts +59 -0
- package/src/sg-nodes/busy-spinner.tsx +84 -0
- package/src/sg-nodes/button-group.props.ts +141 -0
- package/src/sg-nodes/button-group.tsx +30 -0
- package/src/sg-nodes/button.props.ts +119 -0
- package/src/sg-nodes/button.tsx +31 -0
- package/src/sg-nodes/check-list.props.ts +71 -0
- package/src/sg-nodes/check-list.tsx +55 -0
- package/src/sg-nodes/content-node.props.ts +1316 -0
- package/src/sg-nodes/content-node.tsx +153 -0
- package/src/sg-nodes/core-props.ts +197 -0
- package/src/sg-nodes/custom-keyboard.props.ts +108 -0
- package/src/sg-nodes/custom-keyboard.tsx +48 -0
- package/src/sg-nodes/dialog.props.ts +159 -0
- package/src/sg-nodes/dialog.tsx +36 -0
- package/src/sg-nodes/dynamic-key-grid.props.ts +22 -0
- package/src/sg-nodes/dynamic-key-grid.tsx +16 -0
- package/src/sg-nodes/dynamic-keyboard.props.ts +30 -0
- package/src/sg-nodes/dynamic-keyboard.tsx +18 -0
- package/src/sg-nodes/dynamic-mini-keyboard.props.ts +22 -0
- package/src/sg-nodes/dynamic-mini-keyboard.tsx +18 -0
- package/src/sg-nodes/dynamic-pinpad.props.ts +57 -0
- package/src/sg-nodes/dynamic-pinpad.tsx +18 -0
- package/src/sg-nodes/effect.props.ts +119 -0
- package/src/sg-nodes/effect.tsx +167 -0
- package/src/sg-nodes/font.props.ts +45 -0
- package/src/sg-nodes/font.tsx +80 -0
- package/src/sg-nodes/group.props.ts +105 -0
- package/src/sg-nodes/group.tsx +30 -0
- package/src/sg-nodes/label-list.props.ts +86 -0
- package/src/sg-nodes/label-list.tsx +64 -0
- package/src/sg-nodes/label.props.ts +207 -0
- package/src/sg-nodes/label.tsx +56 -0
- package/src/sg-nodes/layout-group.props.ts +69 -0
- package/src/sg-nodes/layout-group.tsx +75 -0
- package/src/sg-nodes/list-panel.props.ts +103 -0
- package/src/sg-nodes/list-panel.tsx +49 -0
- package/src/sg-nodes/markup-grid.props.ts +78 -0
- package/src/sg-nodes/markup-grid.tsx +38 -0
- package/src/sg-nodes/markup-list.props.ts +66 -0
- package/src/sg-nodes/markup-list.tsx +38 -0
- package/src/sg-nodes/mask-group.props.ts +78 -0
- package/src/sg-nodes/mask-group.tsx +59 -0
- package/src/sg-nodes/media-playback-methods.ts +20 -0
- package/src/sg-nodes/node.ts +77 -0
- package/src/sg-nodes/overhang.props.ts +169 -0
- package/src/sg-nodes/overhang.tsx +29 -0
- package/src/sg-nodes/panel-set.props.ts +71 -0
- package/src/sg-nodes/panel-set.tsx +53 -0
- package/src/sg-nodes/panel.props.ts +176 -0
- package/src/sg-nodes/panel.tsx +56 -0
- package/src/sg-nodes/poster-grid.props.ts +277 -0
- package/src/sg-nodes/poster-grid.tsx +45 -0
- package/src/sg-nodes/poster.props.ts +125 -0
- package/src/sg-nodes/poster.tsx +44 -0
- package/src/sg-nodes/radio-button-list.props.ts +59 -0
- package/src/sg-nodes/radio-button-list.tsx +68 -0
- package/src/sg-nodes/rectangle.props.ts +29 -0
- package/src/sg-nodes/rectangle.tsx +27 -0
- package/src/sg-nodes/row-list.props.ts +288 -0
- package/src/sg-nodes/row-list.tsx +47 -0
- package/src/sg-nodes/scene.props.ts +65 -0
- package/src/sg-nodes/scene.tsx +15 -0
- package/src/sg-nodes/scrollable-text.props.ts +133 -0
- package/src/sg-nodes/scrollable-text.tsx +64 -0
- package/src/sg-nodes/scrolling-label.props.ts +106 -0
- package/src/sg-nodes/scrolling-label.tsx +42 -0
- package/src/sg-nodes/simple-label.props.ts +156 -0
- package/src/sg-nodes/simple-label.tsx +59 -0
- package/src/sg-nodes/sound-effect.props.ts +130 -0
- package/src/sg-nodes/sound-effect.tsx +65 -0
- package/src/sg-nodes/standard-dialog.props.ts +520 -0
- package/src/sg-nodes/standard-dialog.tsx +428 -0
- package/src/sg-nodes/standard-keyboard-dialog.props.ts +84 -0
- package/src/sg-nodes/standard-keyboard-dialog.tsx +58 -0
- package/src/sg-nodes/standard-message-dialog.props.ts +56 -0
- package/src/sg-nodes/standard-message-dialog.tsx +47 -0
- package/src/sg-nodes/standard-pinpad-dialog.props.ts +64 -0
- package/src/sg-nodes/standard-pinpad-dialog.tsx +44 -0
- package/src/sg-nodes/standard-progress-dialog.props.ts +30 -0
- package/src/sg-nodes/standard-progress-dialog.tsx +40 -0
- package/src/sg-nodes/target-group.props.ts +278 -0
- package/src/sg-nodes/target-group.tsx +63 -0
- package/src/sg-nodes/target-list.props.ts +128 -0
- package/src/sg-nodes/target-list.tsx +66 -0
- package/src/sg-nodes/target-set.props.ts +63 -0
- package/src/sg-nodes/target-set.tsx +42 -0
- package/src/sg-nodes/time-grid.props.ts +646 -0
- package/src/sg-nodes/time-grid.tsx +87 -0
- package/src/sg-nodes/tween.props.ts +115 -0
- package/src/sg-nodes/types.ts +150 -0
- package/src/sg-nodes/video.props.ts +642 -0
- package/src/sg-nodes/video.tsx +75 -0
- package/src/sg-nodes/voice-text-edit-box.props.ts +91 -0
- package/src/sg-nodes/voice-text-edit-box.tsx +15 -0
- package/src/sg-nodes/zoom-row-list.props.ts +416 -0
- package/src/sg-nodes/zoom-row-list.tsx +42 -0
- package/src/standard-dialog-next/__tests__/standard-dialog-next.test.tsx +186 -0
- package/src/standard-dialog-next/index.ts +5 -0
- package/src/standard-dialog-next/standard-dialog-next.props.ts +80 -0
- package/src/standard-dialog-next/standard-dialog-next.tsx +326 -0
- package/src/test-helpers/tween-test-setup.ts +78 -0
- package/src/tween.tsx +173 -0
- package/src/types.ts +8 -0
- package/src/xml-components/blended-busy-spinner/BlendedBusySpinner.brs +13 -0
- package/src/xml-components/blended-busy-spinner/BlendedBusySpinner.xml +10 -0
- package/src/xml-components/blended-busy-spinner/BlendedBusySpinner.xml.d.ts +42 -0
- package/src/xml-components/bounding-tracker/BoundingTracker.brs +26 -0
- package/src/xml-components/bounding-tracker/BoundingTracker.xml +14 -0
- package/src/xml-components/bounding-tracker/BoundingTracker.xml.d.ts +42 -0
- package/src/xml-components/channel-store/channelStoreExt.brs +159 -0
- package/src/xml-components/channel-store/channelStoreExt.xml +29 -0
- package/src/xml-components/channel-store/channelStoreExt.xml.d.ts +57 -0
- package/src/xml-components/focus/focusable-group.brs +251 -0
- package/src/xml-components/focus/focusable-group.xml +15 -0
- package/src/xml-components/focus/focusable-group.xml.d.ts +42 -0
- package/src/xml-components/focus/focusable.xml +18 -0
- package/src/xml-components/focus/focusable.xml.d.ts +48 -0
- package/src/xml-components/keyboards/CustomAddressKDF.keyboard.json +547 -0
- package/src/xml-components/keyboards/CustomKeyboard.brs +322 -0
- package/src/xml-components/keyboards/CustomKeyboard.xml +45 -0
- package/src/xml-components/keyboards/CustomKeyboard.xml.d.ts +73 -0
- package/src/xml-components/keyboards/KeyGridExt.xml +4 -0
- package/src/xml-components/keyboards/KeyGridExt.xml.d.ts +37 -0
- package/src/xml-components/keyboards/KeyboardExt.xml +4 -0
- package/src/xml-components/keyboards/KeyboardExt.xml.d.ts +37 -0
- package/src/xml-components/keyboards/MOPPinPadCustomKDF.keyboard.json +151 -0
- package/src/xml-components/keyboards/MiniKeyboardExt.xml +4 -0
- package/src/xml-components/keyboards/MiniKeyboardExt.xml.d.ts +37 -0
- package/src/xml-components/keyboards/PhonePadCustomKDF.keyboard.json +42 -0
- package/src/xml-components/keyboards/PinPadExt.brs +102 -0
- package/src/xml-components/keyboards/PinPadExt.xml +17 -0
- package/src/xml-components/keyboards/PinPadExt.xml.d.ts +49 -0
- package/src/xml-components/keyboards/WifiCustomKDF.keyboard.json +1242 -0
- package/src/xml-components/keyhandler/KeyHandler.brs +30 -0
- package/src/xml-components/keyhandler/KeyHandler.xml +16 -0
- package/src/xml-components/keyhandler/KeyHandler.xml.d.ts +45 -0
- package/src/xml-components/managed-list-panel/ManagedListPanel.brs +137 -0
- package/src/xml-components/managed-list-panel/ManagedListPanel.xml +10 -0
- package/src/xml-components/managed-list-panel/ManagedListPanel.xml.d.ts +37 -0
- package/src/xml-components/modal/Modal.brs +105 -0
- package/src/xml-components/modal/Modal.xml +22 -0
- package/src/xml-components/modal/Modal.xml.d.ts +54 -0
- package/src/xml-components/modal/StandardDialogExt.xml +10 -0
- package/src/xml-components/modal/StandardDialogExt.xml.d.ts +37 -0
- package/src/xml-components/modal/StandardKeyboardDialogExt.xml +10 -0
- package/src/xml-components/modal/StandardKeyboardDialogExt.xml.d.ts +37 -0
- package/src/xml-components/modal/StandardMessageDialogExt.xml +10 -0
- package/src/xml-components/modal/StandardMessageDialogExt.xml.d.ts +37 -0
- package/src/xml-components/modal/StandardPinPadDialogExt.xml +10 -0
- package/src/xml-components/modal/StandardPinPadDialogExt.xml.d.ts +37 -0
- package/src/xml-components/modal/StandardProgressDialogExt.xml +10 -0
- package/src/xml-components/modal/StandardProgressDialogExt.xml.d.ts +37 -0
- package/src/xml-components/modal/tests/Modal.test.brs +195 -0
- package/tsconfig.json +10 -0
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
// Copyright (c) 2026 Roku, Inc.
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
5
|
+
|
|
6
|
+
// Side-effect imports: loading these modules triggers their registerImperativeMethods calls.
|
|
7
|
+
|
|
8
|
+
import "../video";
|
|
9
|
+
import "../audio";
|
|
10
|
+
import "../sound-effect";
|
|
11
|
+
import "../animation";
|
|
12
|
+
|
|
13
|
+
import fs from "node:fs";
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
|
|
16
|
+
import { beforeEach, describe, expect, it, jest } from "@jest/globals";
|
|
17
|
+
import { TmNodeRef } from "@roku-sdk/interfaces";
|
|
18
|
+
import { createDomNode, setSetPropertyHandler } from "@roku-sdk/solid/src/dom-node";
|
|
19
|
+
|
|
20
|
+
describe("imperative methods", () => {
|
|
21
|
+
let mockSetProperty: jest.Mock;
|
|
22
|
+
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
mockSetProperty = jest.fn();
|
|
25
|
+
setSetPropertyHandler(mockSetProperty);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe("video", () => {
|
|
29
|
+
it("should have play, pause, stop, resume, replay, prebuffer, skipcontent methods", () => {
|
|
30
|
+
const node = createDomNode("video", { id: 1 } as TmNodeRef) as any;
|
|
31
|
+
|
|
32
|
+
for (const method of ["play", "pause", "stop", "resume", "replay", "prebuffer", "skipcontent"]) {
|
|
33
|
+
expect(typeof node[method]).toBe("function");
|
|
34
|
+
node[method]();
|
|
35
|
+
expect(mockSetProperty).toHaveBeenLastCalledWith(node, "control", method);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
expect(mockSetProperty).toHaveBeenCalledTimes(7);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("should have seekTo method that sets seek property", () => {
|
|
42
|
+
const node = createDomNode("video", { id: 1 } as TmNodeRef) as any;
|
|
43
|
+
|
|
44
|
+
expect(typeof node.seekTo).toBe("function");
|
|
45
|
+
node.seekTo(120);
|
|
46
|
+
expect(mockSetProperty).toHaveBeenCalledWith(node, "seek", 120);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("should have setNextContentIndex method that sets nextContentIndex property", () => {
|
|
50
|
+
const node = createDomNode("video", { id: 1 } as TmNodeRef) as any;
|
|
51
|
+
|
|
52
|
+
expect(typeof node.setNextContentIndex).toBe("function");
|
|
53
|
+
node.setNextContentIndex(3);
|
|
54
|
+
expect(mockSetProperty).toHaveBeenCalledWith(node, "nextContentIndex", 3);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe("audio", () => {
|
|
59
|
+
it("should have play, pause, stop, resume, replay, prebuffer, skipcontent methods", () => {
|
|
60
|
+
const node = createDomNode("audio", { id: 1 } as TmNodeRef) as any;
|
|
61
|
+
|
|
62
|
+
for (const method of ["play", "pause", "stop", "resume", "replay", "prebuffer", "skipcontent"]) {
|
|
63
|
+
expect(typeof node[method]).toBe("function");
|
|
64
|
+
node[method]();
|
|
65
|
+
expect(mockSetProperty).toHaveBeenLastCalledWith(node, "control", method);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
expect(mockSetProperty).toHaveBeenCalledTimes(7);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("should have seekTo method that sets seek property", () => {
|
|
72
|
+
const node = createDomNode("audio", { id: 1 } as TmNodeRef) as any;
|
|
73
|
+
|
|
74
|
+
expect(typeof node.seekTo).toBe("function");
|
|
75
|
+
node.seekTo(60);
|
|
76
|
+
expect(mockSetProperty).toHaveBeenCalledWith(node, "seek", 60);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("should have setNextContentIndex method that sets nextContentIndex property", () => {
|
|
80
|
+
const node = createDomNode("audio", { id: 1 } as TmNodeRef) as any;
|
|
81
|
+
|
|
82
|
+
expect(typeof node.setNextContentIndex).toBe("function");
|
|
83
|
+
node.setNextContentIndex(5);
|
|
84
|
+
expect(mockSetProperty).toHaveBeenCalledWith(node, "nextContentIndex", 5);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe("soundEffect", () => {
|
|
89
|
+
it("should have play and stop methods", () => {
|
|
90
|
+
const node = createDomNode("soundEffect", { id: 1 } as TmNodeRef) as any;
|
|
91
|
+
|
|
92
|
+
for (const method of ["play", "stop"]) {
|
|
93
|
+
expect(typeof node[method]).toBe("function");
|
|
94
|
+
node[method]();
|
|
95
|
+
expect(mockSetProperty).toHaveBeenLastCalledWith(node, "control", method);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
expect(mockSetProperty).toHaveBeenCalledTimes(2);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("should not have pause or resume methods", () => {
|
|
102
|
+
const node = createDomNode("soundEffect", { id: 1 } as TmNodeRef) as any;
|
|
103
|
+
|
|
104
|
+
expect(node.pause).toBeUndefined();
|
|
105
|
+
expect(node.resume).toBeUndefined();
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// `registerImperativeMethods` keys an exact-match Map by tag string, and the tag it is
|
|
110
|
+
// given is a plain string literal that no compiler check ties to the intrinsic the
|
|
111
|
+
// component renders. Renaming one and not the other type-checks, passes every other
|
|
112
|
+
// test, and silently drops the methods at runtime -- which is exactly what happened
|
|
113
|
+
// when `soundeffect` became `soundEffect`. These assert the registered tag against the
|
|
114
|
+
// tag actually rendered, so the two cannot drift apart again unnoticed.
|
|
115
|
+
describe("registered tags match the rendered intrinsics", () => {
|
|
116
|
+
it.each([
|
|
117
|
+
["video", ["play", "pause", "stop", "resume", "replay", "prebuffer", "skipcontent"]],
|
|
118
|
+
["audio", ["play", "pause", "stop", "resume"]],
|
|
119
|
+
["soundEffect", ["play", "stop"]],
|
|
120
|
+
["animation", ["start", "setDuration", "setEaseFunction"]],
|
|
121
|
+
["sequentialAnimation", ["start", "setDuration", "setEaseFunction"]],
|
|
122
|
+
["floatFieldInterpolator", ["setKeyValue", "setFieldToInterp"]],
|
|
123
|
+
["colorFieldInterpolator", ["setKeyValue", "setFieldToInterp"]],
|
|
124
|
+
["vector2dFieldInterpolator", ["setKeyValue", "setFieldToInterp"]]
|
|
125
|
+
])("%s exposes its imperative methods", (tag, methods) => {
|
|
126
|
+
const node = createDomNode(tag as string, { id: 1 } as TmNodeRef) as any;
|
|
127
|
+
|
|
128
|
+
for (const method of methods as string[]) {
|
|
129
|
+
expect(typeof node[method]).toBe("function");
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("registers nothing under the pre-rename lowercase tags", () => {
|
|
134
|
+
for (const stale of ["soundeffect", "sequentialanimation"]) {
|
|
135
|
+
const node = createDomNode(stale, { id: 1 } as TmNodeRef) as any;
|
|
136
|
+
|
|
137
|
+
expect(node.play).toBeUndefined();
|
|
138
|
+
expect(node.start).toBeUndefined();
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// Re-targeting a running animation needs duration, easeFunction and keyValue to reach
|
|
144
|
+
// the node before the surrounding stop/start. These setters exist so the caller can
|
|
145
|
+
// order those writes itself rather than inheriting the reactive props' deferral.
|
|
146
|
+
describe("animation re-target setters", () => {
|
|
147
|
+
it.each(["animation", "sequentialAnimation"])("%s setDuration writes duration", tag => {
|
|
148
|
+
const node = createDomNode(tag, { id: 1 } as TmNodeRef) as any;
|
|
149
|
+
|
|
150
|
+
node.setDuration(0.35);
|
|
151
|
+
expect(mockSetProperty).toHaveBeenCalledWith(node, "duration", 0.35);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it.each(["animation", "sequentialAnimation"])("%s setEaseFunction writes easeFunction", tag => {
|
|
155
|
+
const node = createDomNode(tag, { id: 1 } as TmNodeRef) as any;
|
|
156
|
+
|
|
157
|
+
node.setEaseFunction("linear");
|
|
158
|
+
expect(mockSetProperty).toHaveBeenCalledWith(node, "easeFunction", "linear");
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it("floatFieldInterpolator setKeyValue writes keyValue", () => {
|
|
162
|
+
const node = createDomNode("floatFieldInterpolator", { id: 1 } as TmNodeRef) as any;
|
|
163
|
+
|
|
164
|
+
node.setKeyValue([1, 0]);
|
|
165
|
+
expect(mockSetProperty).toHaveBeenCalledWith(node, "keyValue", [1, 0]);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it("colorFieldInterpolator setKeyValue writes keyValue", () => {
|
|
169
|
+
const node = createDomNode("colorFieldInterpolator", { id: 1 } as TmNodeRef) as any;
|
|
170
|
+
|
|
171
|
+
node.setKeyValue(["0x000000FF", "0xFFFFFFFF"]);
|
|
172
|
+
expect(mockSetProperty).toHaveBeenCalledWith(node, "keyValue", ["0x000000FF", "0xFFFFFFFF"]);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it("vector2dFieldInterpolator setKeyValue writes keyValue", () => {
|
|
176
|
+
const node = createDomNode("vector2dFieldInterpolator", { id: 1 } as TmNodeRef) as any;
|
|
177
|
+
|
|
178
|
+
node.setKeyValue([
|
|
179
|
+
[0, 0],
|
|
180
|
+
[500, 200]
|
|
181
|
+
]);
|
|
182
|
+
expect(mockSetProperty).toHaveBeenCalledWith(node, "keyValue", [
|
|
183
|
+
[0, 0],
|
|
184
|
+
[500, 200]
|
|
185
|
+
]);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it.each(["floatFieldInterpolator", "colorFieldInterpolator", "vector2dFieldInterpolator"])(
|
|
189
|
+
"%s setFieldToInterp writes fieldToInterp",
|
|
190
|
+
tag => {
|
|
191
|
+
const node = createDomNode(tag, { id: 1 } as TmNodeRef) as any;
|
|
192
|
+
|
|
193
|
+
node.setFieldToInterp("42.translation");
|
|
194
|
+
expect(mockSetProperty).toHaveBeenCalledWith(node, "fieldToInterp", "42.translation");
|
|
195
|
+
}
|
|
196
|
+
);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
describe("animation", () => {
|
|
200
|
+
it("should have start, stop, pause, resume, finish methods", () => {
|
|
201
|
+
const node = createDomNode("animation", { id: 1 } as TmNodeRef) as any;
|
|
202
|
+
|
|
203
|
+
for (const method of ["start", "stop", "pause", "resume", "finish"]) {
|
|
204
|
+
expect(typeof node[method]).toBe("function");
|
|
205
|
+
node[method]();
|
|
206
|
+
expect(mockSetProperty).toHaveBeenLastCalledWith(node, "control", method);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
expect(mockSetProperty).toHaveBeenCalledTimes(5);
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
describe("sequentialAnimation", () => {
|
|
214
|
+
it("should have start, stop, pause, resume, finish methods", () => {
|
|
215
|
+
const node = createDomNode("sequentialAnimation", { id: 1 } as TmNodeRef) as any;
|
|
216
|
+
|
|
217
|
+
for (const method of ["start", "stop", "pause", "resume", "finish"]) {
|
|
218
|
+
expect(typeof node[method]).toBe("function");
|
|
219
|
+
node[method]();
|
|
220
|
+
expect(mockSetProperty).toHaveBeenLastCalledWith(node, "control", method);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
expect(mockSetProperty).toHaveBeenCalledTimes(5);
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// `registerImperativeMethods` only runs as a module side effect, and this package
|
|
228
|
+
// declares a `sideEffects` allowlist. A bundler that tree-shakes a registrar module
|
|
229
|
+
// out -- which it may do whenever a consumer reaches the tag through the intrinsic
|
|
230
|
+
// JSX rather than through the exported component -- leaves the tag proto with no
|
|
231
|
+
// methods, and every imperative call throws "is not a function" at runtime. Nothing
|
|
232
|
+
// else in this suite catches that, because Jest imports the modules directly.
|
|
233
|
+
describe("registrar modules are exempt from tree-shaking", () => {
|
|
234
|
+
const packageRoot = path.resolve(__dirname, "../../..");
|
|
235
|
+
|
|
236
|
+
function globToRegExp(pattern: string): RegExp {
|
|
237
|
+
const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&");
|
|
238
|
+
const expanded = escaped
|
|
239
|
+
.replace(/\*\*\//g, "(?:.*/)?")
|
|
240
|
+
.replace(/\*\*/g, ".*")
|
|
241
|
+
.replace(/(?<!\.)\*/g, "[^/]*");
|
|
242
|
+
return new RegExp(`^${expanded}$`);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
it("lists every module that calls registerImperativeMethods in sideEffects", () => {
|
|
246
|
+
const { sideEffects } = JSON.parse(fs.readFileSync(path.join(packageRoot, "package.json"), "utf8")) as {
|
|
247
|
+
sideEffects: string[];
|
|
248
|
+
};
|
|
249
|
+
const patterns = sideEffects.map(globToRegExp);
|
|
250
|
+
|
|
251
|
+
const sgNodesDir = path.resolve(__dirname, "..");
|
|
252
|
+
const registrars = fs
|
|
253
|
+
.readdirSync(sgNodesDir)
|
|
254
|
+
.filter(name => name.endsWith(".tsx"))
|
|
255
|
+
.filter(name =>
|
|
256
|
+
fs.readFileSync(path.join(sgNodesDir, name), "utf8").includes("registerImperativeMethods(")
|
|
257
|
+
)
|
|
258
|
+
.map(name => `src/sg-nodes/${name}`);
|
|
259
|
+
|
|
260
|
+
// Guards the scan itself: a refactor that moves the registrars elsewhere
|
|
261
|
+
// should fail loudly here rather than silently assert over an empty list.
|
|
262
|
+
expect(registrars.length).toBeGreaterThan(0);
|
|
263
|
+
|
|
264
|
+
const unlisted = registrars.filter(file => !patterns.some(pattern => pattern.test(file)));
|
|
265
|
+
expect(unlisted).toEqual([]);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it("lists effect.tsx in sideEffects so the gradient dev observer is not tree-shaken", () => {
|
|
269
|
+
const { sideEffects } = JSON.parse(fs.readFileSync(path.join(packageRoot, "package.json"), "utf8")) as {
|
|
270
|
+
sideEffects: string[];
|
|
271
|
+
};
|
|
272
|
+
const patterns = sideEffects.map(globToRegExp);
|
|
273
|
+
expect(patterns.some(pattern => pattern.test("src/sg-nodes/effect.tsx"))).toBe(true);
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
});
|