hotstaq 0.5.3

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 (318) hide show
  1. package/.dockerignore +123 -0
  2. package/.eslintignore +17 -0
  3. package/.eslintrc.js +11 -0
  4. package/.vscode/launch.json +201 -0
  5. package/.vscode/settings.json +2 -0
  6. package/.vscode/tasks.json +88 -0
  7. package/CONTRIBUTING.md +157 -0
  8. package/LICENSE +21 -0
  9. package/README.md +128 -0
  10. package/bin/hotstaq +3 -0
  11. package/bin/hotstaq.cmd +1 -0
  12. package/build/src/Hot.d.ts +167 -0
  13. package/build/src/Hot.d.ts.map +1 -0
  14. package/build/src/Hot.js +365 -0
  15. package/build/src/Hot.js.map +1 -0
  16. package/build/src/HotAPI.d.ts +137 -0
  17. package/build/src/HotAPI.d.ts.map +1 -0
  18. package/build/src/HotAPI.js +353 -0
  19. package/build/src/HotAPI.js.map +1 -0
  20. package/build/src/HotAgentAPI.d.ts +22 -0
  21. package/build/src/HotAgentAPI.d.ts.map +1 -0
  22. package/build/src/HotAgentAPI.js +85 -0
  23. package/build/src/HotAgentAPI.js.map +1 -0
  24. package/build/src/HotAgentRoute.d.ts +17 -0
  25. package/build/src/HotAgentRoute.d.ts.map +1 -0
  26. package/build/src/HotAgentRoute.js +109 -0
  27. package/build/src/HotAgentRoute.js.map +1 -0
  28. package/build/src/HotBuilder.d.ts +52 -0
  29. package/build/src/HotBuilder.d.ts.map +1 -0
  30. package/build/src/HotBuilder.js +242 -0
  31. package/build/src/HotBuilder.js.map +1 -0
  32. package/build/src/HotClient.d.ts +31 -0
  33. package/build/src/HotClient.d.ts.map +1 -0
  34. package/build/src/HotClient.js +19 -0
  35. package/build/src/HotClient.js.map +1 -0
  36. package/build/src/HotComponent.d.ts +118 -0
  37. package/build/src/HotComponent.d.ts.map +1 -0
  38. package/build/src/HotComponent.js +89 -0
  39. package/build/src/HotComponent.js.map +1 -0
  40. package/build/src/HotCreator.d.ts +88 -0
  41. package/build/src/HotCreator.d.ts.map +1 -0
  42. package/build/src/HotCreator.js +445 -0
  43. package/build/src/HotCreator.js.map +1 -0
  44. package/build/src/HotDB.d.ts +69 -0
  45. package/build/src/HotDB.d.ts.map +1 -0
  46. package/build/src/HotDB.js +29 -0
  47. package/build/src/HotDB.js.map +1 -0
  48. package/build/src/HotDBConnectionInterface.d.ts +40 -0
  49. package/build/src/HotDBConnectionInterface.d.ts.map +1 -0
  50. package/build/src/HotDBConnectionInterface.js +3 -0
  51. package/build/src/HotDBConnectionInterface.js.map +1 -0
  52. package/build/src/HotFile.d.ts +134 -0
  53. package/build/src/HotFile.d.ts.map +1 -0
  54. package/build/src/HotFile.js +454 -0
  55. package/build/src/HotFile.js.map +1 -0
  56. package/build/src/HotGenerator.d.ts +80 -0
  57. package/build/src/HotGenerator.d.ts.map +1 -0
  58. package/build/src/HotGenerator.js +342 -0
  59. package/build/src/HotGenerator.js.map +1 -0
  60. package/build/src/HotHTTPServer.d.ts +162 -0
  61. package/build/src/HotHTTPServer.d.ts.map +1 -0
  62. package/build/src/HotHTTPServer.js +863 -0
  63. package/build/src/HotHTTPServer.js.map +1 -0
  64. package/build/src/HotIO.d.ts +47 -0
  65. package/build/src/HotIO.d.ts.map +1 -0
  66. package/build/src/HotIO.js +232 -0
  67. package/build/src/HotIO.js.map +1 -0
  68. package/build/src/HotLog.d.ts +60 -0
  69. package/build/src/HotLog.d.ts.map +1 -0
  70. package/build/src/HotLog.js +126 -0
  71. package/build/src/HotLog.js.map +1 -0
  72. package/build/src/HotPage.d.ts +125 -0
  73. package/build/src/HotPage.d.ts.map +1 -0
  74. package/build/src/HotPage.js +178 -0
  75. package/build/src/HotPage.js.map +1 -0
  76. package/build/src/HotRoute.d.ts +82 -0
  77. package/build/src/HotRoute.d.ts.map +1 -0
  78. package/build/src/HotRoute.js +83 -0
  79. package/build/src/HotRoute.js.map +1 -0
  80. package/build/src/HotRouteMethod.d.ts +129 -0
  81. package/build/src/HotRouteMethod.d.ts.map +1 -0
  82. package/build/src/HotRouteMethod.js +84 -0
  83. package/build/src/HotRouteMethod.js.map +1 -0
  84. package/build/src/HotServer.d.ts +152 -0
  85. package/build/src/HotServer.d.ts.map +1 -0
  86. package/build/src/HotServer.js +109 -0
  87. package/build/src/HotServer.js.map +1 -0
  88. package/build/src/HotSetAsWeb.d.ts +2 -0
  89. package/build/src/HotSetAsWeb.d.ts.map +1 -0
  90. package/build/src/HotSetAsWeb.js +5 -0
  91. package/build/src/HotSetAsWeb.js.map +1 -0
  92. package/build/src/HotStaq.d.ts +603 -0
  93. package/build/src/HotStaq.d.ts.map +1 -0
  94. package/build/src/HotStaq.js +1260 -0
  95. package/build/src/HotStaq.js.map +1 -0
  96. package/build/src/HotStaqWeb.d.ts +18 -0
  97. package/build/src/HotStaqWeb.d.ts.map +1 -0
  98. package/build/src/HotStaqWeb.js +44 -0
  99. package/build/src/HotStaqWeb.js.map +1 -0
  100. package/build/src/HotTestDriver.d.ts +63 -0
  101. package/build/src/HotTestDriver.d.ts.map +1 -0
  102. package/build/src/HotTestDriver.js +187 -0
  103. package/build/src/HotTestDriver.js.map +1 -0
  104. package/build/src/HotTestElement.d.ts +71 -0
  105. package/build/src/HotTestElement.d.ts.map +1 -0
  106. package/build/src/HotTestElement.js +37 -0
  107. package/build/src/HotTestElement.js.map +1 -0
  108. package/build/src/HotTestMap.d.ts +83 -0
  109. package/build/src/HotTestMap.d.ts.map +1 -0
  110. package/build/src/HotTestMap.js +58 -0
  111. package/build/src/HotTestMap.js.map +1 -0
  112. package/build/src/HotTestSeleniumDriver.d.ts +86 -0
  113. package/build/src/HotTestSeleniumDriver.d.ts.map +1 -0
  114. package/build/src/HotTestSeleniumDriver.js +400 -0
  115. package/build/src/HotTestSeleniumDriver.js.map +1 -0
  116. package/build/src/HotTester.d.ts +188 -0
  117. package/build/src/HotTester.d.ts.map +1 -0
  118. package/build/src/HotTester.js +622 -0
  119. package/build/src/HotTester.js.map +1 -0
  120. package/build/src/HotTesterAPI.d.ts +15 -0
  121. package/build/src/HotTesterAPI.d.ts.map +1 -0
  122. package/build/src/HotTesterAPI.js +161 -0
  123. package/build/src/HotTesterAPI.js.map +1 -0
  124. package/build/src/HotTesterMocha.d.ts +50 -0
  125. package/build/src/HotTesterMocha.d.ts.map +1 -0
  126. package/build/src/HotTesterMocha.js +205 -0
  127. package/build/src/HotTesterMocha.js.map +1 -0
  128. package/build/src/HotTesterMochaSelenium.d.ts +70 -0
  129. package/build/src/HotTesterMochaSelenium.d.ts.map +1 -0
  130. package/build/src/HotTesterMochaSelenium.js +257 -0
  131. package/build/src/HotTesterMochaSelenium.js.map +1 -0
  132. package/build/src/HotTesterServer.d.ts +114 -0
  133. package/build/src/HotTesterServer.d.ts.map +1 -0
  134. package/build/src/HotTesterServer.js +575 -0
  135. package/build/src/HotTesterServer.js.map +1 -0
  136. package/build/src/api copy.d.ts +2 -0
  137. package/build/src/api copy.d.ts.map +1 -0
  138. package/build/src/api copy.js +153 -0
  139. package/build/src/api copy.js.map +1 -0
  140. package/build/src/api-web.d.ts +2 -0
  141. package/build/src/api-web.d.ts.map +1 -0
  142. package/build/src/api-web.js +45 -0
  143. package/build/src/api-web.js.map +1 -0
  144. package/build/src/api.d.ts +33 -0
  145. package/build/src/api.d.ts.map +1 -0
  146. package/build/src/api.js +78 -0
  147. package/build/src/api.js.map +1 -0
  148. package/build/src/cli.d.ts +2 -0
  149. package/build/src/cli.d.ts.map +1 -0
  150. package/build/src/cli.js +1040 -0
  151. package/build/src/cli.js.map +1 -0
  152. package/build/src/schemas/HotDBInflux.d.ts +63 -0
  153. package/build/src/schemas/HotDBInflux.d.ts.map +1 -0
  154. package/build/src/schemas/HotDBInflux.js +239 -0
  155. package/build/src/schemas/HotDBInflux.js.map +1 -0
  156. package/build/src/schemas/HotDBMigration.d.ts +19 -0
  157. package/build/src/schemas/HotDBMigration.d.ts.map +1 -0
  158. package/build/src/schemas/HotDBMigration.js +15 -0
  159. package/build/src/schemas/HotDBMigration.js.map +1 -0
  160. package/build/src/schemas/HotDBMySQL.d.ts +65 -0
  161. package/build/src/schemas/HotDBMySQL.d.ts.map +1 -0
  162. package/build/src/schemas/HotDBMySQL.js +387 -0
  163. package/build/src/schemas/HotDBMySQL.js.map +1 -0
  164. package/build/src/schemas/HotDBSchema.d.ts +15 -0
  165. package/build/src/schemas/HotDBSchema.d.ts.map +1 -0
  166. package/build/src/schemas/HotDBSchema.js +19 -0
  167. package/build/src/schemas/HotDBSchema.js.map +1 -0
  168. package/build/src/schemas/influx/InfluxSchema.d.ts +14 -0
  169. package/build/src/schemas/influx/InfluxSchema.d.ts.map +1 -0
  170. package/build/src/schemas/influx/InfluxSchema.js +33 -0
  171. package/build/src/schemas/influx/InfluxSchema.js.map +1 -0
  172. package/build/src/schemas/mysql/MySQLSchema.d.ts +39 -0
  173. package/build/src/schemas/mysql/MySQLSchema.d.ts.map +1 -0
  174. package/build/src/schemas/mysql/MySQLSchema.js +151 -0
  175. package/build/src/schemas/mysql/MySQLSchema.js.map +1 -0
  176. package/build/src/schemas/mysql/MySQLSchemaField.d.ts +168 -0
  177. package/build/src/schemas/mysql/MySQLSchemaField.d.ts.map +1 -0
  178. package/build/src/schemas/mysql/MySQLSchemaField.js +260 -0
  179. package/build/src/schemas/mysql/MySQLSchemaField.js.map +1 -0
  180. package/build/src/schemas/mysql/MySQLSchemaTable.d.ts +49 -0
  181. package/build/src/schemas/mysql/MySQLSchemaTable.d.ts.map +1 -0
  182. package/build/src/schemas/mysql/MySQLSchemaTable.js +310 -0
  183. package/build/src/schemas/mysql/MySQLSchemaTable.js.map +1 -0
  184. package/build-web/HotStaq.js +2 -0
  185. package/build-web/HotStaq.min.js +119 -0
  186. package/build-web/HotStaqTests_HelloWorldAPI.js +133 -0
  187. package/builder/docker/Dockerfile.linux.gen +42 -0
  188. package/builder/docker/README.md +36 -0
  189. package/builder/docker/app/start.sh +8 -0
  190. package/builder/docker/dockerignore +3 -0
  191. package/builder/docker/scripts/build.bat +11 -0
  192. package/builder/docker/scripts/build.sh +11 -0
  193. package/builder/docker/scripts/start-app.bat +7 -0
  194. package/builder/docker/scripts/start-app.sh +7 -0
  195. package/builder/docker/scripts/stop-app.bat +5 -0
  196. package/builder/docker/scripts/stop-app.sh +5 -0
  197. package/builder/docker-compose/docker-compose.gen.yaml +41 -0
  198. package/builder/docker-compose/env-skeleton +4 -0
  199. package/creator/project/.vscode/launch.json +59 -0
  200. package/creator/project/README.md +20 -0
  201. package/creator/project/gitignore +118 -0
  202. package/creator/project/npmignore +118 -0
  203. package/creator/public/api-test.hott +28 -0
  204. package/creator/public/index.hott +12 -0
  205. package/creator/ts/src/AppAPI.ts +30 -0
  206. package/creator/ts/src/HelloWorld.ts +39 -0
  207. package/creator/ts/src/WebExport.ts +7 -0
  208. package/creator/ts/tsconfig-web.json +73 -0
  209. package/creator/ts/tsconfig.json +73 -0
  210. package/creator/ts/webpack-api.config.js +57 -0
  211. package/dbstart.sh +19 -0
  212. package/dbstop.sh +4 -0
  213. package/docs/.nojekyll +1 -0
  214. package/docs/README.md +130 -0
  215. package/docs/classes/Hot.md +477 -0
  216. package/docs/classes/HotAPI.md +369 -0
  217. package/docs/classes/HotClient.md +95 -0
  218. package/docs/classes/HotComponent.md +279 -0
  219. package/docs/classes/HotDB.md +247 -0
  220. package/docs/classes/HotDBInflux.md +404 -0
  221. package/docs/classes/HotDBMigration.md +80 -0
  222. package/docs/classes/HotDBMySQL.md +310 -0
  223. package/docs/classes/HotDBSchema.md +51 -0
  224. package/docs/classes/HotFile.md +353 -0
  225. package/docs/classes/HotHTTPServer.md +700 -0
  226. package/docs/classes/HotLog.md +162 -0
  227. package/docs/classes/HotPage.md +357 -0
  228. package/docs/classes/HotRoute.md +312 -0
  229. package/docs/classes/HotRouteMethod.md +271 -0
  230. package/docs/classes/HotServer.md +311 -0
  231. package/docs/classes/HotStaq.md +1155 -0
  232. package/docs/classes/HotTestDestination.md +58 -0
  233. package/docs/classes/HotTestDriver.md +332 -0
  234. package/docs/classes/HotTestElement.md +88 -0
  235. package/docs/classes/HotTestElementOptions.md +71 -0
  236. package/docs/classes/HotTestMap.md +92 -0
  237. package/docs/classes/HotTestSeleniumDriver.md +542 -0
  238. package/docs/classes/HotTester.md +653 -0
  239. package/docs/classes/HotTesterAPI.md +493 -0
  240. package/docs/classes/HotTesterMocha.md +843 -0
  241. package/docs/classes/HotTesterMochaSelenium.md +896 -0
  242. package/docs/classes/HotTesterServer.md +633 -0
  243. package/docs/classes/InfluxSchema.md +74 -0
  244. package/docs/classes/MySQLSchema.md +199 -0
  245. package/docs/classes/MySQLSchemaField.md +330 -0
  246. package/docs/classes/MySQLSchemaTable.md +176 -0
  247. package/docs/enums/ConnectionStatus.md +43 -0
  248. package/docs/enums/DeveloperMode.md +38 -0
  249. package/docs/enums/EventExecutionType.md +43 -0
  250. package/docs/enums/HTTPMethod.md +32 -0
  251. package/docs/enums/HotDBGenerationType.md +30 -0
  252. package/docs/enums/HotLogLevel.md +88 -0
  253. package/docs/interfaces/HotDBConnectionInterface.md +116 -0
  254. package/docs/interfaces/HotDestination.md +62 -0
  255. package/docs/interfaces/HotSite.md +187 -0
  256. package/docs/interfaces/HotSiteMapPath.md +37 -0
  257. package/docs/interfaces/HotSiteRoute.md +79 -0
  258. package/docs/interfaces/HotStartOptions.md +115 -0
  259. package/docs/interfaces/HotTestPage.md +44 -0
  260. package/docs/interfaces/HotTestStop.md +62 -0
  261. package/docs/interfaces/IHotComponent.md +135 -0
  262. package/docs/interfaces/IHotStaq.md +118 -0
  263. package/docs/interfaces/IHotTestElement.md +54 -0
  264. package/docs/interfaces/IHotTestElementOptions.md +43 -0
  265. package/docs/interfaces/MySQLResults.md +43 -0
  266. package/docs/interfaces/MySQLSchemaFieldResult.md +75 -0
  267. package/docs/modules.md +182 -0
  268. package/package.json +65 -0
  269. package/selenium-start.sh +7 -0
  270. package/selenium-stop.sh +3 -0
  271. package/src/Hot.ts +319 -0
  272. package/src/HotAPI.ts +386 -0
  273. package/src/HotAgentAPI.ts +43 -0
  274. package/src/HotAgentRoute.ts +44 -0
  275. package/src/HotBuilder.ts +221 -0
  276. package/src/HotClient.ts +40 -0
  277. package/src/HotComponent.ts +158 -0
  278. package/src/HotCreator.ts +470 -0
  279. package/src/HotDB.ts +79 -0
  280. package/src/HotDBConnectionInterface.ts +40 -0
  281. package/src/HotFile.ts +617 -0
  282. package/src/HotGenerator.ts +446 -0
  283. package/src/HotHTTPServer.ts +954 -0
  284. package/src/HotIO.ts +160 -0
  285. package/src/HotLog.ts +158 -0
  286. package/src/HotPage.ts +206 -0
  287. package/src/HotRoute.ts +137 -0
  288. package/src/HotRouteMethod.ts +216 -0
  289. package/src/HotServer.ts +211 -0
  290. package/src/HotSetAsWeb.ts +3 -0
  291. package/src/HotStaq.ts +1881 -0
  292. package/src/HotTestDriver.ts +171 -0
  293. package/src/HotTestElement.ts +97 -0
  294. package/src/HotTestMap.ts +130 -0
  295. package/src/HotTestSeleniumDriver.ts +381 -0
  296. package/src/HotTester.ts +696 -0
  297. package/src/HotTesterAPI.ts +126 -0
  298. package/src/HotTesterMocha.ts +133 -0
  299. package/src/HotTesterMochaSelenium.ts +189 -0
  300. package/src/HotTesterServer.ts +551 -0
  301. package/src/api-web.ts +48 -0
  302. package/src/api.ts +103 -0
  303. package/src/cli.ts +1225 -0
  304. package/src/schemas/HotDBInflux.ts +211 -0
  305. package/src/schemas/HotDBMigration.ts +24 -0
  306. package/src/schemas/HotDBMySQL.ts +312 -0
  307. package/src/schemas/HotDBSchema.ts +21 -0
  308. package/src/schemas/influx/InfluxSchema.ts +19 -0
  309. package/src/schemas/mysql/MySQLSchema.ts +90 -0
  310. package/src/schemas/mysql/MySQLSchemaField.ts +408 -0
  311. package/src/schemas/mysql/MySQLSchemaTable.ts +353 -0
  312. package/temp/HotStaqWeb.ts +59 -0
  313. package/tsconfig-generator.json +17 -0
  314. package/tsconfig-web.json +74 -0
  315. package/tsconfig.json +73 -0
  316. package/webpack.config.generator.js +41 -0
  317. package/webpack.config.js +53 -0
  318. package/webpack.config.tests.js +56 -0
package/src/HotStaq.ts ADDED
@@ -0,0 +1,1881 @@
1
+ import * as fs from "fs";
2
+ import * as ppath from "path";
3
+
4
+ import fetch from "cross-fetch";
5
+ import validateModuleName from "validate-npm-package-name";
6
+
7
+ import { HotPage } from "./HotPage";
8
+ import { HotFile } from "./HotFile";
9
+
10
+ import { HotComponent } from "./HotComponent";
11
+ import { HotLog, HotLogLevel } from "./HotLog";
12
+ import { HotAPI } from "./HotAPI";
13
+ import { HotServer } from "./HotServer";
14
+ import { DeveloperMode } from "./Hot";
15
+ import { HotClient } from "./HotClient";
16
+
17
+ import { HotTester } from "./HotTester";
18
+ import { HotTesterAPI } from "./HotTesterAPI";
19
+ import { HotTestDriver } from "./HotTestDriver";
20
+ import { HotTestDestination, HotTestMap } from "./HotTestMap";
21
+
22
+ var HotTesterMocha: any = null;
23
+ var HotTesterMochaSelenium: any = null;
24
+ var HotTestSeleniumDriver: any = null;
25
+
26
+ /**
27
+ * A map path for testing.
28
+ */
29
+ export interface HotSiteMapPath
30
+ {
31
+ /**
32
+ * If set to true, this will start automatically when tests start.
33
+ * The default is true.
34
+ */
35
+ autoStart?: boolean;
36
+ /**
37
+ * The path to the
38
+ */
39
+ path?: string;
40
+ }
41
+
42
+ /**
43
+ * A route used in a HotSite.
44
+ */
45
+ export interface HotSiteRoute
46
+ {
47
+ /**
48
+ * The name of the route. Will appear in the title.
49
+ */
50
+ name: string;
51
+ /**
52
+ * The url to the file to load.
53
+ */
54
+ url: string;
55
+ /**
56
+ * The name of the API to interface with.
57
+ */
58
+ api?: string;
59
+ /**
60
+ * The order in which destinations are supposed to execute. This is
61
+ * ignored if the destinations are an array.
62
+ */
63
+ destinationOrder?: string[];
64
+ /**
65
+ * The HotTesterMap to use. This can be the name of an
66
+ * existing one attached to the selected tester, or
67
+ * can be an array of destinations that will be used to
68
+ * create a new map.
69
+ */
70
+ map?: string | string[] | { [name: string]: string | HotSiteMapPath; } | HotSiteMapPath[];
71
+ }
72
+
73
+ /**
74
+ * A HotSite to load. This SHOULD NOT contain any private secret keys, passwords,
75
+ * or database connection information related to the server. As such, future
76
+ * versions of the HotSite interface should not contain any database related
77
+ * connection info.
78
+ */
79
+ export interface HotSite
80
+ {
81
+ /**
82
+ * The name of this HotSite.
83
+ */
84
+ name: string;
85
+ /**
86
+ * The path to the current HotSite. This is filled in during parsing.
87
+ */
88
+ hotsitePath?: string;
89
+ /**
90
+ * Additional web server configuration.
91
+ */
92
+ server?: {
93
+ /**
94
+ * The default name for a served Hott file.
95
+ */
96
+ name?: string;
97
+ /**
98
+ * Serve hott files when requested.
99
+ */
100
+ serveHottFiles?: boolean;
101
+ /**
102
+ * The name of the API to interface with across all pages.
103
+ */
104
+ globalApi?: string;
105
+ /**
106
+ * The base url for the server.
107
+ */
108
+ url?: string;
109
+ /**
110
+ * The JavaScript source path.
111
+ */
112
+ jsSrcPath?: string;
113
+ /**
114
+ * The ports to use.
115
+ */
116
+ ports?: {
117
+ /**
118
+ * The HTTP port to serve on.
119
+ */
120
+ http?: number;
121
+ /**
122
+ * The HTTPS port to serve on.
123
+ */
124
+ https?: number;
125
+ /**
126
+ * If set to true, this will redirect from HTTP to HTTPS.
127
+ */
128
+ redirectHTTPtoHTTPS?: boolean;
129
+ };
130
+ /**
131
+ * The list of directory to serve to the client from the server.
132
+ */
133
+ serveDirectories?: {
134
+ /**
135
+ * The web route to take.
136
+ */
137
+ route: string;
138
+ /**
139
+ * The local filesystem path to serve pages from.
140
+ */
141
+ localPath: string;
142
+ }[];
143
+ };
144
+ /**
145
+ * Testing related functionality.
146
+ */
147
+ testing?: {
148
+ web?: {
149
+ /**
150
+ * The tester class to use. EX: HotTesterMochaSelenium
151
+ */
152
+ tester?: string;
153
+ /**
154
+ * The name of the tester to use.
155
+ */
156
+ testerName?: string;
157
+ /**
158
+ * If set to true, this will create a new tester.
159
+ * Default Value: true
160
+ */
161
+ createNewTester?: boolean;
162
+ /**
163
+ * The url that connects to the tester api server.
164
+ */
165
+ testerAPIUrl?: string;
166
+ /**
167
+ * The name of the test driver to use.
168
+ */
169
+ driver?: string;
170
+ /**
171
+ * The url to the html that loads the hott files.
172
+ */
173
+ launchpadUrl?: string;
174
+ /**
175
+ * The maps to test in order.
176
+ */
177
+ maps?: string[];
178
+ },
179
+ api?: {
180
+ /**
181
+ * The tester class to use. EX: HotTesterMocha
182
+ */
183
+ tester?: string;
184
+ /**
185
+ * The name of the tester to use.
186
+ */
187
+ testerName?: string;
188
+ /**
189
+ * If set to true, this will create a new tester.
190
+ * Default Value: true
191
+ */
192
+ createNewTester?: boolean;
193
+ /**
194
+ * The url that connects to the tester api server.
195
+ */
196
+ testerAPIUrl?: string;
197
+ /**
198
+ * The name of the test driver to use.
199
+ */
200
+ driver?: string;
201
+ /**
202
+ * The url to the html that loads the hott files.
203
+ */
204
+ launchpadUrl?: string;
205
+ /**
206
+ * The maps to test in order.
207
+ */
208
+ maps?: string[];
209
+ }
210
+ };
211
+ /**
212
+ * The routes to load.
213
+ */
214
+ routes?: {
215
+ [routeName: string]: HotSiteRoute;
216
+ };
217
+ /**
218
+ * The available APIs on the server. The server must already have these
219
+ * loaded.
220
+ */
221
+ apis?: {
222
+ [name: string]: {
223
+ /**
224
+ * The JS API file to load.
225
+ */
226
+ jsapi?: string;
227
+ /**
228
+ * The exported JS library name to use.
229
+ */
230
+ libraryName?: string;
231
+ /**
232
+ * The name of the api to use.
233
+ */
234
+ apiName?: string;
235
+ /**
236
+ * The port to use.
237
+ */
238
+ port?: number;
239
+ /**
240
+ * The public base url for the api.
241
+ */
242
+ url?: string;
243
+ /**
244
+ * The server-side filepath for the api.
245
+ */
246
+ filepath?: string;
247
+ /**
248
+ * The maps to test in order.
249
+ */
250
+ map?: string[];
251
+ };
252
+ };
253
+ /**
254
+ * Secrets that can be publicly embedded into the page.
255
+ */
256
+ publicSecrets?: {
257
+ [name: string]: string | {
258
+ /**
259
+ * The key of an API secret to pass to the site to
260
+ * be used publicly.
261
+ */
262
+ passSecretFromAPI?: string;
263
+ /**
264
+ * Get the public secret from an environment variable.
265
+ */
266
+ env?: string;
267
+ };
268
+ };
269
+ /**
270
+ * The components to load and register.
271
+ */
272
+ components?: {
273
+ [name: string]: {
274
+ /**
275
+ * The url to the component to load and register.
276
+ */
277
+ url: string;
278
+ };
279
+ };
280
+ /**
281
+ * The files to load and save in memory.
282
+ */
283
+ files?: {
284
+ [name: string]: {
285
+ /**
286
+ * The url to the file to load.
287
+ */
288
+ url: string;
289
+ };
290
+ };
291
+ }
292
+
293
+ /**
294
+ * The options to use when starting a page.
295
+ */
296
+ export interface HotStartOptions
297
+ {
298
+ /**
299
+ * The Hott site to load.
300
+ */
301
+ url: string;
302
+ /**
303
+ * The name of the page to load.
304
+ */
305
+ name?: string;
306
+ /**
307
+ * The processor to use to load the page.
308
+ */
309
+ processor?: HotStaq;
310
+ /**
311
+ * Any arguments to pass to the new page.
312
+ */
313
+ args?: any;
314
+ /**
315
+ * The name of the tester to use.
316
+ */
317
+ testerName?: string;
318
+ /**
319
+ * The name of the tester map to use.
320
+ */
321
+ testerMap?: string;
322
+ /**
323
+ * The base url for the tester api.
324
+ */
325
+ testerAPIBaseUrl?: string;
326
+ /**
327
+ * The url to the html that loads the hott file that's
328
+ * pointed at the url above.
329
+ */
330
+ testerLaunchpadUrl?: string;
331
+ }
332
+
333
+ /**
334
+ * The main class that handles all HTML preprocessing, then outputs the
335
+ * results.
336
+ */
337
+ export interface IHotStaq
338
+ {
339
+ /**
340
+ * The api that's used to communicate with.
341
+ */
342
+ api?: HotAPI;
343
+ /**
344
+ * The tester api that's used to communicate with.
345
+ */
346
+ testerAPI?: HotAPI;
347
+ /**
348
+ * Indicates what type of execution this is.
349
+ */
350
+ mode?: DeveloperMode;
351
+ /**
352
+ * The pages that can be constructed.
353
+ */
354
+ pages?: { [name: string]: HotPage };
355
+ /**
356
+ * The components that can be constructed.
357
+ */
358
+ components?: { [name: string]: HotComponent };
359
+ /**
360
+ * The files that can be stored for later use.
361
+ */
362
+ files?: { [name: string]: HotFile };
363
+ /**
364
+ * The loaded hotsite.
365
+ */
366
+ hotSite?: HotSite;
367
+ }
368
+
369
+ /**
370
+ * The main class that handles all HTML preprocessing, then outputs the
371
+ * results.
372
+ */
373
+ export class HotStaq implements IHotStaq
374
+ {
375
+ /**
376
+ * Indicates if this is a web build.
377
+ */
378
+ static isWeb: boolean = false;
379
+ /**
380
+ * Indicates if this is ready for testing.
381
+ */
382
+ static isReadyForTesting: boolean = false;
383
+ /**
384
+ * Executes this event when this page is ready for testing.
385
+ */
386
+ static onReadyForTesting: () => Promise<void> = null;
387
+ /**
388
+ * Indicates what type of execution this is.
389
+ */
390
+ mode: DeveloperMode;
391
+ /**
392
+ * The api that's used to communicate with.
393
+ */
394
+ api: HotAPI;
395
+ /**
396
+ * The tester api that's used to communicate with.
397
+ */
398
+ testerAPI: HotAPI;
399
+ /**
400
+ * The pages that can be constructed.
401
+ */
402
+ pages: { [name: string]: HotPage };
403
+ /**
404
+ * The components that can be constructed.
405
+ */
406
+ components: { [name: string]: HotComponent };
407
+ /**
408
+ * The files that can be stored for later use.
409
+ */
410
+ files: { [name: string]: HotFile };
411
+ /**
412
+ * The loaded hotsite.
413
+ */
414
+ hotSite: HotSite;
415
+ /**
416
+ * The api content to use when about to load HotStaq.
417
+ */
418
+ apiContent: string;
419
+ /**
420
+ * The tester api content to use when about to load HotStaq.
421
+ */
422
+ testerApiContent: string;
423
+ /**
424
+ * The page content to use when about to load HotStaq.
425
+ */
426
+ pageContent: string;
427
+ /**
428
+ * The logger.
429
+ */
430
+ logger: HotLog;
431
+ /**
432
+ * The secrets that can be exposed publicly.
433
+ */
434
+ publicSecrets: any;
435
+ /**
436
+ * The secrets that can be exposed publicly.
437
+ */
438
+ testers: { [name: string]: HotTester };
439
+
440
+ constructor (copy: IHotStaq = {})
441
+ {
442
+ this.api = copy.api || null;
443
+ this.testerAPI = copy.testerAPI || null;
444
+ this.mode = copy.mode || DeveloperMode.Production;
445
+ this.pages = copy.pages || {};
446
+ this.components = copy.components || {};
447
+ this.files = copy.files || {};
448
+ this.hotSite = copy.hotSite || null;
449
+ this.apiContent = `
450
+ var %api_name% = %api_exported_name%.%api_name%;
451
+ var newHotClient = new HotClient (processor);
452
+ var newapi = new %api_name% (%base_url%, newHotClient);
453
+ newHotClient.api = newapi;
454
+ processor.api = newapi;`;
455
+ this.testerApiContent = `
456
+ var HotTesterAPI = HotStaqWeb.HotTesterAPI;
457
+ var newHotTesterClient = new HotClient (processor);
458
+ var newtesterapi = new HotTesterAPI (%base_tester_url%, newHotTesterClient);
459
+ newHotTesterClient.testerAPI = newtesterapi;
460
+ processor.testerAPI = newtesterapi;`;
461
+ this.pageContent =
462
+ `<!DOCTYPE html>
463
+ <html>
464
+
465
+ <head>
466
+ <title>%title%</title>
467
+
468
+ <script type = "text/javascript" src = "%hotstaq_js_src%"></script>
469
+ <script type = "text/javascript">
470
+ window.HotStaq = HotStaqWeb.HotStaq;
471
+ window.HotClient = HotStaqWeb.HotClient;
472
+ window.HotAPI = HotStaqWeb.HotAPI;
473
+ window.Hot = HotStaqWeb.Hot;
474
+ </script>
475
+
476
+ %apis_to_load%
477
+
478
+ <script type = "text/javascript">
479
+ function hotstaq_startApp ()
480
+ {
481
+ let tempMode = 0;
482
+
483
+ if (window["Hot"] != null)
484
+ tempMode = Hot.Mode;
485
+
486
+ %load_hot_site%
487
+
488
+ var processor = new HotStaq ();
489
+ var promises = [];
490
+ %developer_mode%
491
+
492
+ %api_code%
493
+
494
+ %public_secrets%
495
+ %tester_api%
496
+ %load_files%
497
+
498
+ processor.mode = tempMode;
499
+
500
+ Promise.all (promises).then (function ()
501
+ {
502
+ HotStaq.displayUrl ({
503
+ url: "%url%",
504
+ name: "%title%",
505
+ processor: processor,
506
+ args: %args%,
507
+ testerName: %tester_name%,
508
+ testerMap: %tester_map%,
509
+ testerAPIBaseUrl: %tester_api_base_url%,
510
+ testerLaunchpadUrl: %tester_launchpad_url%
511
+ });
512
+ });
513
+ }
514
+
515
+ hotstaq_startApp ();
516
+ </script>
517
+ </head>
518
+
519
+ <body>
520
+ </body>
521
+
522
+ </html>`;
523
+ this.logger = new HotLog (HotLogLevel.None);
524
+ this.publicSecrets = {};
525
+ this.testers = {};
526
+ }
527
+
528
+ /**
529
+ * Parse a boolean value.
530
+ */
531
+ static parseBoolean (value: string): boolean
532
+ {
533
+ value = value.toLowerCase ();
534
+
535
+ if (value === "true")
536
+ return (true);
537
+
538
+ if (value === "false")
539
+ return (false);
540
+
541
+ if (value === "yes")
542
+ return (true);
543
+
544
+ if (value === "no")
545
+ return (false);
546
+
547
+ if (value === "yep")
548
+ return (true);
549
+
550
+ if (value === "nah")
551
+ return (false);
552
+
553
+ try
554
+ {
555
+ if (parseInt (value) != 0)
556
+ return (true);
557
+ }
558
+ catch (ex)
559
+ {
560
+ }
561
+
562
+ return (false);
563
+ }
564
+
565
+ /**
566
+ * Check if a required parameter exists inside an object. If it exists, return the value.
567
+ */
568
+ static getParam (name: string, objWithParam: any, required: boolean = true, throwException: boolean = true): any
569
+ {
570
+ let value: any = objWithParam[name];
571
+
572
+ if (value == null)
573
+ {
574
+ if (required === true)
575
+ {
576
+ if (throwException === true)
577
+ throw new Error (`Missing required parameter ${name}.`);
578
+ }
579
+ }
580
+
581
+ if (typeof (value) === "string")
582
+ {
583
+ if (required === true)
584
+ {
585
+ if (value === "")
586
+ {
587
+ if (throwException === true)
588
+ throw new Error (`Missing required parameter ${name}.`);
589
+ }
590
+ }
591
+ }
592
+
593
+ return (value);
594
+ }
595
+
596
+ /**
597
+ * Check if a required parameter exists inside an object. If it exists, return the value.
598
+ * If it does not exist, return a default value instead.
599
+ */
600
+ static getParamDefault (name: string, objWithParam: any, defaultValue: any): any
601
+ {
602
+ let value: any = objWithParam[name];
603
+
604
+ if (value == null)
605
+ return (defaultValue);
606
+
607
+ if (typeof (value) === "string")
608
+ {
609
+ if (value === "")
610
+ return (defaultValue);
611
+ }
612
+
613
+ return (value);
614
+ }
615
+
616
+ /**
617
+ * Wait for a number of milliseconds.
618
+ */
619
+ static async wait (numMilliseconds: number): Promise<void>
620
+ {
621
+ return (await new Promise ((resolve, reject) =>
622
+ {
623
+ setTimeout (() =>
624
+ {
625
+ resolve ();
626
+ }, numMilliseconds);
627
+ }));
628
+ }
629
+
630
+ /**
631
+ * Add a page.
632
+ */
633
+ addPage (page: HotPage): void
634
+ {
635
+ this.pages[page.name] = page;
636
+ }
637
+
638
+ /**
639
+ * Get a page to process.
640
+ */
641
+ getPage (pageName: string): HotPage
642
+ {
643
+ return (this.pages[pageName]);
644
+ }
645
+
646
+ /**
647
+ * Add a file.
648
+ */
649
+ addFile (file: HotFile): void
650
+ {
651
+ let name: string = file.name;
652
+
653
+ if (name === "")
654
+ name = file.localFile;
655
+
656
+ if (name === "")
657
+ name = file.url;
658
+
659
+ this.files[name] = file;
660
+ }
661
+
662
+ /**
663
+ * Get a file.
664
+ */
665
+ getFile (name: string): HotFile
666
+ {
667
+ if (this.files[name] == null)
668
+ throw new Error (`Unable to find file ${name}`);
669
+
670
+ return (this.files[name]);
671
+ }
672
+
673
+ /**
674
+ * Add and register a component.
675
+ */
676
+ addComponent (component: HotComponent): void
677
+ {
678
+ this.components[component.name] = component;
679
+ this.registerComponent (component);
680
+ }
681
+
682
+ /**
683
+ * Register a component for use as a HTML tag.
684
+ */
685
+ registerComponent (component: HotComponent): void
686
+ {
687
+ customElements.define (component.tag, class extends HTMLElement
688
+ {
689
+ constructor ()
690
+ {
691
+ super ();
692
+
693
+ /// @fixme Is this bad? Could create race conditions.
694
+ (async () =>
695
+ {
696
+ this.onclick = component.click.bind (component);
697
+
698
+ for (let key in component.events)
699
+ {
700
+ let event = component.events[key];
701
+
702
+ // @ts-ignore
703
+ this.addEventListener (event.type, event.func, event.options);
704
+ }
705
+
706
+ component.htmlElement = await component.onCreated (this);
707
+
708
+ if (component.handleAttributes != null)
709
+ await component.handleAttributes (this.attributes);
710
+ else
711
+ {
712
+ for (let iIdx = 0; iIdx < this.attributes.length; iIdx++)
713
+ {
714
+ let attr: Attr = this.attributes[iIdx];
715
+ let attrName: string = attr.name.toLowerCase ();
716
+ let attrValue: string = attr.value;
717
+
718
+ if (attrName === "id")
719
+ component.name = attrValue;
720
+
721
+ if (attrName === "name")
722
+ component.name = attrValue;
723
+
724
+ if (attrName === "value")
725
+ component.value = attrValue;
726
+ }
727
+ }
728
+
729
+ let str: string = await component.output ();
730
+ let newDOM: Document = new DOMParser ().parseFromString (str, "text/html");
731
+ let shadow: ShadowRoot = this.attachShadow ({ mode: "open" });
732
+
733
+ for (let iIdx = 0; iIdx < newDOM.body.children.length; iIdx++)
734
+ {
735
+ let child = newDOM.body.children[iIdx];
736
+ shadow.appendChild (child);
737
+ }
738
+ })();
739
+ }
740
+ }, component.elementOptions);
741
+ }
742
+
743
+ /**
744
+ * Get a component to process.
745
+ */
746
+ getComponent (name: string): HotComponent
747
+ {
748
+ return (this.components[name]);
749
+ }
750
+
751
+ /**
752
+ * Add a new HTML element(s) to the current document.
753
+ */
754
+ static addHtml (parent: string | HTMLElement, html: string | HTMLElement): HTMLElement | HTMLElement[]
755
+ {
756
+ let foundParent: HTMLElement = null;
757
+
758
+ if (typeof (parent) === "string")
759
+ foundParent = document.querySelector (parent);
760
+ else
761
+ foundParent = parent;
762
+
763
+ if (foundParent == null)
764
+ throw new Error (`Unable to find parent ${parent}!`);
765
+
766
+ let result: HTMLElement = null;
767
+
768
+ if (typeof (html) === "string")
769
+ {
770
+ let newDOM: Document = new DOMParser ().parseFromString (html, "text/html");
771
+ let results: HTMLElement[] = [];
772
+
773
+ for (let iIdx = 0; iIdx < newDOM.body.children.length; iIdx++)
774
+ {
775
+ let child: HTMLElement = (<HTMLElement>newDOM.body.children[iIdx]);
776
+
777
+ results.push (foundParent.appendChild (child));
778
+ }
779
+
780
+ return (results);
781
+ }
782
+ else
783
+ result = foundParent.appendChild (html);
784
+
785
+ return (result);
786
+ }
787
+
788
+ /**
789
+ * Check if a HotSite's name is valid.
790
+ */
791
+ static checkHotSiteName (hotsiteName: string, throwException: boolean = false): boolean
792
+ {
793
+ let throwTheException = () =>
794
+ {
795
+ if (throwException === true)
796
+ throw new Error (`HotSite ${hotsiteName} has an invalid name! The name cannot be empty and must have a valid NPM module name.`);
797
+ };
798
+
799
+ let results = validateModuleName (hotsiteName);
800
+
801
+ if (results.errors != null)
802
+ {
803
+ if (results.errors.length > 0)
804
+ throwTheException ();
805
+ }
806
+
807
+ return (true);
808
+ }
809
+
810
+ /**
811
+ * In the supplied content, replace a key in a ${KEY} with a value.
812
+ *
813
+ * @returns The content with the correct values.
814
+ */
815
+ static replaceKey (content: string, key: string, value: string): string
816
+ {
817
+ const finalStr: string = content.replace (new RegExp (`\\$\\{${key}\\}`, "g"), value);
818
+
819
+ return (finalStr);
820
+ }
821
+
822
+ /**
823
+ * Get a value from a HotSite object.
824
+ *
825
+ * @returns Returns the value from the hotsite object. Returns null if it doesn't exist.
826
+ */
827
+ static getValueFromHotSiteObj (hotsite: HotSite, params: string[]): any
828
+ {
829
+ let value: any = null;
830
+
831
+ if (hotsite != null)
832
+ {
833
+ let prevValue: any = hotsite;
834
+
835
+ // Go through each object in the list of parameters and
836
+ // get the value of the final parameter.
837
+ for (let iIdx = 0; iIdx < params.length; iIdx++)
838
+ {
839
+ let param: string = params[iIdx];
840
+
841
+ if (prevValue[param] == null)
842
+ {
843
+ prevValue = null;
844
+
845
+ break;
846
+ }
847
+
848
+ prevValue = prevValue[param];
849
+ }
850
+
851
+ if (prevValue != null)
852
+ value = prevValue;
853
+ }
854
+
855
+ return (value);
856
+ }
857
+
858
+ /**
859
+ * Load from a HotSite.json file. Be sure to load and attach any testers before
860
+ * loading a HotSite.
861
+ */
862
+ async loadHotSite (path: string): Promise<void>
863
+ {
864
+ let jsonStr: string = "";
865
+
866
+ if (HotStaq.isWeb === true)
867
+ {
868
+ this.logger.info (`Retrieving HotSite ${path}`);
869
+
870
+ let res: any = await fetch (path);
871
+
872
+ this.logger.info (`Retrieved site ${path}`);
873
+
874
+ jsonStr = res.text ();
875
+ }
876
+ else
877
+ {
878
+ path = ppath.normalize (path);
879
+
880
+ this.logger.info (`Retrieving HotSite ${path}`);
881
+
882
+ jsonStr = await new Promise (
883
+ (resolve: any, reject: any): void =>
884
+ {
885
+ fs.readFile (path, (err: NodeJS.ErrnoException, data: Buffer): void =>
886
+ {
887
+ if (err != null)
888
+ throw err;
889
+
890
+ let content: string = data.toString ();
891
+
892
+ this.logger.info (`Retrieved site ${path}`);
893
+
894
+ resolve (content);
895
+ });
896
+ });
897
+ }
898
+
899
+ this.hotSite = JSON.parse (jsonStr);
900
+
901
+ HotStaq.checkHotSiteName (this.hotSite.name, true);
902
+
903
+ this.hotSite.hotsitePath = path;
904
+ let routes = this.hotSite.routes;
905
+ let testerUrl: string = "http://127.0.0.1:8182";
906
+ let tester: HotTester = null;
907
+ let driver: HotTestDriver = null;
908
+
909
+ if (HotStaq.isWeb === false)
910
+ {
911
+ if (this.mode === DeveloperMode.Development)
912
+ {
913
+ if (this.hotSite.testing != null)
914
+ {
915
+ let setupTester = (parentObj: any) =>
916
+ {
917
+ let createNewTester: boolean = true;
918
+
919
+ if (parentObj.createNewTester != null)
920
+ createNewTester = parentObj.createNewTester;
921
+
922
+ let testerName: string = "Tester";
923
+
924
+ if (parentObj.tester != null)
925
+ testerName = parentObj.tester;
926
+
927
+ if (parentObj.testerName != null)
928
+ testerName = parentObj.testerName;
929
+
930
+ if (createNewTester === true)
931
+ {
932
+ /// @fixme Find a way to securely allow devs to use their own drivers and testers...
933
+ /// @fixme Hack for dealing with WebPack's bs.
934
+ HotTesterMocha = require ("./HotTesterMocha").HotTesterMocha;
935
+ HotTesterMochaSelenium = require ("./HotTesterMochaSelenium").HotTesterMochaSelenium;
936
+ HotTestSeleniumDriver = require ("./HotTestSeleniumDriver").HotTestSeleniumDriver;
937
+
938
+ if (parentObj.testerAPIUrl === "")
939
+ testerUrl = parentObj.testerAPIUrl;
940
+
941
+ if (parentObj.driver === "HotTestSeleniumDriver")
942
+ driver = new HotTestSeleniumDriver ();
943
+
944
+ if (parentObj.tester === "HotTesterMocha")
945
+ tester = new HotTesterMocha (this, testerName, testerUrl, driver);
946
+
947
+ if (parentObj.tester === "HotTesterMochaSelenium")
948
+ tester = new HotTesterMochaSelenium (this, testerName, testerUrl);
949
+ }
950
+ else
951
+ tester = this.testers[testerName];
952
+ };
953
+
954
+ if (this.hotSite.testing.web != null)
955
+ setupTester (this.hotSite.testing.web);
956
+
957
+ if (this.hotSite.testing.api != null)
958
+ setupTester (this.hotSite.testing.api);
959
+ }
960
+ }
961
+ }
962
+
963
+ if (routes != null)
964
+ {
965
+ for (let key in routes)
966
+ {
967
+ let route: HotSiteRoute = routes[key];
968
+ let file: HotFile = new HotFile (route);
969
+ let page: HotPage = new HotPage ({
970
+ processor: this,
971
+ name: route.name || "",
972
+ route: key,
973
+ files: [file]
974
+ });
975
+
976
+ if (tester != null)
977
+ {
978
+ if (this.mode === DeveloperMode.Development)
979
+ {
980
+ let mapName: string = route.name;
981
+ let testMap: HotTestMap = null;
982
+
983
+ if (route.map != null)
984
+ {
985
+ if (typeof (route.map) === "string")
986
+ {
987
+ if (tester.testMaps[route.map] == null)
988
+ throw new Error (`Test map ${route.map} does not exist!`);
989
+
990
+ tester.testMaps[mapName] = tester.testMaps[route.map];
991
+ }
992
+ else
993
+ {
994
+ testMap = new HotTestMap ();
995
+ let destinations: HotTestDestination[] | { [name: string]: HotTestDestination } = null;
996
+
997
+ if (route.map instanceof Array)
998
+ {
999
+ destinations = [];
1000
+
1001
+ for (let iIdx = 0; iIdx < route.map.length; iIdx++)
1002
+ {
1003
+ let dest = route.map[iIdx];
1004
+
1005
+ destinations.push (new HotTestDestination (dest));
1006
+ }
1007
+ }
1008
+ else
1009
+ {
1010
+ destinations = {};
1011
+
1012
+ for (let key2 in route.map)
1013
+ {
1014
+ let dest = route.map[key2];
1015
+
1016
+ destinations[key2] = new HotTestDestination (dest);
1017
+ }
1018
+ }
1019
+
1020
+ testMap.destinations = destinations;
1021
+ }
1022
+
1023
+ tester.testMaps[mapName] = testMap;
1024
+ }
1025
+
1026
+ if (route.destinationOrder != null)
1027
+ tester.testMaps[mapName].destinationOrder = route.destinationOrder;
1028
+ }
1029
+ }
1030
+
1031
+ this.addPage (page);
1032
+ }
1033
+ }
1034
+
1035
+ if (this.hotSite.apis != null)
1036
+ {
1037
+ for (let key in this.hotSite.apis)
1038
+ {
1039
+ let api = this.hotSite.apis[key];
1040
+
1041
+ if (api.map == null)
1042
+ continue;
1043
+
1044
+ if (HotStaq.isWeb === false)
1045
+ {
1046
+ if (this.mode === DeveloperMode.Development)
1047
+ {
1048
+ let mapName: string = key;
1049
+ let testMap: HotTestMap = new HotTestMap ();
1050
+
1051
+ testMap.destinations = [];
1052
+
1053
+ for (let iIdx = 0; iIdx < api.map.length; iIdx++)
1054
+ {
1055
+ let map: string = api.map[iIdx];
1056
+
1057
+ testMap.destinations.push (new HotTestDestination (map));
1058
+ }
1059
+
1060
+ if (tester == null)
1061
+ throw new Error (`A tester was not created first! You must specify one in the CLI or in HotSite.json.`);
1062
+
1063
+ tester.testMaps[mapName] = testMap;
1064
+ }
1065
+ }
1066
+ }
1067
+ }
1068
+
1069
+ /// @fixme Allow this to work for server-side as well...
1070
+ if (HotStaq.isWeb === true)
1071
+ {
1072
+ for (let key in this.hotSite.components)
1073
+ {
1074
+ let component = this.hotSite.components[key];
1075
+ let componentUrl: string = component.url;
1076
+
1077
+ /// @fixme Create unit test for fetching, loading, and registering.
1078
+ let res: any = await fetch (componentUrl);
1079
+ let newComponent: HotComponent = eval (res);
1080
+
1081
+ this.addComponent (newComponent);
1082
+ }
1083
+ }
1084
+
1085
+ if (this.hotSite.routes == null)
1086
+ this.hotSite.routes = {};
1087
+
1088
+ await this.loadHotFiles (this.hotSite.files);
1089
+
1090
+ if (tester != null)
1091
+ this.addTester (tester);
1092
+ }
1093
+
1094
+ /**
1095
+ * Load an array of files. If a file already has content, it will not be reloaded
1096
+ * unless forceContentLoading is set to true.
1097
+ */
1098
+ async loadHotFiles (files: { [name: string]: { url?: string; localFile?: string; content?: string; } },
1099
+ forceContentLoading: boolean = false): Promise<void>
1100
+ {
1101
+ for (let key in files)
1102
+ {
1103
+ let file = files[key];
1104
+ let newFile: HotFile = null;
1105
+
1106
+ if (HotStaq.isWeb === true)
1107
+ {
1108
+ newFile = new HotFile ({
1109
+ "name": key
1110
+ });
1111
+ }
1112
+ else
1113
+ {
1114
+ newFile = new HotFile ({
1115
+ "name": key
1116
+ });
1117
+ }
1118
+
1119
+ if (file.url != null)
1120
+ newFile.url = file.url;
1121
+
1122
+ if (HotStaq.isWeb === false)
1123
+ {
1124
+ if (file.localFile != null)
1125
+ newFile.localFile = file.localFile;
1126
+ }
1127
+
1128
+ let loadContent: boolean = true;
1129
+
1130
+ if (file.content != null)
1131
+ {
1132
+ newFile.content = file.content;
1133
+ loadContent = false;
1134
+ }
1135
+
1136
+ if (forceContentLoading === true)
1137
+ loadContent = true;
1138
+
1139
+ if (loadContent === true)
1140
+ await newFile.load ();
1141
+
1142
+ this.addFile (newFile);
1143
+ }
1144
+ }
1145
+
1146
+ /**
1147
+ * Generate the content to send to a client.
1148
+ */
1149
+ generateContent (routeKey: string, name: string = "", url: string = "./",
1150
+ jsSrcPath: string = "./js/HotStaq.js", passArgs: boolean = true,
1151
+ args: any = null): string
1152
+ {
1153
+ let apiScripts: string = "";
1154
+ let apiCode: string = "";
1155
+ let publicSecrets: string = "";
1156
+
1157
+ /// @todo Optimize this function as much as possible.
1158
+
1159
+ // Load the API string.
1160
+ if (this.hotSite != null)
1161
+ {
1162
+ if (this.hotSite.server.globalApi != null)
1163
+ {
1164
+ if (this.hotSite.server.globalApi !== "")
1165
+ {
1166
+ const globalApi = this.hotSite.apis[this.hotSite.server.globalApi];
1167
+
1168
+ if (globalApi == null)
1169
+ this.logger.warning (`API with name ${this.hotSite.server.globalApi} doesn't exist!`);
1170
+ else
1171
+ {
1172
+ let sendJSContent: boolean = true;
1173
+
1174
+ if (globalApi.jsapi == null)
1175
+ {
1176
+ sendJSContent = false;
1177
+ this.logger.warning (`API with name ${this.hotSite.server.globalApi} doesn't have a jsapi set. Will not send js content to client.`);
1178
+ }
1179
+
1180
+ if (globalApi.libraryName == null)
1181
+ {
1182
+ sendJSContent = false;
1183
+ this.logger.warning (`API with name ${this.hotSite.server.globalApi} doesn't have a libraryName set. Will not send js content to client.`);
1184
+ }
1185
+
1186
+ if (globalApi.apiName == null)
1187
+ {
1188
+ sendJSContent = false;
1189
+ this.logger.warning (`API with name ${this.hotSite.server.globalApi} doesn't have a apiName set. Will not send js content to client.`);
1190
+ }
1191
+
1192
+ if (sendJSContent === true)
1193
+ {
1194
+ apiScripts += `\t<script type = "text/javascript" src = "${globalApi.jsapi}"></script>\n`;
1195
+
1196
+ let baseUrl: string = "\"\"";
1197
+
1198
+ if (this.api != null)
1199
+ baseUrl = `\"${this.api.baseUrl}\"`;
1200
+
1201
+ let tempAPIContent: string = this.apiContent;
1202
+ tempAPIContent = tempAPIContent.replace (/\%api\_name\%/g, globalApi.apiName);
1203
+ tempAPIContent = tempAPIContent.replace (/\%api\_exported\_name\%/g, globalApi.libraryName);
1204
+ tempAPIContent = tempAPIContent.replace (/\%base\_url\%/g, baseUrl);
1205
+
1206
+ apiCode += tempAPIContent;
1207
+ }
1208
+ }
1209
+ }
1210
+ }
1211
+
1212
+ if (this.hotSite.apis != null)
1213
+ {
1214
+ let route = this.hotSite.routes[routeKey];
1215
+
1216
+ if (route != null)
1217
+ {
1218
+ if (route.api != null)
1219
+ {
1220
+ let api = this.hotSite.apis[route.api];
1221
+
1222
+ if (api == null)
1223
+ throw new Error (`Unable to find API ${route.api}`);
1224
+
1225
+ let sendJSContent: boolean = true;
1226
+
1227
+ if (api.jsapi == null)
1228
+ {
1229
+ sendJSContent = false;
1230
+ this.logger.warning (`API with name ${route.api} doesn't have a jsapi set. Will not send js content to client.`);
1231
+ }
1232
+
1233
+ if (api.libraryName == null)
1234
+ {
1235
+ sendJSContent = false;
1236
+ this.logger.warning (`API with name ${route.api} doesn't have a libraryName set. Will not send js content to client.`);
1237
+ }
1238
+
1239
+ if (api.apiName == null)
1240
+ {
1241
+ sendJSContent = false;
1242
+ this.logger.warning (`API with name ${route.api} doesn't have a apiName set. Will not send js content to client.`);
1243
+ }
1244
+
1245
+ if (sendJSContent === true)
1246
+ {
1247
+ let jsapipath = api.jsapi;
1248
+ apiScripts += `\t<script type = "text/javascript" src = "${jsapipath}"></script>\n`;
1249
+
1250
+ let baseUrl: string = "\"\"";
1251
+
1252
+ if (this.api != null)
1253
+ baseUrl = `\"${this.api.baseUrl}\"`;
1254
+
1255
+ let tempAPIContent: string = this.apiContent;
1256
+ tempAPIContent = tempAPIContent.replace (/\%api\_name\%/g, api.apiName);
1257
+ tempAPIContent = tempAPIContent.replace (/\%api\_exported\_name\%/g, api.libraryName);
1258
+ tempAPIContent = tempAPIContent.replace (/\%base\_url\%/g, baseUrl);
1259
+
1260
+ apiCode += tempAPIContent;
1261
+ }
1262
+ }
1263
+ }
1264
+ }
1265
+
1266
+ if (this.hotSite.server != null)
1267
+ {
1268
+ if (this.hotSite.server.jsSrcPath != null)
1269
+ jsSrcPath = this.hotSite.server.jsSrcPath;
1270
+ }
1271
+
1272
+ if (this.hotSite.publicSecrets != null)
1273
+ {
1274
+ for (let key in this.hotSite.publicSecrets)
1275
+ {
1276
+ let secret = this.hotSite.publicSecrets[key];
1277
+ let value: string = undefined;
1278
+
1279
+ if (typeof (secret) === "string")
1280
+ value = JSON.stringify (secret);
1281
+ else
1282
+ {
1283
+ if (HotStaq.isWeb === false)
1284
+ {
1285
+ if (this.api != null)
1286
+ {
1287
+ if (this.api.connection == null)
1288
+ throw new Error (`Cannot pass secrets from the API if there's no connection!`);
1289
+
1290
+ let serverConn: HotServer = (<HotServer>this.api.connection);
1291
+
1292
+ if (secret.passSecretFromAPI != null)
1293
+ value = JSON.stringify (serverConn.secrets[key]);
1294
+ }
1295
+
1296
+ if (secret.env != null)
1297
+ {
1298
+ /// @fixme @secvul Is this a security vulnerability? Need to verify that
1299
+ /// only the server has access to this. At this point, I think only the
1300
+ /// server has access.
1301
+ const envKey: string = secret.env;
1302
+
1303
+ value = JSON.stringify (process.env[envKey]);
1304
+ }
1305
+ }
1306
+ }
1307
+
1308
+ publicSecrets += `processor.publicSecrets["${key}"] = ${value};\n`;
1309
+ }
1310
+ }
1311
+ }
1312
+
1313
+ let content: string = this.pageContent;
1314
+ let fixContent = (tempContent: string) =>
1315
+ {
1316
+ let developerModeStr: string = "";
1317
+ let testerAPIStr: string = "";
1318
+
1319
+ if (this.mode === DeveloperMode.Development)
1320
+ {
1321
+ developerModeStr = `tempMode = HotStaqWeb.DeveloperMode.Development;`;
1322
+ testerAPIStr = this.testerApiContent;
1323
+
1324
+ if (this.hotSite != null)
1325
+ {
1326
+ if (this.hotSite.testing != null)
1327
+ {
1328
+ if (this.hotSite.testing.web.testerAPIUrl == null)
1329
+ this.hotSite.testing.web.testerAPIUrl = "http://127.0.0.1:8182";
1330
+
1331
+ testerAPIStr = testerAPIStr.replace (/\%base\_tester\_url\%/g, `\"${this.hotSite.testing.web.testerAPIUrl}\"`);
1332
+ }
1333
+ }
1334
+ }
1335
+
1336
+ let loadFiles: string = "";
1337
+
1338
+ if (Object.keys (this.files).length > 0)
1339
+ {
1340
+ loadFiles += `var files = {};\n\n`;
1341
+
1342
+ for (let key in this.files)
1343
+ {
1344
+ let file = this.files[key];
1345
+ let fileUrl: string = `"${file.url}"`;
1346
+ let fileContent: string = "";
1347
+
1348
+ if (file.content !== "")
1349
+ {
1350
+ let escapedContent: string = JSON.stringify (file.content);
1351
+
1352
+ // Find any script tags and interrupt them so the HTML parsers
1353
+ // don't get confused.
1354
+ escapedContent = escapedContent.replace (new RegExp ("\\<script", "gmi"), "<scr\" + \"ipt");
1355
+ escapedContent = escapedContent.replace (new RegExp ("\\<\\/script", "gmi"), "</scr\" + \"ipt");
1356
+
1357
+ fileContent = `, "content": ${escapedContent}`;
1358
+ }
1359
+
1360
+ loadFiles += `\t\t\tfiles["${key}"] = { "url": ${fileUrl}${fileContent} };\n`;
1361
+ }
1362
+
1363
+ loadFiles += `\t\t\tpromises.push (processor.loadHotFiles (files));\n`;
1364
+ }
1365
+
1366
+ tempContent = tempContent.replace (/\%title\%/g, name);
1367
+
1368
+ if (passArgs === true)
1369
+ tempContent = tempContent.replace (/\%args\%/g, "Hot.Arguments");
1370
+
1371
+ if (args != null)
1372
+ tempContent = tempContent.replace (/\%args\%/g, JSON.stringify (args));
1373
+
1374
+ let testerMap: string = routeKey;
1375
+ let testerUrl: string = "";
1376
+ let testerLaunchpadUrl: string = "";
1377
+ let testerName: string = "Tester";
1378
+
1379
+ if (this.hotSite != null)
1380
+ {
1381
+ if (this.hotSite.testing != null)
1382
+ {
1383
+ if (this.hotSite.testing.web.tester != null)
1384
+ testerName = this.hotSite.testing.web.tester;
1385
+
1386
+ if (this.hotSite.testing.web.testerName != null)
1387
+ testerName = this.hotSite.testing.web.testerName;
1388
+
1389
+ if (this.hotSite.testing.web.testerAPIUrl != null)
1390
+ testerUrl = this.hotSite.testing.web.testerAPIUrl;
1391
+
1392
+ if (this.hotSite.testing.web.launchpadUrl != null)
1393
+ testerLaunchpadUrl = this.hotSite.testing.web.launchpadUrl;
1394
+ }
1395
+
1396
+ if (this.hotSite.routes != null)
1397
+ {
1398
+ if (this.hotSite.routes[routeKey] != null)
1399
+ {
1400
+ let route = this.hotSite.routes[routeKey];
1401
+ testerMap = route.name;
1402
+ }
1403
+ }
1404
+ }
1405
+
1406
+ tempContent = tempContent.replace (/\%hotstaq\_js\_src\%/g, jsSrcPath);
1407
+ tempContent = tempContent.replace (/\%developer\_mode\%/g, developerModeStr);
1408
+ tempContent = tempContent.replace (/\%tester\_api\%/g, testerAPIStr);
1409
+ tempContent = tempContent.replace (/\%apis\_to\_load\%/g, apiScripts);
1410
+ tempContent = tempContent.replace (/\%load\_hot\_site\%/g, ""); /// @fixme Should this only be done server-side?
1411
+ tempContent = tempContent.replace (/\%load\_files\%/g, loadFiles);
1412
+ tempContent = tempContent.replace (/\%api\_code\%/g, apiCode);
1413
+ tempContent = tempContent.replace (/\%public\_secrets\%/g, publicSecrets);
1414
+ tempContent = tempContent.replace (/\%url\%/g, url);
1415
+ tempContent = tempContent.replace (/\%tester\_name\%/g, `"${testerName}"`);
1416
+ tempContent = tempContent.replace (/\%tester\_map\%/g, `"${testerMap}"`);
1417
+ tempContent = tempContent.replace (/\%tester\_api\_base\_url\%/g, `"${testerUrl}"`);
1418
+ tempContent = tempContent.replace (/\%tester\_launchpad\_url\%/g, `"${testerLaunchpadUrl}"`);
1419
+
1420
+ return (tempContent);
1421
+ };
1422
+ content = fixContent (content);
1423
+
1424
+ return (content);
1425
+ }
1426
+
1427
+ /**
1428
+ * Create the Express routes from the given pages. Be sure to load the
1429
+ * pages first before doing this. This method is meant to be used for
1430
+ * customized Express applications. If you wish to use the loaded routes
1431
+ * from this HotStaq object with HotHTTPServer, be sure to use
1432
+ * the loadHotSite method in HotHTTPServer.
1433
+ */
1434
+ createExpressRoutes (expressApp: any, jsSrcPath: string = "./js/HotStaq.js"): void
1435
+ {
1436
+ for (let key in this.pages)
1437
+ {
1438
+ let page: HotPage = this.pages[key];
1439
+ const content: string = this.generateContent (page.route, page.name, page.files[0].url, jsSrcPath);
1440
+
1441
+ expressApp.get (page.route, (req: any, res: any) =>
1442
+ {
1443
+ res.send (content);
1444
+ });
1445
+ }
1446
+ }
1447
+
1448
+ /**
1449
+ * Add a tester for use later.
1450
+ */
1451
+ addTester (tester: HotTester): void
1452
+ {
1453
+ this.testers[tester.name] = tester;
1454
+ }
1455
+
1456
+ /**
1457
+ * Get the list of maps for testing from the HotSite.
1458
+ */
1459
+ getWebTestingMaps (): string[]
1460
+ {
1461
+ if (this.hotSite == null)
1462
+ throw new Error ("No HotSite was loaded!");
1463
+
1464
+ if (this.hotSite.testing == null)
1465
+ throw new Error ("The HotSite does not have a testing object!");
1466
+
1467
+ if (this.hotSite.testing.web == null)
1468
+ throw new Error ("The HotSite does not have a testing web object!");
1469
+
1470
+ if (this.hotSite.testing.web.maps == null)
1471
+ throw new Error ("The HotSite testing object does not have any maps!");
1472
+
1473
+ return (this.hotSite.testing.web.maps);
1474
+ }
1475
+
1476
+ /**
1477
+ * Get the list of maps for testing from the HotSite.
1478
+ */
1479
+ getAPITestingMaps (): string[]
1480
+ {
1481
+ if (this.hotSite == null)
1482
+ throw new Error ("No HotSite was loaded!");
1483
+
1484
+ if (this.hotSite.testing == null)
1485
+ throw new Error ("The HotSite does not have a testing object!");
1486
+
1487
+ if (this.hotSite.testing.api == null)
1488
+ throw new Error ("The HotSite does not have a testing api object!");
1489
+
1490
+ if (this.hotSite.testing.api.maps == null)
1491
+ throw new Error ("The HotSite testing object does not have any maps!");
1492
+
1493
+ return (this.hotSite.testing.api.maps);
1494
+ }
1495
+
1496
+ /**
1497
+ * Get a route's key from a route's name.
1498
+ */
1499
+ getRouteKeyFromName (name: string): string
1500
+ {
1501
+ let foundKey: string = "";
1502
+
1503
+ if (this.hotSite != null)
1504
+ {
1505
+ if (this.hotSite.routes != null)
1506
+ {
1507
+ for (let key in this.hotSite.routes)
1508
+ {
1509
+ let route: HotSiteRoute = this.hotSite.routes[key];
1510
+
1511
+ if (route.name === name)
1512
+ {
1513
+ foundKey = key;
1514
+
1515
+ break;
1516
+ }
1517
+ }
1518
+ }
1519
+ }
1520
+
1521
+ return (foundKey);
1522
+ }
1523
+
1524
+ /**
1525
+ * Get a route from a route's name.
1526
+ */
1527
+ getRouteFromName (name: string): HotSiteRoute
1528
+ {
1529
+ let foundRoute: HotSiteRoute = null;
1530
+ let foundKey: string = this.getRouteKeyFromName (name);
1531
+
1532
+ if (foundKey !== "")
1533
+ foundRoute = this.hotSite.routes[foundKey];
1534
+
1535
+ return (foundRoute);
1536
+ }
1537
+
1538
+ /**
1539
+ * Execute tests.
1540
+ *
1541
+ * @param testerName The tester to use to execute tests.
1542
+ * @param mapName The map or maps to use to navigate through tests.
1543
+ */
1544
+ async executeTests (testerName: string, mapName: string): Promise<void>
1545
+ {
1546
+ let tester: HotTester = this.testers[testerName];
1547
+
1548
+ if (tester == null)
1549
+ throw new Error (`Unable to execute tests. Tester ${testerName} does not exist!`);
1550
+
1551
+ return (tester.execute (mapName));
1552
+ }
1553
+
1554
+ /**
1555
+ * Execute all web tests from the HotSite testing web object.
1556
+ *
1557
+ * @param testerName The tester to use to execute tests.
1558
+ */
1559
+ async executeAllWebTests (testerName: string): Promise<void>
1560
+ {
1561
+ let maps: string[] = this.getWebTestingMaps ();
1562
+ let tester: HotTester = this.testers[testerName];
1563
+
1564
+ if (tester == null)
1565
+ throw new Error (`Unable to execute tests. Tester ${testerName} does not exist!`);
1566
+
1567
+ for (let iIdx = 0; iIdx < maps.length; iIdx++)
1568
+ {
1569
+ let mapName: string = maps[iIdx];
1570
+
1571
+ await this.executeTests (testerName, mapName);
1572
+ }
1573
+ }
1574
+
1575
+ /**
1576
+ * Execute all api tests from the HotSite testing api object.
1577
+ *
1578
+ * @param testerName The tester to use to execute tests.
1579
+ */
1580
+ async executeAllAPITests (testerName: string): Promise<void>
1581
+ {
1582
+ let maps: string[] = this.getAPITestingMaps ();
1583
+ let tester: HotTester = this.testers[testerName];
1584
+
1585
+ if (tester == null)
1586
+ throw new Error (`Unable to execute tests. Tester ${testerName} does not exist!`);
1587
+
1588
+ for (let iIdx = 0; iIdx < maps.length; iIdx++)
1589
+ {
1590
+ let mapName: string = maps[iIdx];
1591
+
1592
+ await this.executeTests (testerName, mapName);
1593
+ }
1594
+ }
1595
+
1596
+ /**
1597
+ * Process a page and get the result.
1598
+ */
1599
+ async process (pageName: string, args: any = null): Promise<string>
1600
+ {
1601
+ let page: HotPage = this.getPage (pageName);
1602
+ let result: string = await page.process (args);
1603
+
1604
+ return (result);
1605
+ }
1606
+
1607
+ /**
1608
+ * Process a local file and get the result.
1609
+ */
1610
+ static async processLocalFile (localFilepath: string, name: string = localFilepath, args: any = null): Promise<string>
1611
+ {
1612
+ let processor: HotStaq = new HotStaq ();
1613
+ let file: HotFile = new HotFile ({
1614
+ "localFile": localFilepath
1615
+ });
1616
+ await file.load ();
1617
+ let page: HotPage = new HotPage ({
1618
+ "processor": processor,
1619
+ "name": name,
1620
+ "files": [file]
1621
+ });
1622
+ processor.addPage (page);
1623
+ let result: string = await processor.process (name, args);
1624
+
1625
+ return (result);
1626
+ }
1627
+
1628
+ /**
1629
+ * Process a url and get the result.
1630
+ */
1631
+ static async processUrl (options: HotStartOptions): Promise<string>
1632
+ {
1633
+ let file: HotFile = new HotFile ({
1634
+ "url": options.url
1635
+ });
1636
+
1637
+ await file.load ();
1638
+ let page: HotPage = new HotPage ({
1639
+ "processor": options.processor,
1640
+ "name": options.name,
1641
+ "files": [file],
1642
+ "testerName": options.testerName,
1643
+ "testerMap": options.testerMap
1644
+ });
1645
+ options.processor.addPage (page);
1646
+ let result: string = await options.processor.process (options.name, options.args);
1647
+
1648
+ return (result);
1649
+ }
1650
+
1651
+ /**
1652
+ * Process content and get the result.
1653
+ */
1654
+ static async processContent (processor: HotStaq,
1655
+ content: string, name: string, args: any = null): Promise<string>
1656
+ {
1657
+ let file: HotFile = new HotFile ({
1658
+ "content": content
1659
+ });
1660
+ await file.load ();
1661
+ let page: HotPage = new HotPage ({
1662
+ "processor": processor,
1663
+ "name": name,
1664
+ "files": [file]
1665
+ });
1666
+ processor.addPage (page);
1667
+ let result: string = await processor.process (name, args);
1668
+
1669
+ return (result);
1670
+ }
1671
+
1672
+ /**
1673
+ * When the window has finished loading, execute the function.
1674
+ * This is meant for web browser use only.
1675
+ */
1676
+ static onReady (readyFunc: () => void): void
1677
+ {
1678
+ if ((document.readyState === "complete") || (document.readyState === "interactive"))
1679
+ readyFunc ();
1680
+ else
1681
+ window.addEventListener ("load", readyFunc);
1682
+ }
1683
+
1684
+ /**
1685
+ * Replace the current HTML page with the output.
1686
+ * This is meant for web browser use only.
1687
+ */
1688
+ static useOutput (output: string): void
1689
+ {
1690
+ document.open ();
1691
+ document.write (output);
1692
+ document.close ();
1693
+ }
1694
+
1695
+ /**
1696
+ * Wait for testers to load.
1697
+ *
1698
+ * @fixme This does not wait for ALL testers to finish loading. Only
1699
+ * the first one.
1700
+ */
1701
+ static async waitForTesters (): Promise<void>
1702
+ {
1703
+ while (HotStaq.isReadyForTesting === false)
1704
+ await HotStaq.wait (10);
1705
+
1706
+ if (HotStaq.onReadyForTesting != null)
1707
+ await HotStaq.onReadyForTesting ();
1708
+ }
1709
+
1710
+ /**
1711
+ * Process and replace the current HTML page with the hott script from the given url.
1712
+ * This is meant for web browser use only.
1713
+ */
1714
+ static async displayUrl (url: string | HotStartOptions, name: string = null,
1715
+ processor: HotStaq = null, args: any = null): Promise<HotStaq>
1716
+ {
1717
+ return (new Promise<HotStaq> ((resolve, reject) =>
1718
+ {
1719
+ HotStaq.onReady (async () =>
1720
+ {
1721
+ let options: HotStartOptions = {
1722
+ "url": ""
1723
+ };
1724
+
1725
+ if (name == null)
1726
+ {
1727
+ if (typeof (url) === "string")
1728
+ options.name = url;
1729
+ else
1730
+ options.name = url.name;
1731
+ }
1732
+ else
1733
+ options.name = name;
1734
+
1735
+ if (options.name === "")
1736
+ {
1737
+ if (typeof (url) === "string")
1738
+ options.name = url;
1739
+ else
1740
+ options.name = url.name;
1741
+ }
1742
+
1743
+ if (typeof (url) === "string")
1744
+ options.url = url;
1745
+ else
1746
+ {
1747
+ options.url = url.url;
1748
+
1749
+ if (processor == null)
1750
+ {
1751
+ if (url.processor != null)
1752
+ processor = url.processor;
1753
+ }
1754
+
1755
+ if (args == null)
1756
+ {
1757
+ if (url.args != null)
1758
+ args = url.args;
1759
+ }
1760
+
1761
+ if (url.testerMap != null)
1762
+ options.testerMap = url.testerMap;
1763
+
1764
+ if (url.testerName != null)
1765
+ options.testerName = url.testerName;
1766
+
1767
+ if (url.testerAPIBaseUrl != null)
1768
+ options.testerAPIBaseUrl = url.testerAPIBaseUrl;
1769
+ }
1770
+
1771
+ if (processor == null)
1772
+ processor = new HotStaq ();
1773
+
1774
+ if (processor.mode === DeveloperMode.Development)
1775
+ {
1776
+ if (processor.testerAPI == null)
1777
+ {
1778
+ if (options.testerAPIBaseUrl == null)
1779
+ options.testerAPIBaseUrl = "";
1780
+
1781
+ if (options.testerAPIBaseUrl === "")
1782
+ options.testerAPIBaseUrl = "http://127.0.0.1:8182";
1783
+
1784
+ let client: HotClient = new HotClient (processor);
1785
+ let testerAPI: HotTesterAPI = new HotTesterAPI (options.testerAPIBaseUrl, client);
1786
+ testerAPI.connection.api = testerAPI;
1787
+ processor.testerAPI = testerAPI;
1788
+ }
1789
+ }
1790
+
1791
+ options.processor = processor;
1792
+ options.args = args;
1793
+
1794
+ let output: string = await HotStaq.processUrl (options);
1795
+
1796
+ if (processor.mode === DeveloperMode.Development)
1797
+ {
1798
+ output +=
1799
+ `<script type = "text/javascript">
1800
+ function hotstaq_isDocumentReady ()
1801
+ {
1802
+ if (window["Hot"] != null)
1803
+ {
1804
+ if (Hot.Mode === HotStaqWeb.DeveloperMode.Development)
1805
+ {
1806
+ let func = function ()
1807
+ {
1808
+ if (Hot.TesterAPI != null)
1809
+ {
1810
+ let testPaths = {};
1811
+ let testElements = JSON.stringify (Hot.CurrentPage.testElements);
1812
+ let testMaps = JSON.stringify (Hot.CurrentPage.testMaps);
1813
+
1814
+ for (let key in Hot.CurrentPage.testPaths)
1815
+ {
1816
+ let testPath = Hot.CurrentPage.testPaths[key];
1817
+
1818
+ testPaths[key] = testPath.toString ();
1819
+ }
1820
+
1821
+ let testPathsStr = JSON.stringify (testPaths);
1822
+
1823
+ Hot.TesterAPI.tester.pageLoaded ({
1824
+ testerName: Hot.CurrentPage.testerName,
1825
+ testerMap: Hot.CurrentPage.testerMap,
1826
+ pageName: Hot.CurrentPage.name,
1827
+ testElements: testElements,
1828
+ testPaths: testPathsStr
1829
+ }).then (function (resp)
1830
+ {
1831
+ if (resp.error != null)
1832
+ {
1833
+ if (resp.error !== "")
1834
+ throw new Error (resp.error);
1835
+ }
1836
+
1837
+ HotStaqWeb.HotStaq.isReadyForTesting = true;
1838
+ });
1839
+ }
1840
+ };
1841
+
1842
+ if ((document.readyState === "complete") || (document.readyState === "interactive"))
1843
+ func ();
1844
+ else
1845
+ document.addEventListener ("DOMContentLoaded", func);
1846
+ }
1847
+ }
1848
+ }
1849
+
1850
+ hotstaq_isDocumentReady ();
1851
+ </script>`;
1852
+ }
1853
+
1854
+ HotStaq.useOutput (output);
1855
+ resolve (processor);
1856
+ });
1857
+ }));
1858
+ }
1859
+
1860
+ /**
1861
+ * Process and replace the current HTML page with the hott script.
1862
+ * This is meant for web browser use only.
1863
+ */
1864
+ static async displayContent (content: string, name: string, processor: HotStaq = null): Promise<HotStaq>
1865
+ {
1866
+ return (new Promise<HotStaq> ((resolve, reject) =>
1867
+ {
1868
+ HotStaq.onReady (async () =>
1869
+ {
1870
+ if (processor == null)
1871
+ processor = new HotStaq ();
1872
+
1873
+ let output: string = await HotStaq.processContent (processor, content, name);
1874
+
1875
+ HotStaq.useOutput (output);
1876
+ resolve (processor);
1877
+ });
1878
+ }));
1879
+ }
1880
+ }
1881
+