@quenty/steputils 3.6.3 → 3.7.1
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/CHANGELOG.md +24 -92
- package/deploy.nevermore.json +10 -0
- package/package.json +7 -2
- package/src/Shared/StepUtils.lua +55 -3
- package/src/Shared/StepUtils.spec.lua +635 -0
- package/src/Shared/onRenderStepFrame.spec.lua +98 -0
- package/src/Shared/onSteppedFrame.spec.lua +85 -0
- package/src/jest.config.lua +3 -0
- package/src/node_modules.project.json +7 -0
- package/test/default.project.json +25 -0
- package/test/scripts/Server/ServerMain.server.lua +14 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,187 +3,119 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
-
## [3.
|
|
7
|
-
|
|
6
|
+
## [3.7.1](https://github.com/Quenty/NevermoreEngine/compare/@quenty/steputils@3.7.0...@quenty/steputils@3.7.1) (2026-07-25)
|
|
8
7
|
|
|
9
8
|
### Bug Fixes
|
|
10
9
|
|
|
11
|
-
|
|
10
|
+
- Fix things ([de8a3b5](https://github.com/Quenty/NevermoreEngine/commit/de8a3b5fbe2aa839f6fc928d4052405d3a13cd71))
|
|
12
11
|
|
|
12
|
+
# [3.7.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/steputils@3.6.3...@quenty/steputils@3.7.0) (2026-07-23)
|
|
13
13
|
|
|
14
|
+
### Bug Fixes
|
|
14
15
|
|
|
16
|
+
- Cleanup code a bit ([fa8c596](https://github.com/Quenty/NevermoreEngine/commit/fa8c596544e45610e180f5ae3797154f35930ccd))
|
|
15
17
|
|
|
18
|
+
### Features
|
|
16
19
|
|
|
17
|
-
|
|
20
|
+
- Add baseline player-mock and support across Nevermore for mocked players. ([567d121](https://github.com/Quenty/NevermoreEngine/commit/567d121ffc014b42391554088189a1a6296dda83))
|
|
18
21
|
|
|
19
|
-
|
|
22
|
+
## [3.6.3](https://github.com/Quenty/NevermoreEngine/compare/@quenty/steputils@3.6.2...@quenty/steputils@3.6.3) (2026-01-12)
|
|
20
23
|
|
|
24
|
+
### Bug Fixes
|
|
21
25
|
|
|
26
|
+
- Fix dependency installation process for pnpm ([9651b09](https://github.com/Quenty/NevermoreEngine/commit/9651b09af34f252ef3f9c7c539793cc878dd8cba))
|
|
22
27
|
|
|
28
|
+
## [3.6.2](https://github.com/Quenty/NevermoreEngine/compare/@quenty/steputils@3.6.1...@quenty/steputils@3.6.2) (2026-01-10)
|
|
23
29
|
|
|
30
|
+
**Note:** Version bump only for package @quenty/steputils
|
|
24
31
|
|
|
25
32
|
## [3.6.1](https://github.com/Quenty/NevermoreEngine/compare/@quenty/steputils@3.6.0...@quenty/steputils@3.6.1) (2026-01-03)
|
|
26
33
|
|
|
27
34
|
**Note:** Version bump only for package @quenty/steputils
|
|
28
35
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
36
|
# [3.6.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/steputils@3.5.5...@quenty/steputils@3.6.0) (2025-05-10)
|
|
34
37
|
|
|
35
38
|
**Note:** Version bump only for package @quenty/steputils
|
|
36
39
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
40
|
## [3.5.5](https://github.com/Quenty/NevermoreEngine/compare/@quenty/steputils@3.5.3...@quenty/steputils@3.5.5) (2025-04-07)
|
|
42
41
|
|
|
43
|
-
|
|
44
42
|
### Bug Fixes
|
|
45
43
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
44
|
+
- Add types to packages ([2374fb2](https://github.com/Quenty/NevermoreEngine/commit/2374fb2b043cfbe0e9b507b3316eec46a4e353a0))
|
|
45
|
+
- Bump package versions for republishing ([ba47c62](https://github.com/Quenty/NevermoreEngine/commit/ba47c62e32170bf74377b0c658c60b84306dc294))
|
|
52
46
|
|
|
53
47
|
## [3.5.4](https://github.com/Quenty/NevermoreEngine/compare/@quenty/steputils@3.5.3...@quenty/steputils@3.5.4) (2025-04-07)
|
|
54
48
|
|
|
55
|
-
|
|
56
49
|
### Bug Fixes
|
|
57
50
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
51
|
+
- Add types to packages ([2374fb2](https://github.com/Quenty/NevermoreEngine/commit/2374fb2b043cfbe0e9b507b3316eec46a4e353a0))
|
|
63
52
|
|
|
64
53
|
## [3.5.3](https://github.com/Quenty/NevermoreEngine/compare/@quenty/steputils@3.5.2...@quenty/steputils@3.5.3) (2025-03-21)
|
|
65
54
|
|
|
66
55
|
**Note:** Version bump only for package @quenty/steputils
|
|
67
56
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
57
|
## [3.5.2](https://github.com/Quenty/NevermoreEngine/compare/@quenty/steputils@3.5.1...@quenty/steputils@3.5.2) (2024-10-09)
|
|
73
58
|
|
|
74
|
-
|
|
75
59
|
### Bug Fixes
|
|
76
60
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
61
|
+
- Deprecate a large set of APIs ([#506](https://github.com/Quenty/NevermoreEngine/issues/506)) ([d6bdd65](https://github.com/Quenty/NevermoreEngine/commit/d6bdd6567668f238c45d3bd0c85bedb763d30e5c))
|
|
82
62
|
|
|
83
63
|
## [3.5.1](https://github.com/Quenty/NevermoreEngine/compare/@quenty/steputils@3.5.0...@quenty/steputils@3.5.1) (2024-10-04)
|
|
84
64
|
|
|
85
65
|
**Note:** Version bump only for package @quenty/steputils
|
|
86
66
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
67
|
# [3.5.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/steputils@3.4.0...@quenty/steputils@3.5.0) (2024-09-25)
|
|
92
68
|
|
|
93
69
|
**Note:** Version bump only for package @quenty/steputils
|
|
94
70
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
71
|
# [3.4.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/steputils@3.3.0...@quenty/steputils@3.4.0) (2024-05-09)
|
|
100
72
|
|
|
101
|
-
|
|
102
73
|
### Bug Fixes
|
|
103
74
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
75
|
+
- Fix .package-lock.json replicating in packages ([75d0efe](https://github.com/Quenty/NevermoreEngine/commit/75d0efeef239f221d93352af71a5b3e930ec23c5))
|
|
109
76
|
|
|
110
77
|
# [3.3.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/steputils@3.2.0...@quenty/steputils@3.3.0) (2023-09-04)
|
|
111
78
|
|
|
112
|
-
|
|
113
79
|
### Features
|
|
114
80
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
81
|
+
- Add StepUtils.deferWait() ([caac896](https://github.com/Quenty/NevermoreEngine/commit/caac896fc8439d0de87245cf4bdcb534ba2a9efe))
|
|
120
82
|
|
|
121
83
|
# [3.2.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/steputils@3.1.0...@quenty/steputils@3.2.0) (2022-10-11)
|
|
122
84
|
|
|
123
|
-
|
|
124
85
|
### Features
|
|
125
86
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
87
|
+
- Add StepUtils.bindToStepped ([f49f70d](https://github.com/Quenty/NevermoreEngine/commit/f49f70d870fa9fd8276a9cb0056a40ebeb85c049))
|
|
131
88
|
|
|
132
89
|
# [3.1.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/steputils@3.0.1...@quenty/steputils@3.1.0) (2022-03-27)
|
|
133
90
|
|
|
134
91
|
**Note:** Version bump only for package @quenty/steputils
|
|
135
92
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
93
|
## [3.0.1](https://github.com/Quenty/NevermoreEngine/compare/@quenty/steputils@3.0.0...@quenty/steputils@3.0.1) (2021-12-30)
|
|
141
94
|
|
|
142
95
|
**Note:** Version bump only for package @quenty/steputils
|
|
143
96
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
97
|
# [3.0.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/steputils@2.0.0...@quenty/steputils@3.0.0) (2021-09-11)
|
|
149
98
|
|
|
150
|
-
|
|
151
99
|
### Features
|
|
152
100
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
101
|
+
- Add new methods to StepUtils ([738712a](https://github.com/Quenty/NevermoreEngine/commit/738712a3fea2ccf5ba1d479749d6db73d28a0902))
|
|
158
102
|
|
|
159
103
|
# [2.0.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/steputils@1.2.0...@quenty/steputils@2.0.0) (2021-09-05)
|
|
160
104
|
|
|
161
|
-
|
|
162
105
|
### Bug Fixes
|
|
163
106
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
107
|
+
- Remove peer dependencies. This is because lerna doesn't really support peer dependencies being linked and getting a new version on build, which is unfortunate. ([5f5aeee](https://github.com/Quenty/NevermoreEngine/commit/5f5aeeea8de9975435309e53679f0ef7064f9dd0))
|
|
169
108
|
|
|
170
109
|
# 1.2.0 (2021-07-31)
|
|
171
110
|
|
|
172
|
-
|
|
173
111
|
### Bug Fixes
|
|
174
112
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
113
|
+
- Add CI and CD ([47513e9](https://github.com/Quenty/NevermoreEngine/commit/47513e9b568162707534af132396dd8756947dd3))
|
|
114
|
+
- Adjust CI badge to show automatic build and release state ([5a55d3f](https://github.com/Quenty/NevermoreEngine/commit/5a55d3f19bf8d66a760d67da9b56ed47fab74656))
|
|
115
|
+
- Fix selene linting ([45fc074](https://github.com/Quenty/NevermoreEngine/commit/45fc07489ee59127ac6582689f19a0e87c1e5b5a))
|
|
180
116
|
|
|
181
117
|
## 1.0.2 (2021-07-25)
|
|
182
118
|
|
|
183
|
-
|
|
184
|
-
|
|
185
119
|
## 1.0.1 (2021-07-25)
|
|
186
120
|
|
|
187
|
-
|
|
188
|
-
|
|
189
121
|
# 1.0.0 (2021-07-24)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quenty/steputils",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.7.1",
|
|
4
4
|
"description": "Binds animations into step, where the animation only runs as needed",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Roblox",
|
|
@@ -27,8 +27,13 @@
|
|
|
27
27
|
"contributors": [
|
|
28
28
|
"Quenty"
|
|
29
29
|
],
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@quenty/loader": "10.11.0",
|
|
32
|
+
"@quenty/nevermore-test-runner": "1.5.0",
|
|
33
|
+
"@quentystudios/jest-lua": "3.10.0-quenty.2"
|
|
34
|
+
},
|
|
30
35
|
"publishConfig": {
|
|
31
36
|
"access": "public"
|
|
32
37
|
},
|
|
33
|
-
"gitHead": "
|
|
38
|
+
"gitHead": "f276b791b8eb4e88b952b5babaf0f4189c1a11d0"
|
|
34
39
|
}
|
package/src/Shared/StepUtils.lua
CHANGED
|
@@ -10,7 +10,59 @@ local RunService = game:GetService("RunService")
|
|
|
10
10
|
local StepUtils = {}
|
|
11
11
|
|
|
12
12
|
--[=[
|
|
13
|
-
|
|
13
|
+
Returns the signal animation should step on: [RunService.RenderStepped] on the client,
|
|
14
|
+
[RunService.Stepped] (the physics pre-step) on a running server so that writes land before
|
|
15
|
+
the physics step -- springs that drive CFrames stay in lockstep with constraints, characters,
|
|
16
|
+
and replication. Heartbeat is only the fallback for a non-running DataModel (headless test
|
|
17
|
+
runs, edit mode), where Stepped never fires.
|
|
18
|
+
|
|
19
|
+
Use this instead of hand-rolling the realm branch at call sites.
|
|
20
|
+
|
|
21
|
+
:::caution
|
|
22
|
+
The argument shape is realm-dependent: RenderStepped fires `(deltaTime)` but Stepped fires
|
|
23
|
+
`(time, deltaTime)`. Shared code connecting directly to this signal must not assume the
|
|
24
|
+
first argument is deltaTime.
|
|
25
|
+
:::
|
|
26
|
+
|
|
27
|
+
@return RBXScriptSignal
|
|
28
|
+
]=]
|
|
29
|
+
function StepUtils.getAnimationStepSignal(): RBXScriptSignal
|
|
30
|
+
if RunService:IsClient() then
|
|
31
|
+
return RunService.RenderStepped
|
|
32
|
+
elseif RunService:IsRunning() then
|
|
33
|
+
return RunService.Stepped
|
|
34
|
+
else
|
|
35
|
+
return RunService.Heartbeat
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
--[=[
|
|
40
|
+
Returns the signal per-frame bookkeeping should run on: [RunService.Stepped] (the physics
|
|
41
|
+
pre-step) on a running DataModel, falling back to [RunService.Heartbeat] on a non-running one
|
|
42
|
+
(headless test runs, edit mode) where Stepped never fires.
|
|
43
|
+
|
|
44
|
+
Unlike [StepUtils.getAnimationStepSignal] this does not branch to RenderStepped on the client,
|
|
45
|
+
so both realms step at the same point in the frame. Prefer it for work that is not driving
|
|
46
|
+
visuals -- rescoring, polling, cache invalidation -- and that would otherwise silently stop
|
|
47
|
+
running outside a live game.
|
|
48
|
+
|
|
49
|
+
:::caution
|
|
50
|
+
Stepped fires `(time, deltaTime)` but Heartbeat fires `(deltaTime)`. Do not assume the first
|
|
51
|
+
argument is deltaTime when connecting directly to this signal.
|
|
52
|
+
:::
|
|
53
|
+
|
|
54
|
+
@return RBXScriptSignal
|
|
55
|
+
]=]
|
|
56
|
+
function StepUtils.getSteppedSignal(): RBXScriptSignal
|
|
57
|
+
if RunService:IsRunning() then
|
|
58
|
+
return RunService.Stepped
|
|
59
|
+
else
|
|
60
|
+
return RunService.Heartbeat
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
--[=[
|
|
65
|
+
Binds the given update function to [StepUtils.getAnimationStepSignal].
|
|
14
66
|
|
|
15
67
|
```lua
|
|
16
68
|
local spring = Spring.new(0)
|
|
@@ -37,7 +89,7 @@ local StepUtils = {}
|
|
|
37
89
|
@return () -> () -- Disconnect function
|
|
38
90
|
]=]
|
|
39
91
|
function StepUtils.bindToRenderStep(update: () -> boolean): (() -> (), () -> ())
|
|
40
|
-
return StepUtils.bindToSignal(
|
|
92
|
+
return StepUtils.bindToSignal(StepUtils.getAnimationStepSignal(), update)
|
|
41
93
|
end
|
|
42
94
|
|
|
43
95
|
--[=[
|
|
@@ -183,7 +235,7 @@ end
|
|
|
183
235
|
@return function -- Call this function to cancel call
|
|
184
236
|
]=]
|
|
185
237
|
function StepUtils.onceAtRenderStepped(func: () -> ()): () -> ()
|
|
186
|
-
local conn =
|
|
238
|
+
local conn = StepUtils.getAnimationStepSignal():Once(func)
|
|
187
239
|
return function()
|
|
188
240
|
conn:Disconnect()
|
|
189
241
|
end
|
|
@@ -0,0 +1,635 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--[[
|
|
3
|
+
@class StepUtils.spec.lua
|
|
4
|
+
]]
|
|
5
|
+
|
|
6
|
+
local require = require(script.Parent.loader).load(script)
|
|
7
|
+
|
|
8
|
+
local RunService = game:GetService("RunService")
|
|
9
|
+
|
|
10
|
+
local Jest = require("Jest")
|
|
11
|
+
local StepUtils = require("StepUtils")
|
|
12
|
+
|
|
13
|
+
local describe = Jest.Globals.describe
|
|
14
|
+
local expect = Jest.Globals.expect
|
|
15
|
+
local it = Jest.Globals.it
|
|
16
|
+
|
|
17
|
+
type Controller = {
|
|
18
|
+
newBindable: () -> BindableEvent,
|
|
19
|
+
bind: (RBXScriptSignal, (...any) -> boolean) -> ((...any) -> (), () -> ()),
|
|
20
|
+
bindRenderStep: ((...any) -> boolean) -> ((...any) -> (), () -> ()),
|
|
21
|
+
bindStepped: ((...any) -> boolean) -> ((...any) -> (), () -> ()),
|
|
22
|
+
track: (() -> ()) -> () -> (),
|
|
23
|
+
stepFrames: (number) -> (),
|
|
24
|
+
destroy: () -> (),
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
local function setup(): Controller
|
|
28
|
+
local instances: { Instance } = {}
|
|
29
|
+
local cleanups: { () -> () } = {}
|
|
30
|
+
|
|
31
|
+
local function track(cleanup: () -> ()): () -> ()
|
|
32
|
+
table.insert(cleanups, cleanup)
|
|
33
|
+
return cleanup
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
local controller: Controller = {
|
|
37
|
+
newBindable = function()
|
|
38
|
+
local bindable = Instance.new("BindableEvent")
|
|
39
|
+
table.insert(instances, bindable)
|
|
40
|
+
return bindable
|
|
41
|
+
end,
|
|
42
|
+
|
|
43
|
+
bind = function(signal, update)
|
|
44
|
+
local connect, disconnect = StepUtils.bindToSignal(signal, update)
|
|
45
|
+
track(disconnect)
|
|
46
|
+
return connect, disconnect
|
|
47
|
+
end,
|
|
48
|
+
|
|
49
|
+
bindRenderStep = function(update)
|
|
50
|
+
local connect, disconnect = StepUtils.bindToRenderStep(update)
|
|
51
|
+
track(disconnect)
|
|
52
|
+
return connect, disconnect
|
|
53
|
+
end,
|
|
54
|
+
|
|
55
|
+
bindStepped = function(update)
|
|
56
|
+
local connect, disconnect = StepUtils.bindToStepped(update)
|
|
57
|
+
track(disconnect)
|
|
58
|
+
return connect, disconnect
|
|
59
|
+
end,
|
|
60
|
+
|
|
61
|
+
track = track,
|
|
62
|
+
|
|
63
|
+
stepFrames = function(count: number)
|
|
64
|
+
for _ = 1, count do
|
|
65
|
+
task.wait()
|
|
66
|
+
end
|
|
67
|
+
end,
|
|
68
|
+
|
|
69
|
+
destroy = function()
|
|
70
|
+
for _, cleanup in cleanups do
|
|
71
|
+
cleanup()
|
|
72
|
+
end
|
|
73
|
+
for _, inst in instances do
|
|
74
|
+
inst:Destroy()
|
|
75
|
+
end
|
|
76
|
+
end,
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return controller
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
describe("StepUtils.getAnimationStepSignal", function()
|
|
83
|
+
it("returns a signal", function()
|
|
84
|
+
local controller = setup()
|
|
85
|
+
|
|
86
|
+
expect(typeof(StepUtils.getAnimationStepSignal())).toBe("RBXScriptSignal")
|
|
87
|
+
|
|
88
|
+
controller.destroy()
|
|
89
|
+
end)
|
|
90
|
+
|
|
91
|
+
it("falls back to Heartbeat off a running client", function()
|
|
92
|
+
local controller = setup()
|
|
93
|
+
|
|
94
|
+
expect(StepUtils.getAnimationStepSignal()).toBe(RunService.Heartbeat)
|
|
95
|
+
|
|
96
|
+
controller.destroy()
|
|
97
|
+
end)
|
|
98
|
+
|
|
99
|
+
it("returns a signal that actually fires here", function()
|
|
100
|
+
local controller = setup()
|
|
101
|
+
|
|
102
|
+
local fired = false
|
|
103
|
+
local conn = StepUtils.getAnimationStepSignal():Connect(function()
|
|
104
|
+
fired = true
|
|
105
|
+
end)
|
|
106
|
+
controller.track(function()
|
|
107
|
+
conn:Disconnect()
|
|
108
|
+
end)
|
|
109
|
+
|
|
110
|
+
controller.stepFrames(2)
|
|
111
|
+
|
|
112
|
+
expect(fired).toBe(true)
|
|
113
|
+
|
|
114
|
+
controller.destroy()
|
|
115
|
+
end)
|
|
116
|
+
end)
|
|
117
|
+
|
|
118
|
+
describe("StepUtils.getSteppedSignal", function()
|
|
119
|
+
it("returns a signal", function()
|
|
120
|
+
local controller = setup()
|
|
121
|
+
|
|
122
|
+
expect(typeof(StepUtils.getSteppedSignal())).toBe("RBXScriptSignal")
|
|
123
|
+
|
|
124
|
+
controller.destroy()
|
|
125
|
+
end)
|
|
126
|
+
|
|
127
|
+
it("falls back to Heartbeat on a non-running DataModel", function()
|
|
128
|
+
local controller = setup()
|
|
129
|
+
|
|
130
|
+
expect(StepUtils.getSteppedSignal()).toBe(RunService.Heartbeat)
|
|
131
|
+
expect(StepUtils.getSteppedSignal()).never.toBe(RunService.Stepped)
|
|
132
|
+
|
|
133
|
+
controller.destroy()
|
|
134
|
+
end)
|
|
135
|
+
|
|
136
|
+
it("returns a signal that actually fires here", function()
|
|
137
|
+
local controller = setup()
|
|
138
|
+
|
|
139
|
+
local fired = false
|
|
140
|
+
local conn = StepUtils.getSteppedSignal():Connect(function()
|
|
141
|
+
fired = true
|
|
142
|
+
end)
|
|
143
|
+
controller.track(function()
|
|
144
|
+
conn:Disconnect()
|
|
145
|
+
end)
|
|
146
|
+
|
|
147
|
+
controller.stepFrames(2)
|
|
148
|
+
|
|
149
|
+
expect(fired).toBe(true)
|
|
150
|
+
|
|
151
|
+
controller.destroy()
|
|
152
|
+
end)
|
|
153
|
+
end)
|
|
154
|
+
|
|
155
|
+
describe("StepUtils.bindToSignal", function()
|
|
156
|
+
it("invokes the update immediately on connect", function()
|
|
157
|
+
local controller = setup()
|
|
158
|
+
local bindable = controller.newBindable()
|
|
159
|
+
|
|
160
|
+
local calls = 0
|
|
161
|
+
local connect = controller.bind(bindable.Event, function()
|
|
162
|
+
calls += 1
|
|
163
|
+
return false
|
|
164
|
+
end)
|
|
165
|
+
|
|
166
|
+
connect()
|
|
167
|
+
|
|
168
|
+
expect(calls).toBe(1)
|
|
169
|
+
|
|
170
|
+
controller.destroy()
|
|
171
|
+
end)
|
|
172
|
+
|
|
173
|
+
it("does not connect when the first update returns false", function()
|
|
174
|
+
local controller = setup()
|
|
175
|
+
local bindable = controller.newBindable()
|
|
176
|
+
|
|
177
|
+
local calls = 0
|
|
178
|
+
local connect = controller.bind(bindable.Event, function()
|
|
179
|
+
calls += 1
|
|
180
|
+
return false
|
|
181
|
+
end)
|
|
182
|
+
|
|
183
|
+
connect()
|
|
184
|
+
bindable:Fire()
|
|
185
|
+
bindable:Fire()
|
|
186
|
+
|
|
187
|
+
expect(calls).toBe(1)
|
|
188
|
+
|
|
189
|
+
controller.destroy()
|
|
190
|
+
end)
|
|
191
|
+
|
|
192
|
+
it("keeps invoking the update while it returns true", function()
|
|
193
|
+
local controller = setup()
|
|
194
|
+
local bindable = controller.newBindable()
|
|
195
|
+
|
|
196
|
+
local calls = 0
|
|
197
|
+
local connect = controller.bind(bindable.Event, function()
|
|
198
|
+
calls += 1
|
|
199
|
+
return calls < 3
|
|
200
|
+
end)
|
|
201
|
+
|
|
202
|
+
connect()
|
|
203
|
+
bindable:Fire()
|
|
204
|
+
bindable:Fire()
|
|
205
|
+
|
|
206
|
+
expect(calls).toBe(3)
|
|
207
|
+
|
|
208
|
+
controller.destroy()
|
|
209
|
+
end)
|
|
210
|
+
|
|
211
|
+
it("stops invoking the update once it returns false", function()
|
|
212
|
+
local controller = setup()
|
|
213
|
+
local bindable = controller.newBindable()
|
|
214
|
+
|
|
215
|
+
local calls = 0
|
|
216
|
+
local connect = controller.bind(bindable.Event, function()
|
|
217
|
+
calls += 1
|
|
218
|
+
return calls < 2
|
|
219
|
+
end)
|
|
220
|
+
|
|
221
|
+
connect()
|
|
222
|
+
bindable:Fire()
|
|
223
|
+
bindable:Fire()
|
|
224
|
+
bindable:Fire()
|
|
225
|
+
|
|
226
|
+
expect(calls).toBe(2)
|
|
227
|
+
|
|
228
|
+
controller.destroy()
|
|
229
|
+
end)
|
|
230
|
+
|
|
231
|
+
it("forwards the connect arguments to every later update", function()
|
|
232
|
+
local controller = setup()
|
|
233
|
+
local bindable = controller.newBindable()
|
|
234
|
+
|
|
235
|
+
local seen = {}
|
|
236
|
+
local connect = controller.bind(bindable.Event, function(arg)
|
|
237
|
+
table.insert(seen, arg)
|
|
238
|
+
return #seen < 3
|
|
239
|
+
end)
|
|
240
|
+
|
|
241
|
+
connect("self")
|
|
242
|
+
bindable:Fire()
|
|
243
|
+
bindable:Fire()
|
|
244
|
+
|
|
245
|
+
expect(seen).toEqual({ "self", "self", "self" })
|
|
246
|
+
|
|
247
|
+
controller.destroy()
|
|
248
|
+
end)
|
|
249
|
+
|
|
250
|
+
it("ignores the signal's own arguments", function()
|
|
251
|
+
local controller = setup()
|
|
252
|
+
local bindable = controller.newBindable()
|
|
253
|
+
|
|
254
|
+
local seen = {}
|
|
255
|
+
local connect = controller.bind(bindable.Event, function(arg)
|
|
256
|
+
table.insert(seen, { value = arg })
|
|
257
|
+
return #seen < 2
|
|
258
|
+
end)
|
|
259
|
+
|
|
260
|
+
connect()
|
|
261
|
+
bindable:Fire("fromSignal")
|
|
262
|
+
|
|
263
|
+
expect(seen).toEqual({ { value = nil }, { value = nil } })
|
|
264
|
+
|
|
265
|
+
controller.destroy()
|
|
266
|
+
end)
|
|
267
|
+
|
|
268
|
+
it("ignores a repeated connect while already connected", function()
|
|
269
|
+
local controller = setup()
|
|
270
|
+
local bindable = controller.newBindable()
|
|
271
|
+
|
|
272
|
+
local calls = 0
|
|
273
|
+
local connect = controller.bind(bindable.Event, function()
|
|
274
|
+
calls += 1
|
|
275
|
+
return true
|
|
276
|
+
end)
|
|
277
|
+
|
|
278
|
+
connect()
|
|
279
|
+
connect()
|
|
280
|
+
|
|
281
|
+
expect(calls).toBe(1)
|
|
282
|
+
|
|
283
|
+
controller.destroy()
|
|
284
|
+
end)
|
|
285
|
+
|
|
286
|
+
it("reconnects after an explicit disconnect", function()
|
|
287
|
+
local controller = setup()
|
|
288
|
+
local bindable = controller.newBindable()
|
|
289
|
+
|
|
290
|
+
local calls = 0
|
|
291
|
+
local connect, disconnect = controller.bind(bindable.Event, function()
|
|
292
|
+
calls += 1
|
|
293
|
+
return true
|
|
294
|
+
end)
|
|
295
|
+
|
|
296
|
+
connect()
|
|
297
|
+
disconnect()
|
|
298
|
+
bindable:Fire()
|
|
299
|
+
expect(calls).toBe(1)
|
|
300
|
+
|
|
301
|
+
connect()
|
|
302
|
+
bindable:Fire()
|
|
303
|
+
|
|
304
|
+
expect(calls).toBe(3)
|
|
305
|
+
|
|
306
|
+
controller.destroy()
|
|
307
|
+
end)
|
|
308
|
+
|
|
309
|
+
it("tolerates disconnect before connect and repeated disconnects", function()
|
|
310
|
+
local controller = setup()
|
|
311
|
+
local bindable = controller.newBindable()
|
|
312
|
+
|
|
313
|
+
local _connect, disconnect = controller.bind(bindable.Event, function()
|
|
314
|
+
return true
|
|
315
|
+
end)
|
|
316
|
+
|
|
317
|
+
expect(function()
|
|
318
|
+
disconnect()
|
|
319
|
+
disconnect()
|
|
320
|
+
end).never.toThrow()
|
|
321
|
+
|
|
322
|
+
controller.destroy()
|
|
323
|
+
end)
|
|
324
|
+
|
|
325
|
+
it("throws on something that is not a signal", function()
|
|
326
|
+
local controller = setup()
|
|
327
|
+
|
|
328
|
+
expect(function()
|
|
329
|
+
(StepUtils :: any).bindToSignal({}, function()
|
|
330
|
+
return false
|
|
331
|
+
end)
|
|
332
|
+
end).toThrow()
|
|
333
|
+
|
|
334
|
+
controller.destroy()
|
|
335
|
+
end)
|
|
336
|
+
|
|
337
|
+
it("throws on a non-function update", function()
|
|
338
|
+
local controller = setup()
|
|
339
|
+
local bindable = controller.newBindable()
|
|
340
|
+
|
|
341
|
+
expect(function()
|
|
342
|
+
(StepUtils :: any).bindToSignal(bindable.Event, "update")
|
|
343
|
+
end).toThrow()
|
|
344
|
+
|
|
345
|
+
controller.destroy()
|
|
346
|
+
end)
|
|
347
|
+
end)
|
|
348
|
+
|
|
349
|
+
describe("StepUtils.bindToRenderStep", function()
|
|
350
|
+
it("drives the update off the animation step signal", function()
|
|
351
|
+
local controller = setup()
|
|
352
|
+
|
|
353
|
+
local calls = 0
|
|
354
|
+
local connect = controller.bindRenderStep(function()
|
|
355
|
+
calls += 1
|
|
356
|
+
return calls < 3
|
|
357
|
+
end)
|
|
358
|
+
|
|
359
|
+
connect()
|
|
360
|
+
controller.stepFrames(5)
|
|
361
|
+
|
|
362
|
+
expect(calls).toBe(3)
|
|
363
|
+
|
|
364
|
+
controller.destroy()
|
|
365
|
+
end)
|
|
366
|
+
|
|
367
|
+
it("does not bind when the first update returns false", function()
|
|
368
|
+
local controller = setup()
|
|
369
|
+
|
|
370
|
+
local calls = 0
|
|
371
|
+
local connect = controller.bindRenderStep(function()
|
|
372
|
+
calls += 1
|
|
373
|
+
return false
|
|
374
|
+
end)
|
|
375
|
+
|
|
376
|
+
connect()
|
|
377
|
+
controller.stepFrames(3)
|
|
378
|
+
|
|
379
|
+
expect(calls).toBe(1)
|
|
380
|
+
|
|
381
|
+
controller.destroy()
|
|
382
|
+
end)
|
|
383
|
+
|
|
384
|
+
it("stops driving the update after disconnect", function()
|
|
385
|
+
local controller = setup()
|
|
386
|
+
|
|
387
|
+
local calls = 0
|
|
388
|
+
local connect, disconnect = controller.bindRenderStep(function()
|
|
389
|
+
calls += 1
|
|
390
|
+
return true
|
|
391
|
+
end)
|
|
392
|
+
|
|
393
|
+
connect()
|
|
394
|
+
controller.stepFrames(2)
|
|
395
|
+
disconnect()
|
|
396
|
+
|
|
397
|
+
local afterDisconnect = calls
|
|
398
|
+
controller.stepFrames(3)
|
|
399
|
+
|
|
400
|
+
expect(calls).toBe(afterDisconnect)
|
|
401
|
+
|
|
402
|
+
controller.destroy()
|
|
403
|
+
end)
|
|
404
|
+
end)
|
|
405
|
+
|
|
406
|
+
describe("StepUtils.bindToStepped", function()
|
|
407
|
+
it("invokes the update immediately on connect", function()
|
|
408
|
+
local controller = setup()
|
|
409
|
+
|
|
410
|
+
local calls = 0
|
|
411
|
+
local connect = controller.bindStepped(function()
|
|
412
|
+
calls += 1
|
|
413
|
+
return false
|
|
414
|
+
end)
|
|
415
|
+
|
|
416
|
+
connect()
|
|
417
|
+
|
|
418
|
+
expect(calls).toBe(1)
|
|
419
|
+
|
|
420
|
+
controller.destroy()
|
|
421
|
+
end)
|
|
422
|
+
|
|
423
|
+
it("tolerates disconnect before connect", function()
|
|
424
|
+
local controller = setup()
|
|
425
|
+
|
|
426
|
+
local _connect, disconnect = controller.bindStepped(function()
|
|
427
|
+
return true
|
|
428
|
+
end)
|
|
429
|
+
|
|
430
|
+
expect(function()
|
|
431
|
+
disconnect()
|
|
432
|
+
end).never.toThrow()
|
|
433
|
+
|
|
434
|
+
controller.destroy()
|
|
435
|
+
end)
|
|
436
|
+
end)
|
|
437
|
+
|
|
438
|
+
describe("StepUtils.deferWait", function()
|
|
439
|
+
it("resumes only after already-queued deferrals run", function()
|
|
440
|
+
local controller = setup()
|
|
441
|
+
|
|
442
|
+
local order = {}
|
|
443
|
+
task.defer(function()
|
|
444
|
+
table.insert(order, "deferred")
|
|
445
|
+
end)
|
|
446
|
+
|
|
447
|
+
StepUtils.deferWait()
|
|
448
|
+
table.insert(order, "resumed")
|
|
449
|
+
|
|
450
|
+
expect(order).toEqual({ "deferred", "resumed" })
|
|
451
|
+
|
|
452
|
+
controller.destroy()
|
|
453
|
+
end)
|
|
454
|
+
end)
|
|
455
|
+
|
|
456
|
+
describe("StepUtils.onceAtEvent", function()
|
|
457
|
+
it("invokes the function on the next event", function()
|
|
458
|
+
local controller = setup()
|
|
459
|
+
local bindable = controller.newBindable()
|
|
460
|
+
|
|
461
|
+
local calls = 0
|
|
462
|
+
controller.track(StepUtils.onceAtEvent(bindable.Event, function()
|
|
463
|
+
calls += 1
|
|
464
|
+
end))
|
|
465
|
+
|
|
466
|
+
bindable:Fire()
|
|
467
|
+
|
|
468
|
+
expect(calls).toBe(1)
|
|
469
|
+
|
|
470
|
+
controller.destroy()
|
|
471
|
+
end)
|
|
472
|
+
|
|
473
|
+
it("passes the event arguments through", function()
|
|
474
|
+
local controller = setup()
|
|
475
|
+
local bindable = controller.newBindable()
|
|
476
|
+
|
|
477
|
+
local seen: any = nil
|
|
478
|
+
controller.track(StepUtils.onceAtEvent(bindable.Event, function(value: any)
|
|
479
|
+
seen = value
|
|
480
|
+
end))
|
|
481
|
+
|
|
482
|
+
bindable:Fire("payload")
|
|
483
|
+
|
|
484
|
+
expect(seen).toBe("payload")
|
|
485
|
+
|
|
486
|
+
controller.destroy()
|
|
487
|
+
end)
|
|
488
|
+
|
|
489
|
+
it("only invokes the function once", function()
|
|
490
|
+
local controller = setup()
|
|
491
|
+
local bindable = controller.newBindable()
|
|
492
|
+
|
|
493
|
+
local calls = 0
|
|
494
|
+
controller.track(StepUtils.onceAtEvent(bindable.Event, function()
|
|
495
|
+
calls += 1
|
|
496
|
+
end))
|
|
497
|
+
|
|
498
|
+
bindable:Fire()
|
|
499
|
+
bindable:Fire()
|
|
500
|
+
|
|
501
|
+
expect(calls).toBe(1)
|
|
502
|
+
|
|
503
|
+
controller.destroy()
|
|
504
|
+
end)
|
|
505
|
+
|
|
506
|
+
it("does not invoke the function once cancelled", function()
|
|
507
|
+
local controller = setup()
|
|
508
|
+
local bindable = controller.newBindable()
|
|
509
|
+
|
|
510
|
+
local calls = 0
|
|
511
|
+
local cancel = StepUtils.onceAtEvent(bindable.Event, function()
|
|
512
|
+
calls += 1
|
|
513
|
+
end)
|
|
514
|
+
|
|
515
|
+
cancel()
|
|
516
|
+
bindable:Fire()
|
|
517
|
+
|
|
518
|
+
expect(calls).toBe(0)
|
|
519
|
+
|
|
520
|
+
controller.destroy()
|
|
521
|
+
end)
|
|
522
|
+
|
|
523
|
+
it("tolerates cancelling after the function ran", function()
|
|
524
|
+
local controller = setup()
|
|
525
|
+
local bindable = controller.newBindable()
|
|
526
|
+
|
|
527
|
+
local cancel = StepUtils.onceAtEvent(bindable.Event, function() end)
|
|
528
|
+
bindable:Fire()
|
|
529
|
+
|
|
530
|
+
expect(function()
|
|
531
|
+
cancel()
|
|
532
|
+
cancel()
|
|
533
|
+
end).never.toThrow()
|
|
534
|
+
|
|
535
|
+
controller.destroy()
|
|
536
|
+
end)
|
|
537
|
+
|
|
538
|
+
it("throws on a non-function", function()
|
|
539
|
+
local controller = setup()
|
|
540
|
+
local bindable = controller.newBindable()
|
|
541
|
+
|
|
542
|
+
expect(function()
|
|
543
|
+
(StepUtils :: any).onceAtEvent(bindable.Event, "func")
|
|
544
|
+
end).toThrow()
|
|
545
|
+
|
|
546
|
+
controller.destroy()
|
|
547
|
+
end)
|
|
548
|
+
end)
|
|
549
|
+
|
|
550
|
+
describe("StepUtils.onceAtRenderStepped", function()
|
|
551
|
+
it("invokes the function once on the animation step signal", function()
|
|
552
|
+
local controller = setup()
|
|
553
|
+
|
|
554
|
+
local calls = 0
|
|
555
|
+
controller.track(StepUtils.onceAtRenderStepped(function()
|
|
556
|
+
calls += 1
|
|
557
|
+
end))
|
|
558
|
+
|
|
559
|
+
controller.stepFrames(3)
|
|
560
|
+
|
|
561
|
+
expect(calls).toBe(1)
|
|
562
|
+
|
|
563
|
+
controller.destroy()
|
|
564
|
+
end)
|
|
565
|
+
|
|
566
|
+
it("does not invoke the function once cancelled", function()
|
|
567
|
+
local controller = setup()
|
|
568
|
+
|
|
569
|
+
local calls = 0
|
|
570
|
+
local cancel = StepUtils.onceAtRenderStepped(function()
|
|
571
|
+
calls += 1
|
|
572
|
+
end)
|
|
573
|
+
|
|
574
|
+
cancel()
|
|
575
|
+
controller.stepFrames(3)
|
|
576
|
+
|
|
577
|
+
expect(calls).toBe(0)
|
|
578
|
+
|
|
579
|
+
controller.destroy()
|
|
580
|
+
end)
|
|
581
|
+
end)
|
|
582
|
+
|
|
583
|
+
describe("StepUtils.onceAtStepped", function()
|
|
584
|
+
it("returns a cancel function that is safe to call twice", function()
|
|
585
|
+
local controller = setup()
|
|
586
|
+
|
|
587
|
+
local calls = 0
|
|
588
|
+
local cancel = StepUtils.onceAtStepped(function()
|
|
589
|
+
calls += 1
|
|
590
|
+
end)
|
|
591
|
+
|
|
592
|
+
expect(function()
|
|
593
|
+
cancel()
|
|
594
|
+
cancel()
|
|
595
|
+
end).never.toThrow()
|
|
596
|
+
expect(calls).toBe(0)
|
|
597
|
+
|
|
598
|
+
controller.destroy()
|
|
599
|
+
end)
|
|
600
|
+
end)
|
|
601
|
+
|
|
602
|
+
describe("StepUtils.onceAtRenderPriority", function()
|
|
603
|
+
it("returns a cancel function that is safe to call twice", function()
|
|
604
|
+
local controller = setup()
|
|
605
|
+
|
|
606
|
+
local cancel = StepUtils.onceAtRenderPriority(Enum.RenderPriority.Last.Value, function() end)
|
|
607
|
+
|
|
608
|
+
expect(function()
|
|
609
|
+
cancel()
|
|
610
|
+
cancel()
|
|
611
|
+
end).never.toThrow()
|
|
612
|
+
|
|
613
|
+
controller.destroy()
|
|
614
|
+
end)
|
|
615
|
+
|
|
616
|
+
it("throws on a non-number priority", function()
|
|
617
|
+
local controller = setup()
|
|
618
|
+
|
|
619
|
+
expect(function()
|
|
620
|
+
(StepUtils :: any).onceAtRenderPriority("high", function() end)
|
|
621
|
+
end).toThrow()
|
|
622
|
+
|
|
623
|
+
controller.destroy()
|
|
624
|
+
end)
|
|
625
|
+
|
|
626
|
+
it("throws on a non-function", function()
|
|
627
|
+
local controller = setup()
|
|
628
|
+
|
|
629
|
+
expect(function()
|
|
630
|
+
(StepUtils :: any).onceAtRenderPriority(100, "func")
|
|
631
|
+
end).toThrow()
|
|
632
|
+
|
|
633
|
+
controller.destroy()
|
|
634
|
+
end)
|
|
635
|
+
end)
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--[[
|
|
3
|
+
@class onRenderStepFrame.spec.lua
|
|
4
|
+
]]
|
|
5
|
+
|
|
6
|
+
local require = require(script.Parent.loader).load(script)
|
|
7
|
+
|
|
8
|
+
local Jest = require("Jest")
|
|
9
|
+
local onRenderStepFrame = require("onRenderStepFrame")
|
|
10
|
+
|
|
11
|
+
local describe = Jest.Globals.describe
|
|
12
|
+
local expect = Jest.Globals.expect
|
|
13
|
+
local it = Jest.Globals.it
|
|
14
|
+
|
|
15
|
+
type Controller = {
|
|
16
|
+
bind: (number, () -> ()) -> () -> (),
|
|
17
|
+
destroy: () -> (),
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
local function setup(): Controller
|
|
21
|
+
local unbinds: { () -> () } = {}
|
|
22
|
+
|
|
23
|
+
local controller: Controller = {
|
|
24
|
+
bind = function(priority: number, callback: () -> ())
|
|
25
|
+
local unbind = onRenderStepFrame(priority, callback)
|
|
26
|
+
table.insert(unbinds, unbind)
|
|
27
|
+
return unbind
|
|
28
|
+
end,
|
|
29
|
+
|
|
30
|
+
destroy = function()
|
|
31
|
+
for _, unbind in unbinds do
|
|
32
|
+
unbind()
|
|
33
|
+
end
|
|
34
|
+
end,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return controller
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
describe("onRenderStepFrame", function()
|
|
41
|
+
it("returns an unbind function", function()
|
|
42
|
+
local controller = setup()
|
|
43
|
+
|
|
44
|
+
local unbind = controller.bind(Enum.RenderPriority.Last.Value, function() end)
|
|
45
|
+
|
|
46
|
+
expect(type(unbind)).toBe("function")
|
|
47
|
+
|
|
48
|
+
controller.destroy()
|
|
49
|
+
end)
|
|
50
|
+
|
|
51
|
+
it("tolerates a repeated unbind", function()
|
|
52
|
+
local controller = setup()
|
|
53
|
+
|
|
54
|
+
local unbind = controller.bind(Enum.RenderPriority.Last.Value, function() end)
|
|
55
|
+
|
|
56
|
+
expect(function()
|
|
57
|
+
unbind()
|
|
58
|
+
unbind()
|
|
59
|
+
end).never.toThrow()
|
|
60
|
+
|
|
61
|
+
controller.destroy()
|
|
62
|
+
end)
|
|
63
|
+
|
|
64
|
+
it("binds each call under its own key", function()
|
|
65
|
+
local controller = setup()
|
|
66
|
+
|
|
67
|
+
local first = controller.bind(Enum.RenderPriority.Last.Value, function() end)
|
|
68
|
+
local second = controller.bind(Enum.RenderPriority.Last.Value, function() end)
|
|
69
|
+
|
|
70
|
+
expect(second).never.toBe(first)
|
|
71
|
+
expect(function()
|
|
72
|
+
first()
|
|
73
|
+
second()
|
|
74
|
+
end).never.toThrow()
|
|
75
|
+
|
|
76
|
+
controller.destroy()
|
|
77
|
+
end)
|
|
78
|
+
|
|
79
|
+
it("throws on a non-number priority", function()
|
|
80
|
+
local controller = setup()
|
|
81
|
+
|
|
82
|
+
expect(function()
|
|
83
|
+
(onRenderStepFrame :: any)("high", function() end)
|
|
84
|
+
end).toThrow()
|
|
85
|
+
|
|
86
|
+
controller.destroy()
|
|
87
|
+
end)
|
|
88
|
+
|
|
89
|
+
it("throws on a non-function callback", function()
|
|
90
|
+
local controller = setup()
|
|
91
|
+
|
|
92
|
+
expect(function()
|
|
93
|
+
(onRenderStepFrame :: any)(100, "callback")
|
|
94
|
+
end).toThrow()
|
|
95
|
+
|
|
96
|
+
controller.destroy()
|
|
97
|
+
end)
|
|
98
|
+
end)
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--[[
|
|
3
|
+
@class onSteppedFrame.spec.lua
|
|
4
|
+
]]
|
|
5
|
+
|
|
6
|
+
local require = require(script.Parent.loader).load(script)
|
|
7
|
+
|
|
8
|
+
local Jest = require("Jest")
|
|
9
|
+
local onSteppedFrame = require("onSteppedFrame")
|
|
10
|
+
|
|
11
|
+
local describe = Jest.Globals.describe
|
|
12
|
+
local expect = Jest.Globals.expect
|
|
13
|
+
local it = Jest.Globals.it
|
|
14
|
+
|
|
15
|
+
type Controller = {
|
|
16
|
+
bind: (() -> ()) -> RBXScriptConnection,
|
|
17
|
+
destroy: () -> (),
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
local function setup(): Controller
|
|
21
|
+
local connections: { RBXScriptConnection } = {}
|
|
22
|
+
|
|
23
|
+
local controller: Controller = {
|
|
24
|
+
bind = function(callback)
|
|
25
|
+
local conn = onSteppedFrame(callback)
|
|
26
|
+
table.insert(connections, conn)
|
|
27
|
+
return conn
|
|
28
|
+
end,
|
|
29
|
+
|
|
30
|
+
destroy = function()
|
|
31
|
+
for _, conn in connections do
|
|
32
|
+
conn:Disconnect()
|
|
33
|
+
end
|
|
34
|
+
end,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return controller
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
describe("onSteppedFrame", function()
|
|
41
|
+
it("returns a live connection", function()
|
|
42
|
+
local controller = setup()
|
|
43
|
+
|
|
44
|
+
local conn = controller.bind(function() end)
|
|
45
|
+
|
|
46
|
+
expect(typeof(conn)).toBe("RBXScriptConnection")
|
|
47
|
+
expect(conn.Connected).toBe(true)
|
|
48
|
+
|
|
49
|
+
controller.destroy()
|
|
50
|
+
end)
|
|
51
|
+
|
|
52
|
+
it("goes dead once disconnected", function()
|
|
53
|
+
local controller = setup()
|
|
54
|
+
|
|
55
|
+
local conn = controller.bind(function() end)
|
|
56
|
+
conn:Disconnect()
|
|
57
|
+
|
|
58
|
+
expect(conn.Connected).toBe(false)
|
|
59
|
+
|
|
60
|
+
controller.destroy()
|
|
61
|
+
end)
|
|
62
|
+
|
|
63
|
+
it("tolerates a repeated disconnect", function()
|
|
64
|
+
local controller = setup()
|
|
65
|
+
|
|
66
|
+
local conn = controller.bind(function() end)
|
|
67
|
+
|
|
68
|
+
expect(function()
|
|
69
|
+
conn:Disconnect()
|
|
70
|
+
conn:Disconnect()
|
|
71
|
+
end).never.toThrow()
|
|
72
|
+
|
|
73
|
+
controller.destroy()
|
|
74
|
+
end)
|
|
75
|
+
|
|
76
|
+
it("throws on a non-function", function()
|
|
77
|
+
local controller = setup()
|
|
78
|
+
|
|
79
|
+
expect(function()
|
|
80
|
+
(onSteppedFrame :: any)("func")
|
|
81
|
+
end).toThrow()
|
|
82
|
+
|
|
83
|
+
controller.destroy()
|
|
84
|
+
end)
|
|
85
|
+
end)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "StepUtilsTest",
|
|
3
|
+
"globIgnorePaths": [
|
|
4
|
+
"**/.package-lock.json",
|
|
5
|
+
"**/.pnpm",
|
|
6
|
+
"**/.pnpm-workspace-state-v1.json",
|
|
7
|
+
"**/.modules.yaml",
|
|
8
|
+
"**/.ignored",
|
|
9
|
+
"**/.ignored_*"
|
|
10
|
+
],
|
|
11
|
+
"tree": {
|
|
12
|
+
"$className": "DataModel",
|
|
13
|
+
"ServerScriptService": {
|
|
14
|
+
"$properties": {
|
|
15
|
+
"LoadStringEnabled": true
|
|
16
|
+
},
|
|
17
|
+
"steputils": {
|
|
18
|
+
"$path": ".."
|
|
19
|
+
},
|
|
20
|
+
"Script": {
|
|
21
|
+
"$path": "scripts/Server"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
--!nonstrict
|
|
2
|
+
--[[
|
|
3
|
+
@class ServerMain
|
|
4
|
+
]]
|
|
5
|
+
|
|
6
|
+
local ServerScriptService = game:GetService("ServerScriptService")
|
|
7
|
+
|
|
8
|
+
local root = ServerScriptService.steputils
|
|
9
|
+
local loader = root:FindFirstChild("LoaderUtils", true).Parent
|
|
10
|
+
local require = require(loader).bootstrapGame(root)
|
|
11
|
+
|
|
12
|
+
local NevermoreTestRunnerUtils = require("NevermoreTestRunnerUtils")
|
|
13
|
+
|
|
14
|
+
NevermoreTestRunnerUtils.runTestsIfNeededAsync(root)
|