roblox-opencode 1.0.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.
Files changed (248) hide show
  1. package/README.md +122 -0
  2. package/commands/setup-game.md +108 -0
  3. package/commands/sync-check.md +53 -0
  4. package/core/roblox-core.md +93 -0
  5. package/dist/server.js +167 -0
  6. package/package.json +35 -0
  7. package/skills/roblox-analytics/SKILL.md +277 -0
  8. package/skills/roblox-analytics/references/event-batcher.luau +75 -0
  9. package/skills/roblox-animation-vfx/SKILL.md +1325 -0
  10. package/skills/roblox-architecture/SKILL.md +863 -0
  11. package/skills/roblox-architecture/references/combat-systems.md +1381 -0
  12. package/skills/roblox-code-review/SKILL.md +687 -0
  13. package/skills/roblox-data/SKILL.md +889 -0
  14. package/skills/roblox-data/references/inventory-systems.md +1729 -0
  15. package/skills/roblox-debug/SKILL.md +99 -0
  16. package/skills/roblox-gui/SKILL.md +1103 -0
  17. package/skills/roblox-gui-fusion/SKILL.md +150 -0
  18. package/skills/roblox-gui-fusion/references/inventory.luau +427 -0
  19. package/skills/roblox-gui-fusion/references/settings-menu.luau +579 -0
  20. package/skills/roblox-gui-fusion/references/shop.luau +411 -0
  21. package/skills/roblox-luau-mastery/SKILL.md +1519 -0
  22. package/skills/roblox-monetization/SKILL.md +1084 -0
  23. package/skills/roblox-monetization/references/process-receipt.luau +131 -0
  24. package/skills/roblox-networking/SKILL.md +669 -0
  25. package/skills/roblox-networking/references/remote-validator.luau +193 -0
  26. package/skills/roblox-publish-checklist/SKILL.md +128 -0
  27. package/skills/roblox-runtime/SKILL.md +753 -0
  28. package/skills/roblox-sharp-edges/SKILL.md +295 -0
  29. package/skills/roblox-sync/SKILL.md +126 -0
  30. package/skills/roblox-testing/SKILL.md +943 -0
  31. package/skills/roblox-tooling/SKILL.md +150 -0
  32. package/vendor/LICENSES/ProfileStore-LICENSE +201 -0
  33. package/vendor/LICENSES/RbxUtil-LICENSE +7 -0
  34. package/vendor/LICENSES/promise-LICENSE +21 -0
  35. package/vendor/LICENSES/t-LICENSE +21 -0
  36. package/vendor/LICENSES/testez-LICENSE +201 -0
  37. package/vendor/README.md +84 -0
  38. package/vendor/fusion/Animation/ExternalTime.luau +84 -0
  39. package/vendor/fusion/Animation/Spring.luau +322 -0
  40. package/vendor/fusion/Animation/Stopwatch.luau +128 -0
  41. package/vendor/fusion/Animation/Tween.luau +187 -0
  42. package/vendor/fusion/Animation/getTweenDuration.luau +27 -0
  43. package/vendor/fusion/Animation/getTweenRatio.luau +47 -0
  44. package/vendor/fusion/Animation/lerpType.luau +164 -0
  45. package/vendor/fusion/Animation/packType.luau +100 -0
  46. package/vendor/fusion/Animation/springCoefficients.luau +80 -0
  47. package/vendor/fusion/Animation/unpackType.luau +103 -0
  48. package/vendor/fusion/Colour/Oklab.luau +70 -0
  49. package/vendor/fusion/Colour/sRGB.luau +55 -0
  50. package/vendor/fusion/External.luau +168 -0
  51. package/vendor/fusion/ExternalDebug.luau +70 -0
  52. package/vendor/fusion/Graph/Observer.luau +114 -0
  53. package/vendor/fusion/Graph/castToGraph.luau +29 -0
  54. package/vendor/fusion/Graph/change.luau +81 -0
  55. package/vendor/fusion/Graph/depend.luau +33 -0
  56. package/vendor/fusion/Graph/evaluate.luau +56 -0
  57. package/vendor/fusion/Instances/Attribute.luau +58 -0
  58. package/vendor/fusion/Instances/AttributeChange.luau +47 -0
  59. package/vendor/fusion/Instances/AttributeOut.luau +63 -0
  60. package/vendor/fusion/Instances/Child.luau +21 -0
  61. package/vendor/fusion/Instances/Children.luau +148 -0
  62. package/vendor/fusion/Instances/Hydrate.luau +33 -0
  63. package/vendor/fusion/Instances/New.luau +53 -0
  64. package/vendor/fusion/Instances/OnChange.luau +50 -0
  65. package/vendor/fusion/Instances/OnEvent.luau +54 -0
  66. package/vendor/fusion/Instances/Out.luau +69 -0
  67. package/vendor/fusion/Instances/applyInstanceProps.luau +149 -0
  68. package/vendor/fusion/Instances/defaultProps.luau +194 -0
  69. package/vendor/fusion/LICENSE +21 -0
  70. package/vendor/fusion/Logging/formatError.luau +49 -0
  71. package/vendor/fusion/Logging/messages.luau +52 -0
  72. package/vendor/fusion/Logging/parseError.luau +25 -0
  73. package/vendor/fusion/Memory/checkLifetime.luau +134 -0
  74. package/vendor/fusion/Memory/deriveScope.luau +24 -0
  75. package/vendor/fusion/Memory/deriveScopeImpl.luau +45 -0
  76. package/vendor/fusion/Memory/doCleanup.luau +79 -0
  77. package/vendor/fusion/Memory/innerScope.luau +34 -0
  78. package/vendor/fusion/Memory/legacyCleanup.luau +18 -0
  79. package/vendor/fusion/Memory/needsDestruction.luau +17 -0
  80. package/vendor/fusion/Memory/poisonScope.luau +34 -0
  81. package/vendor/fusion/Memory/scopePool.luau +55 -0
  82. package/vendor/fusion/Memory/scoped.luau +27 -0
  83. package/vendor/fusion/Memory/whichLivesLonger.luau +75 -0
  84. package/vendor/fusion/RobloxExternal.luau +98 -0
  85. package/vendor/fusion/State/Computed.luau +139 -0
  86. package/vendor/fusion/State/For/Disassembly.luau +211 -0
  87. package/vendor/fusion/State/For/ForTypes.luau +30 -0
  88. package/vendor/fusion/State/For/init.luau +110 -0
  89. package/vendor/fusion/State/ForKeys.luau +94 -0
  90. package/vendor/fusion/State/ForPairs.luau +97 -0
  91. package/vendor/fusion/State/ForValues.luau +94 -0
  92. package/vendor/fusion/State/Value.luau +88 -0
  93. package/vendor/fusion/State/castToState.luau +26 -0
  94. package/vendor/fusion/State/peek.luau +31 -0
  95. package/vendor/fusion/State/updateAll.luau +1 -0
  96. package/vendor/fusion/Types.luau +314 -0
  97. package/vendor/fusion/Utility/Contextual.luau +91 -0
  98. package/vendor/fusion/Utility/Safe.luau +23 -0
  99. package/vendor/fusion/Utility/isSimilar.luau +29 -0
  100. package/vendor/fusion/Utility/merge.luau +35 -0
  101. package/vendor/fusion/Utility/nameOf.luau +35 -0
  102. package/vendor/fusion/Utility/never.luau +14 -0
  103. package/vendor/fusion/Utility/nicknames.luau +11 -0
  104. package/vendor/fusion/Utility/xtypeof.luau +27 -0
  105. package/vendor/fusion/init.luau +82 -0
  106. package/vendor/profilestore/init.luau +2243 -0
  107. package/vendor/promise/init.luau +1982 -0
  108. package/vendor/rbxutil/buffer-util/Buffer.test.luau +25 -0
  109. package/vendor/rbxutil/buffer-util/BufferReader.luau +228 -0
  110. package/vendor/rbxutil/buffer-util/BufferWriter.luau +269 -0
  111. package/vendor/rbxutil/buffer-util/DataTypeBuffer.luau +223 -0
  112. package/vendor/rbxutil/buffer-util/Types.luau +60 -0
  113. package/vendor/rbxutil/buffer-util/index.d.ts +153 -0
  114. package/vendor/rbxutil/buffer-util/init.luau +41 -0
  115. package/vendor/rbxutil/buffer-util/package.json +16 -0
  116. package/vendor/rbxutil/buffer-util/wally.toml +9 -0
  117. package/vendor/rbxutil/comm/Client/ClientComm.luau +232 -0
  118. package/vendor/rbxutil/comm/Client/ClientRemoteProperty.luau +156 -0
  119. package/vendor/rbxutil/comm/Client/ClientRemoteSignal.luau +109 -0
  120. package/vendor/rbxutil/comm/Client/init.luau +135 -0
  121. package/vendor/rbxutil/comm/Server/RemoteProperty.luau +295 -0
  122. package/vendor/rbxutil/comm/Server/RemoteSignal.luau +211 -0
  123. package/vendor/rbxutil/comm/Server/ServerComm.luau +211 -0
  124. package/vendor/rbxutil/comm/Server/init.luau +140 -0
  125. package/vendor/rbxutil/comm/Types.luau +18 -0
  126. package/vendor/rbxutil/comm/Util.luau +27 -0
  127. package/vendor/rbxutil/comm/init.luau +35 -0
  128. package/vendor/rbxutil/comm/wally.toml +13 -0
  129. package/vendor/rbxutil/component/init.luau +759 -0
  130. package/vendor/rbxutil/component/init.test.luau +311 -0
  131. package/vendor/rbxutil/component/wally.toml +14 -0
  132. package/vendor/rbxutil/concur/init.luau +542 -0
  133. package/vendor/rbxutil/concur/init.test.luau +364 -0
  134. package/vendor/rbxutil/concur/wally.toml +8 -0
  135. package/vendor/rbxutil/enum-list/init.luau +101 -0
  136. package/vendor/rbxutil/enum-list/init.test.luau +91 -0
  137. package/vendor/rbxutil/enum-list/wally.toml +8 -0
  138. package/vendor/rbxutil/find/index.d.ts +20 -0
  139. package/vendor/rbxutil/find/init.luau +44 -0
  140. package/vendor/rbxutil/find/package.json +17 -0
  141. package/vendor/rbxutil/find/wally.toml +8 -0
  142. package/vendor/rbxutil/input/Gamepad.luau +559 -0
  143. package/vendor/rbxutil/input/Keyboard.luau +124 -0
  144. package/vendor/rbxutil/input/Mouse.luau +278 -0
  145. package/vendor/rbxutil/input/PreferredInput.luau +91 -0
  146. package/vendor/rbxutil/input/Touch.luau +120 -0
  147. package/vendor/rbxutil/input/init.luau +33 -0
  148. package/vendor/rbxutil/input/wally.toml +12 -0
  149. package/vendor/rbxutil/loader/index.d.ts +15 -0
  150. package/vendor/rbxutil/loader/init.luau +137 -0
  151. package/vendor/rbxutil/loader/wally.toml +8 -0
  152. package/vendor/rbxutil/log/index.d.ts +38 -0
  153. package/vendor/rbxutil/log/init.luau +746 -0
  154. package/vendor/rbxutil/log/wally.toml +8 -0
  155. package/vendor/rbxutil/net/init.luau +190 -0
  156. package/vendor/rbxutil/net/wally.toml +8 -0
  157. package/vendor/rbxutil/option/index.d.ts +44 -0
  158. package/vendor/rbxutil/option/init.luau +489 -0
  159. package/vendor/rbxutil/option/init.test.luau +342 -0
  160. package/vendor/rbxutil/option/wally.toml +8 -0
  161. package/vendor/rbxutil/pid/index.d.ts +53 -0
  162. package/vendor/rbxutil/pid/init.luau +195 -0
  163. package/vendor/rbxutil/pid/package.json +16 -0
  164. package/vendor/rbxutil/pid/wally.toml +9 -0
  165. package/vendor/rbxutil/quaternion/index.d.ts +117 -0
  166. package/vendor/rbxutil/quaternion/init.luau +570 -0
  167. package/vendor/rbxutil/quaternion/package.json +16 -0
  168. package/vendor/rbxutil/quaternion/wally.toml +9 -0
  169. package/vendor/rbxutil/query/index.d.ts +43 -0
  170. package/vendor/rbxutil/query/init.luau +117 -0
  171. package/vendor/rbxutil/query/package.json +18 -0
  172. package/vendor/rbxutil/query/wally.toml +9 -0
  173. package/vendor/rbxutil/sequent/index.d.ts +28 -0
  174. package/vendor/rbxutil/sequent/init.luau +340 -0
  175. package/vendor/rbxutil/sequent/package.json +16 -0
  176. package/vendor/rbxutil/sequent/wally.toml +9 -0
  177. package/vendor/rbxutil/ser/init.luau +175 -0
  178. package/vendor/rbxutil/ser/init.test.luau +50 -0
  179. package/vendor/rbxutil/ser/wally.toml +11 -0
  180. package/vendor/rbxutil/shake/index.d.ts +36 -0
  181. package/vendor/rbxutil/shake/init.luau +532 -0
  182. package/vendor/rbxutil/shake/init.test.luau +267 -0
  183. package/vendor/rbxutil/shake/package.json +16 -0
  184. package/vendor/rbxutil/shake/wally.toml +9 -0
  185. package/vendor/rbxutil/signal/index.d.ts +100 -0
  186. package/vendor/rbxutil/signal/init.luau +432 -0
  187. package/vendor/rbxutil/signal/init.test.luau +190 -0
  188. package/vendor/rbxutil/signal/package.json +17 -0
  189. package/vendor/rbxutil/signal/wally.toml +9 -0
  190. package/vendor/rbxutil/silo/TableWatcher.luau +65 -0
  191. package/vendor/rbxutil/silo/Util.luau +55 -0
  192. package/vendor/rbxutil/silo/init.luau +338 -0
  193. package/vendor/rbxutil/silo/init.test.luau +215 -0
  194. package/vendor/rbxutil/silo/wally.toml +8 -0
  195. package/vendor/rbxutil/spring/index.d.ts +40 -0
  196. package/vendor/rbxutil/spring/init.luau +97 -0
  197. package/vendor/rbxutil/spring/package.json +17 -0
  198. package/vendor/rbxutil/spring/wally.toml +8 -0
  199. package/vendor/rbxutil/stream/index.d.ts +88 -0
  200. package/vendor/rbxutil/stream/init.luau +597 -0
  201. package/vendor/rbxutil/stream/package.json +18 -0
  202. package/vendor/rbxutil/stream/wally.toml +9 -0
  203. package/vendor/rbxutil/streamable/Streamable.luau +202 -0
  204. package/vendor/rbxutil/streamable/StreamableUtil.luau +80 -0
  205. package/vendor/rbxutil/streamable/init.luau +8 -0
  206. package/vendor/rbxutil/streamable/wally.toml +12 -0
  207. package/vendor/rbxutil/symbol/init.luau +56 -0
  208. package/vendor/rbxutil/symbol/init.test.luau +37 -0
  209. package/vendor/rbxutil/symbol/wally.toml +8 -0
  210. package/vendor/rbxutil/table-util/init.luau +938 -0
  211. package/vendor/rbxutil/table-util/init.test.luau +439 -0
  212. package/vendor/rbxutil/table-util/wally.toml +8 -0
  213. package/vendor/rbxutil/task-queue/index.d.ts +27 -0
  214. package/vendor/rbxutil/task-queue/init.luau +97 -0
  215. package/vendor/rbxutil/task-queue/wally.toml +8 -0
  216. package/vendor/rbxutil/timer/index.d.ts +81 -0
  217. package/vendor/rbxutil/timer/init.luau +249 -0
  218. package/vendor/rbxutil/timer/init.test.luau +73 -0
  219. package/vendor/rbxutil/timer/wally.toml +11 -0
  220. package/vendor/rbxutil/tree/index.d.ts +15 -0
  221. package/vendor/rbxutil/tree/init.luau +137 -0
  222. package/vendor/rbxutil/tree/wally.toml +8 -0
  223. package/vendor/rbxutil/trove/index.d.ts +46 -0
  224. package/vendor/rbxutil/trove/init.luau +787 -0
  225. package/vendor/rbxutil/trove/init.test.luau +203 -0
  226. package/vendor/rbxutil/trove/wally.toml +8 -0
  227. package/vendor/rbxutil/typed-remote/init.luau +196 -0
  228. package/vendor/rbxutil/typed-remote/wally.toml +8 -0
  229. package/vendor/rbxutil/wait-for/index.d.ts +17 -0
  230. package/vendor/rbxutil/wait-for/init.luau +257 -0
  231. package/vendor/rbxutil/wait-for/init.test.luau +182 -0
  232. package/vendor/rbxutil/wait-for/wally.toml +11 -0
  233. package/vendor/t/t.lua +1350 -0
  234. package/vendor/testez/Context.lua +26 -0
  235. package/vendor/testez/Expectation.lua +311 -0
  236. package/vendor/testez/ExpectationContext.lua +38 -0
  237. package/vendor/testez/LifecycleHooks.lua +89 -0
  238. package/vendor/testez/Reporters/TeamCityReporter.lua +102 -0
  239. package/vendor/testez/Reporters/TextReporter.lua +106 -0
  240. package/vendor/testez/Reporters/TextReporterQuiet.lua +97 -0
  241. package/vendor/testez/TestBootstrap.lua +147 -0
  242. package/vendor/testez/TestEnum.lua +28 -0
  243. package/vendor/testez/TestPlan.lua +304 -0
  244. package/vendor/testez/TestPlanner.lua +40 -0
  245. package/vendor/testez/TestResults.lua +112 -0
  246. package/vendor/testez/TestRunner.lua +188 -0
  247. package/vendor/testez/TestSession.lua +243 -0
  248. package/vendor/testez/init.lua +40 -0
@@ -0,0 +1,97 @@
1
+ --[[
2
+ Copy of TextReporter that doesn't output successful tests.
3
+
4
+ This should be temporary, it's just a workaround to make CI environments
5
+ happy in the short-term.
6
+ ]]
7
+
8
+ local TestService = game:GetService("TestService")
9
+
10
+ local TestEnum = require(script.Parent.Parent.TestEnum)
11
+
12
+ local INDENT = (" "):rep(3)
13
+ local STATUS_SYMBOLS = {
14
+ [TestEnum.TestStatus.Success] = "+",
15
+ [TestEnum.TestStatus.Failure] = "-",
16
+ [TestEnum.TestStatus.Skipped] = "~"
17
+ }
18
+ local UNKNOWN_STATUS_SYMBOL = "?"
19
+
20
+ local TextReporterQuiet = {}
21
+
22
+ local function reportNode(node, buffer, level)
23
+ buffer = buffer or {}
24
+ level = level or 0
25
+
26
+ if node.status == TestEnum.TestStatus.Skipped then
27
+ return buffer
28
+ end
29
+
30
+ local line
31
+
32
+ if node.status ~= TestEnum.TestStatus.Success then
33
+ local symbol = STATUS_SYMBOLS[node.status] or UNKNOWN_STATUS_SYMBOL
34
+
35
+ line = ("%s[%s] %s"):format(
36
+ INDENT:rep(level),
37
+ symbol,
38
+ node.planNode.phrase
39
+ )
40
+ end
41
+
42
+ table.insert(buffer, line)
43
+
44
+ for _, child in ipairs(node.children) do
45
+ reportNode(child, buffer, level + 1)
46
+ end
47
+
48
+ return buffer
49
+ end
50
+
51
+ local function reportRoot(node)
52
+ local buffer = {}
53
+
54
+ for _, child in ipairs(node.children) do
55
+ reportNode(child, buffer, 0)
56
+ end
57
+
58
+ return buffer
59
+ end
60
+
61
+ local function report(root)
62
+ local buffer = reportRoot(root)
63
+
64
+ return table.concat(buffer, "\n")
65
+ end
66
+
67
+ function TextReporterQuiet.report(results)
68
+ local resultBuffer = {
69
+ "Test results:",
70
+ report(results),
71
+ ("%d passed, %d failed, %d skipped"):format(
72
+ results.successCount,
73
+ results.failureCount,
74
+ results.skippedCount
75
+ )
76
+ }
77
+
78
+ print(table.concat(resultBuffer, "\n"))
79
+
80
+ if results.failureCount > 0 then
81
+ print(("%d test nodes reported failures."):format(results.failureCount))
82
+ end
83
+
84
+ if #results.errors > 0 then
85
+ print("Errors reported by tests:")
86
+ print("")
87
+
88
+ for _, message in ipairs(results.errors) do
89
+ TestService:Error(message)
90
+
91
+ -- Insert a blank line after each error
92
+ print("")
93
+ end
94
+ end
95
+ end
96
+
97
+ return TextReporterQuiet
@@ -0,0 +1,147 @@
1
+ --[[
2
+ Provides an interface to quickly run and report tests from a given object.
3
+ ]]
4
+
5
+ local TestPlanner = require(script.Parent.TestPlanner)
6
+ local TestRunner = require(script.Parent.TestRunner)
7
+ local TextReporter = require(script.Parent.Reporters.TextReporter)
8
+
9
+ local TestBootstrap = {}
10
+
11
+ local function stripSpecSuffix(name)
12
+ return (name:gsub("%.spec$", ""))
13
+ end
14
+ local function isSpecScript(aScript)
15
+ return aScript:IsA("ModuleScript") and aScript.Name:match("%.spec$")
16
+ end
17
+
18
+ local function getPath(module, root)
19
+ root = root or game
20
+
21
+ local path = {}
22
+ local last = module
23
+
24
+ if last.Name == "init.spec" then
25
+ -- Use the directory's node for init.spec files.
26
+ last = last.Parent
27
+ end
28
+
29
+ while last ~= nil and last ~= root do
30
+ table.insert(path, stripSpecSuffix(last.Name))
31
+ last = last.Parent
32
+ end
33
+ table.insert(path, stripSpecSuffix(root.Name))
34
+
35
+ return path
36
+ end
37
+
38
+ local function toStringPath(tablePath)
39
+ local stringPath = ""
40
+ local first = true
41
+ for _, element in ipairs(tablePath) do
42
+ if first then
43
+ stringPath = element
44
+ first = false
45
+ else
46
+ stringPath = element .. " " .. stringPath
47
+ end
48
+ end
49
+ return stringPath
50
+ end
51
+
52
+ function TestBootstrap:getModulesImpl(root, modules, current)
53
+ modules = modules or {}
54
+ current = current or root
55
+
56
+ if isSpecScript(current) then
57
+ local method = require(current)
58
+ local path = getPath(current, root)
59
+ local pathString = toStringPath(path)
60
+
61
+ table.insert(modules, {
62
+ method = method,
63
+ path = path,
64
+ pathStringForSorting = pathString:lower()
65
+ })
66
+ end
67
+ end
68
+
69
+ --[[
70
+ Find all the ModuleScripts in this tree that are tests.
71
+ ]]
72
+ function TestBootstrap:getModules(root)
73
+ local modules = {}
74
+
75
+ self:getModulesImpl(root, modules)
76
+
77
+ for _, child in ipairs(root:GetDescendants()) do
78
+ self:getModulesImpl(root, modules, child)
79
+ end
80
+
81
+ return modules
82
+ end
83
+
84
+ --[[
85
+ Runs all test and reports the results using the given test reporter.
86
+
87
+ If no reporter is specified, a reasonable default is provided.
88
+
89
+ This function demonstrates the expected workflow with this testing system:
90
+ 1. Locate test modules
91
+ 2. Generate test plan
92
+ 3. Run test plan
93
+ 4. Report test results
94
+
95
+ This means we could hypothetically present a GUI to the developer that shows
96
+ the test plan before we execute it, allowing them to toggle specific tests
97
+ before they're run, but after they've been identified!
98
+ ]]
99
+ function TestBootstrap:run(roots, reporter, otherOptions)
100
+ reporter = reporter or TextReporter
101
+
102
+ otherOptions = otherOptions or {}
103
+ local showTimingInfo = otherOptions["showTimingInfo"] or false
104
+ local testNamePattern = otherOptions["testNamePattern"]
105
+ local extraEnvironment = otherOptions["extraEnvironment"] or {}
106
+
107
+ if type(roots) ~= "table" then
108
+ error(("Bad argument #1 to TestBootstrap:run. Expected table, got %s"):format(typeof(roots)), 2)
109
+ end
110
+
111
+ local startTime = tick()
112
+
113
+ local modules = {}
114
+ for _, subRoot in ipairs(roots) do
115
+ local newModules = self:getModules(subRoot)
116
+
117
+ for _, newModule in ipairs(newModules) do
118
+ table.insert(modules, newModule)
119
+ end
120
+ end
121
+
122
+ local afterModules = tick()
123
+
124
+ local plan = TestPlanner.createPlan(modules, testNamePattern, extraEnvironment)
125
+ local afterPlan = tick()
126
+
127
+ local results = TestRunner.runPlan(plan)
128
+ local afterRun = tick()
129
+
130
+ reporter.report(results)
131
+ local afterReport = tick()
132
+
133
+ if showTimingInfo then
134
+ local timing = {
135
+ ("Took %f seconds to locate test modules"):format(afterModules - startTime),
136
+ ("Took %f seconds to create test plan"):format(afterPlan - afterModules),
137
+ ("Took %f seconds to run tests"):format(afterRun - afterPlan),
138
+ ("Took %f seconds to report tests"):format(afterReport - afterRun),
139
+ }
140
+
141
+ print(table.concat(timing, "\n"))
142
+ end
143
+
144
+ return results
145
+ end
146
+
147
+ return TestBootstrap
@@ -0,0 +1,28 @@
1
+ --[[
2
+ Constants used throughout the testing framework.
3
+ ]]
4
+
5
+ local TestEnum = {}
6
+
7
+ TestEnum.TestStatus = {
8
+ Success = "Success",
9
+ Failure = "Failure",
10
+ Skipped = "Skipped"
11
+ }
12
+
13
+ TestEnum.NodeType = {
14
+ Describe = "Describe",
15
+ It = "It",
16
+ BeforeAll = "BeforeAll",
17
+ AfterAll = "AfterAll",
18
+ BeforeEach = "BeforeEach",
19
+ AfterEach = "AfterEach"
20
+ }
21
+
22
+ TestEnum.NodeModifier = {
23
+ None = "None",
24
+ Skip = "Skip",
25
+ Focus = "Focus"
26
+ }
27
+
28
+ return TestEnum
@@ -0,0 +1,304 @@
1
+ --[[
2
+ Represents a tree of tests that have been loaded but not necessarily
3
+ executed yet.
4
+
5
+ TestPlan objects are produced by TestPlanner.
6
+ ]]
7
+
8
+ local TestEnum = require(script.Parent.TestEnum)
9
+ local Expectation = require(script.Parent.Expectation)
10
+
11
+ local function newEnvironment(currentNode, extraEnvironment)
12
+ local env = {}
13
+
14
+ if extraEnvironment then
15
+ if type(extraEnvironment) ~= "table" then
16
+ error(("Bad argument #2 to newEnvironment. Expected table, got %s"):format(
17
+ typeof(extraEnvironment)), 2)
18
+ end
19
+
20
+ for key, value in pairs(extraEnvironment) do
21
+ env[key] = value
22
+ end
23
+ end
24
+
25
+ local function addChild(phrase, callback, nodeType, nodeModifier)
26
+ local node = currentNode:addChild(phrase, nodeType, nodeModifier)
27
+ node.callback = callback
28
+ if nodeType == TestEnum.NodeType.Describe then
29
+ node:expand()
30
+ end
31
+ return node
32
+ end
33
+
34
+ function env.describeFOCUS(phrase, callback)
35
+ addChild(phrase, callback, TestEnum.NodeType.Describe, TestEnum.NodeModifier.Focus)
36
+ end
37
+
38
+ function env.describeSKIP(phrase, callback)
39
+ addChild(phrase, callback, TestEnum.NodeType.Describe, TestEnum.NodeModifier.Skip)
40
+ end
41
+
42
+ function env.describe(phrase, callback, nodeModifier)
43
+ addChild(phrase, callback, TestEnum.NodeType.Describe, TestEnum.NodeModifier.None)
44
+ end
45
+
46
+ function env.itFOCUS(phrase, callback)
47
+ addChild(phrase, callback, TestEnum.NodeType.It, TestEnum.NodeModifier.Focus)
48
+ end
49
+
50
+ function env.itSKIP(phrase, callback)
51
+ addChild(phrase, callback, TestEnum.NodeType.It, TestEnum.NodeModifier.Skip)
52
+ end
53
+
54
+ function env.itFIXME(phrase, callback)
55
+ local node = addChild(phrase, callback, TestEnum.NodeType.It, TestEnum.NodeModifier.Skip)
56
+ warn("FIXME: broken test", node:getFullName())
57
+ end
58
+
59
+ function env.it(phrase, callback, nodeModifier)
60
+ addChild(phrase, callback, TestEnum.NodeType.It, TestEnum.NodeModifier.None)
61
+ end
62
+
63
+ -- Incrementing counter used to ensure that beforeAll, afterAll, beforeEach, afterEach have unique phrases
64
+ local lifecyclePhaseId = 0
65
+
66
+ local lifecycleHooks = {
67
+ [TestEnum.NodeType.BeforeAll] = "beforeAll",
68
+ [TestEnum.NodeType.AfterAll] = "afterAll",
69
+ [TestEnum.NodeType.BeforeEach] = "beforeEach",
70
+ [TestEnum.NodeType.AfterEach] = "afterEach"
71
+ }
72
+
73
+ for nodeType, name in pairs(lifecycleHooks) do
74
+ env[name] = function(callback)
75
+ addChild(name .. "_" .. tostring(lifecyclePhaseId), callback, nodeType, TestEnum.NodeModifier.None)
76
+ lifecyclePhaseId = lifecyclePhaseId + 1
77
+ end
78
+ end
79
+
80
+ function env.FIXME(optionalMessage)
81
+ warn("FIXME: broken test", currentNode:getFullName(), optionalMessage or "")
82
+
83
+ currentNode.modifier = TestEnum.NodeModifier.Skip
84
+ end
85
+
86
+ function env.FOCUS()
87
+ currentNode.modifier = TestEnum.NodeModifier.Focus
88
+ end
89
+
90
+ function env.SKIP()
91
+ currentNode.modifier = TestEnum.NodeModifier.Skip
92
+ end
93
+
94
+ --[[
95
+ This function is deprecated. Calling it is a no-op beyond generating a
96
+ warning.
97
+ ]]
98
+ function env.HACK_NO_XPCALL()
99
+ warn("HACK_NO_XPCALL is deprecated. It is now safe to yield in an " ..
100
+ "xpcall, so this is no longer necessary. It can be safely deleted.")
101
+ end
102
+
103
+ env.fit = env.itFOCUS
104
+ env.xit = env.itSKIP
105
+ env.fdescribe = env.describeFOCUS
106
+ env.xdescribe = env.describeSKIP
107
+
108
+ env.expect = setmetatable({
109
+ extend = function(...)
110
+ error("Cannot call \"expect.extend\" from within a \"describe\" node.")
111
+ end,
112
+ }, {
113
+ __call = function(_self, ...)
114
+ return Expectation.new(...)
115
+ end,
116
+ })
117
+
118
+ return env
119
+ end
120
+
121
+ local TestNode = {}
122
+ TestNode.__index = TestNode
123
+
124
+ --[[
125
+ Create a new test node. A pointer to the test plan, a phrase to describe it
126
+ and the type of node it is are required. The modifier is optional and will
127
+ be None if left blank.
128
+ ]]
129
+ function TestNode.new(plan, phrase, nodeType, nodeModifier)
130
+ nodeModifier = nodeModifier or TestEnum.NodeModifier.None
131
+
132
+ local node = {
133
+ plan = plan,
134
+ phrase = phrase,
135
+ type = nodeType,
136
+ modifier = nodeModifier,
137
+ children = {},
138
+ callback = nil,
139
+ parent = nil,
140
+ }
141
+
142
+ node.environment = newEnvironment(node, plan.extraEnvironment)
143
+ return setmetatable(node, TestNode)
144
+ end
145
+
146
+ local function getModifier(name, pattern, modifier)
147
+ if pattern and (modifier == nil or modifier == TestEnum.NodeModifier.None) then
148
+ if name:match(pattern) then
149
+ return TestEnum.NodeModifier.Focus
150
+ else
151
+ return TestEnum.NodeModifier.Skip
152
+ end
153
+ end
154
+ return modifier
155
+ end
156
+
157
+ function TestNode:addChild(phrase, nodeType, nodeModifier)
158
+ if nodeType == TestEnum.NodeType.It then
159
+ for _, child in pairs(self.children) do
160
+ if child.phrase == phrase then
161
+ error("Duplicate it block found: " .. child:getFullName())
162
+ end
163
+ end
164
+ end
165
+
166
+ local childName = self:getFullName() .. " " .. phrase
167
+ nodeModifier = getModifier(childName, self.plan.testNamePattern, nodeModifier)
168
+ local child = TestNode.new(self.plan, phrase, nodeType, nodeModifier)
169
+ child.parent = self
170
+ table.insert(self.children, child)
171
+ return child
172
+ end
173
+
174
+ --[[
175
+ Join the names of all the nodes back to the parent.
176
+ ]]
177
+ function TestNode:getFullName()
178
+ if self.parent then
179
+ local parentPhrase = self.parent:getFullName()
180
+ if parentPhrase then
181
+ return parentPhrase .. " " .. self.phrase
182
+ end
183
+ end
184
+ return self.phrase
185
+ end
186
+
187
+ --[[
188
+ Expand a node by setting its callback environment and then calling it. Any
189
+ further it and describe calls within the callback will be added to the tree.
190
+ ]]
191
+ function TestNode:expand()
192
+ local originalEnv = getfenv(self.callback)
193
+ local callbackEnv = setmetatable({}, { __index = originalEnv })
194
+ for key, value in pairs(self.environment) do
195
+ callbackEnv[key] = value
196
+ end
197
+ -- Copy 'script' directly to new env to make Studio debugger happy.
198
+ -- Studio debugger does not look into __index, because of security reasons
199
+ callbackEnv.script = originalEnv.script
200
+ setfenv(self.callback, callbackEnv)
201
+
202
+ local success, result = xpcall(self.callback, function(message)
203
+ return debug.traceback(tostring(message), 2)
204
+ end)
205
+
206
+ if not success then
207
+ self.loadError = result
208
+ end
209
+ end
210
+
211
+ local TestPlan = {}
212
+ TestPlan.__index = TestPlan
213
+
214
+ --[[
215
+ Create a new, empty TestPlan.
216
+ ]]
217
+ function TestPlan.new(testNamePattern, extraEnvironment)
218
+ local plan = {
219
+ children = {},
220
+ testNamePattern = testNamePattern,
221
+ extraEnvironment = extraEnvironment,
222
+ }
223
+
224
+ return setmetatable(plan, TestPlan)
225
+ end
226
+
227
+ --[[
228
+ Add a new child under the test plan's root node.
229
+ ]]
230
+ function TestPlan:addChild(phrase, nodeType, nodeModifier)
231
+ nodeModifier = getModifier(phrase, self.testNamePattern, nodeModifier)
232
+ local child = TestNode.new(self, phrase, nodeType, nodeModifier)
233
+ table.insert(self.children, child)
234
+ return child
235
+ end
236
+
237
+ --[[
238
+ Add a new describe node with the given method as a callback. Generates or
239
+ reuses all the describe nodes along the path.
240
+ ]]
241
+ function TestPlan:addRoot(path, method)
242
+ local curNode = self
243
+ for i = #path, 1, -1 do
244
+ local nextNode = nil
245
+
246
+ for _, child in ipairs(curNode.children) do
247
+ if child.phrase == path[i] then
248
+ nextNode = child
249
+ break
250
+ end
251
+ end
252
+
253
+ if nextNode == nil then
254
+ nextNode = curNode:addChild(path[i], TestEnum.NodeType.Describe)
255
+ end
256
+
257
+ curNode = nextNode
258
+ end
259
+
260
+ curNode.callback = method
261
+ curNode:expand()
262
+ end
263
+
264
+ --[[
265
+ Calls the given callback on all nodes in the tree, traversed depth-first.
266
+ ]]
267
+ function TestPlan:visitAllNodes(callback, root, level)
268
+ root = root or self
269
+ level = level or 0
270
+
271
+ for _, child in ipairs(root.children) do
272
+ callback(child, level)
273
+
274
+ self:visitAllNodes(callback, child, level + 1)
275
+ end
276
+ end
277
+
278
+ --[[
279
+ Visualizes the test plan in a simple format, suitable for debugging the test
280
+ plan's structure.
281
+ ]]
282
+ function TestPlan:visualize()
283
+ local buffer = {}
284
+ self:visitAllNodes(function(node, level)
285
+ table.insert(buffer, (" "):rep(3 * level) .. node.phrase)
286
+ end)
287
+ return table.concat(buffer, "\n")
288
+ end
289
+
290
+ --[[
291
+ Gets a list of all nodes in the tree for which the given callback returns
292
+ true.
293
+ ]]
294
+ function TestPlan:findNodes(callback)
295
+ local results = {}
296
+ self:visitAllNodes(function(node)
297
+ if callback(node) then
298
+ table.insert(results, node)
299
+ end
300
+ end)
301
+ return results
302
+ end
303
+
304
+ return TestPlan
@@ -0,0 +1,40 @@
1
+ --[[
2
+ Turns a series of specification functions into a test plan.
3
+
4
+ Uses a TestPlanBuilder to keep track of the state of the tree being built.
5
+ ]]
6
+ local TestPlan = require(script.Parent.TestPlan)
7
+
8
+ local TestPlanner = {}
9
+
10
+ --[[
11
+ Create a new TestPlan from a list of specification functions.
12
+
13
+ These functions should call a combination of `describe` and `it` (and their
14
+ variants), which will be turned into a test plan to be executed.
15
+
16
+ Parameters:
17
+ - modulesList - list of tables describing test modules {
18
+ method, -- specification function described above
19
+ path, -- array of parent entires, first element is the leaf that owns `method`
20
+ pathStringForSorting -- a string representation of `path`, used for sorting of the test plan
21
+ }
22
+ - testNamePattern - Only tests matching this Lua pattern string will run. Pass empty or nil to run all tests
23
+ - extraEnvironment - Lua table holding additional functions and variables to be injected into the specification
24
+ function during execution
25
+ ]]
26
+ function TestPlanner.createPlan(modulesList, testNamePattern, extraEnvironment)
27
+ local plan = TestPlan.new(testNamePattern, extraEnvironment)
28
+
29
+ table.sort(modulesList, function(a, b)
30
+ return a.pathStringForSorting < b.pathStringForSorting
31
+ end)
32
+
33
+ for _, module in ipairs(modulesList) do
34
+ plan:addRoot(module.path, module.method)
35
+ end
36
+
37
+ return plan
38
+ end
39
+
40
+ return TestPlanner