@digital-alchemy/hass 24.9.3 → 24.9.5

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 (276) hide show
  1. package/README.md +3 -0
  2. package/dist/dynamic.d.ts +1 -1
  3. package/dist/dynamic.js +7 -2
  4. package/dist/dynamic.js.map +1 -1
  5. package/dist/extensions/area.extension.d.ts +2 -12
  6. package/dist/extensions/area.extension.js +20 -26
  7. package/dist/extensions/area.extension.js.map +1 -1
  8. package/dist/extensions/backup.extension.d.ts +2 -7
  9. package/dist/extensions/backup.extension.js +5 -8
  10. package/dist/extensions/backup.extension.js.map +1 -1
  11. package/dist/extensions/call-proxy.extension.d.ts +1 -1
  12. package/dist/extensions/call-proxy.extension.js +3 -22
  13. package/dist/extensions/call-proxy.extension.js.map +1 -1
  14. package/dist/extensions/config.extension.d.ts +2 -6
  15. package/dist/extensions/config.extension.js +21 -25
  16. package/dist/extensions/config.extension.js.map +1 -1
  17. package/dist/extensions/conversation.extension.d.ts +2 -6
  18. package/dist/extensions/conversation.extension.js +5 -8
  19. package/dist/extensions/conversation.extension.js.map +1 -1
  20. package/dist/extensions/device.extension.d.ts +2 -5
  21. package/dist/extensions/device.extension.js +16 -22
  22. package/dist/extensions/device.extension.js.map +1 -1
  23. package/dist/extensions/entity.extension.d.ts +2 -61
  24. package/dist/extensions/entity.extension.js +42 -83
  25. package/dist/extensions/entity.extension.js.map +1 -1
  26. package/dist/extensions/events.extension.d.ts +3 -11
  27. package/dist/extensions/events.extension.js +8 -11
  28. package/dist/extensions/events.extension.js.map +1 -1
  29. package/dist/extensions/fetch-api.extension.d.ts +12 -4
  30. package/dist/extensions/fetch-api.extension.js +23 -35
  31. package/dist/extensions/fetch-api.extension.js.map +1 -1
  32. package/dist/extensions/floor.extension.d.ts +2 -9
  33. package/dist/extensions/floor.extension.js +17 -23
  34. package/dist/extensions/floor.extension.js.map +1 -1
  35. package/dist/extensions/id-by.extension.js +15 -20
  36. package/dist/extensions/id-by.extension.js.map +1 -1
  37. package/dist/extensions/index.d.ts +1 -0
  38. package/dist/extensions/index.js +16 -18
  39. package/dist/extensions/index.js.map +1 -1
  40. package/dist/extensions/internal.extension.d.ts +18 -0
  41. package/dist/extensions/internal.extension.js +102 -0
  42. package/dist/extensions/internal.extension.js.map +1 -0
  43. package/dist/extensions/label.extension.d.ts +2 -9
  44. package/dist/extensions/label.extension.js +17 -23
  45. package/dist/extensions/label.extension.js.map +1 -1
  46. package/dist/extensions/reference.extension.d.ts +2 -12
  47. package/dist/extensions/reference.extension.js +19 -25
  48. package/dist/extensions/reference.extension.js.map +1 -1
  49. package/dist/extensions/registry.extension.d.ts +2 -7
  50. package/dist/extensions/registry.extension.js +1 -4
  51. package/dist/extensions/registry.extension.js.map +1 -1
  52. package/dist/extensions/websocket-api.extension.d.ts +3 -78
  53. package/dist/extensions/websocket-api.extension.js +82 -165
  54. package/dist/extensions/websocket-api.extension.js.map +1 -1
  55. package/dist/extensions/zone.extension.d.ts +2 -7
  56. package/dist/extensions/zone.extension.js +15 -21
  57. package/dist/extensions/zone.extension.js.map +1 -1
  58. package/dist/hass.module.d.ts +47 -36
  59. package/dist/hass.module.js +70 -70
  60. package/dist/hass.module.js.map +1 -1
  61. package/dist/helpers/backup.helper.js +1 -2
  62. package/dist/helpers/constants.helper.js +15 -18
  63. package/dist/helpers/constants.helper.js.map +1 -1
  64. package/dist/helpers/device.helper.js +2 -5
  65. package/dist/helpers/device.helper.js.map +1 -1
  66. package/dist/helpers/entity-state.helper.d.ts +3 -8
  67. package/dist/helpers/entity-state.helper.js +1 -8
  68. package/dist/helpers/entity-state.helper.js.map +1 -1
  69. package/dist/helpers/features.helper.js +79 -85
  70. package/dist/helpers/features.helper.js.map +1 -1
  71. package/dist/helpers/fetch/calendar.js +1 -2
  72. package/dist/helpers/fetch/configuration.js +2 -5
  73. package/dist/helpers/fetch/configuration.js.map +1 -1
  74. package/dist/helpers/fetch/index.js +5 -8
  75. package/dist/helpers/fetch/index.js.map +1 -1
  76. package/dist/helpers/fetch/server-log.js +1 -2
  77. package/dist/helpers/fetch/server-log.js.map +1 -1
  78. package/dist/helpers/fetch/service-list.js +1 -2
  79. package/dist/helpers/fetch/weather-forecasts.js +1 -2
  80. package/dist/helpers/fetch/weather-forecasts.js.map +1 -1
  81. package/dist/helpers/fetch.helper.d.ts +162 -0
  82. package/dist/helpers/fetch.helper.js +161 -0
  83. package/dist/helpers/fetch.helper.js.map +1 -0
  84. package/dist/helpers/id-by.helper.js +1 -2
  85. package/dist/helpers/index.d.ts +2 -1
  86. package/dist/helpers/index.js +13 -15
  87. package/dist/helpers/index.js.map +1 -1
  88. package/dist/helpers/interfaces.helper.d.ts +228 -0
  89. package/dist/helpers/interfaces.helper.js +10 -0
  90. package/dist/helpers/interfaces.helper.js.map +1 -0
  91. package/dist/helpers/manifest.helper.d.ts +0 -1
  92. package/dist/helpers/manifest.helper.js +0 -1
  93. package/dist/helpers/notify.helper.d.ts +13 -5
  94. package/dist/helpers/notify.helper.js +1 -2
  95. package/dist/helpers/registry.js +7 -10
  96. package/dist/helpers/registry.js.map +1 -1
  97. package/dist/helpers/utility.helper.d.ts +6 -1
  98. package/dist/helpers/utility.helper.js +9 -13
  99. package/dist/helpers/utility.helper.js.map +1 -1
  100. package/dist/helpers/websocket.helper.d.ts +1 -2
  101. package/dist/helpers/websocket.helper.js +1 -2
  102. package/dist/index.js +5 -8
  103. package/dist/index.js.map +1 -1
  104. package/dist/mock_assistant/extensions/area.extension.d.ts +8 -0
  105. package/dist/mock_assistant/extensions/area.extension.js +51 -0
  106. package/dist/mock_assistant/extensions/area.extension.js.map +1 -0
  107. package/dist/mock_assistant/extensions/config.extension.d.ts +14 -0
  108. package/dist/mock_assistant/extensions/config.extension.js +29 -0
  109. package/dist/mock_assistant/extensions/config.extension.js.map +1 -0
  110. package/dist/mock_assistant/extensions/device.extension.d.ts +8 -0
  111. package/dist/mock_assistant/extensions/device.extension.js +33 -0
  112. package/dist/mock_assistant/extensions/device.extension.js.map +1 -0
  113. package/dist/mock_assistant/extensions/entity-registry.extension.d.ts +13 -0
  114. package/dist/mock_assistant/extensions/entity-registry.extension.js +28 -0
  115. package/dist/mock_assistant/extensions/entity-registry.extension.js.map +1 -0
  116. package/dist/mock_assistant/extensions/entity.extension.d.ts +30 -0
  117. package/dist/mock_assistant/extensions/entity.extension.js +77 -0
  118. package/dist/mock_assistant/extensions/entity.extension.js.map +1 -0
  119. package/dist/mock_assistant/extensions/events.extension.d.ts +1 -1
  120. package/dist/mock_assistant/extensions/events.extension.js +3 -6
  121. package/dist/mock_assistant/extensions/events.extension.js.map +1 -1
  122. package/dist/mock_assistant/extensions/fetch.extension.d.ts +1 -0
  123. package/dist/mock_assistant/extensions/fetch.extension.js +4 -0
  124. package/dist/mock_assistant/extensions/fetch.extension.js.map +1 -0
  125. package/dist/mock_assistant/extensions/fixtures.extension.d.ts +1 -1
  126. package/dist/mock_assistant/extensions/fixtures.extension.js +29 -33
  127. package/dist/mock_assistant/extensions/fixtures.extension.js.map +1 -1
  128. package/dist/mock_assistant/extensions/floor.extension.d.ts +8 -0
  129. package/dist/mock_assistant/extensions/floor.extension.js +51 -0
  130. package/dist/mock_assistant/extensions/floor.extension.js.map +1 -0
  131. package/dist/mock_assistant/extensions/index.d.ts +10 -0
  132. package/dist/mock_assistant/extensions/index.js +12 -5
  133. package/dist/mock_assistant/extensions/index.js.map +1 -1
  134. package/dist/mock_assistant/extensions/label.extension.d.ts +8 -0
  135. package/dist/mock_assistant/extensions/label.extension.js +51 -0
  136. package/dist/mock_assistant/extensions/label.extension.js.map +1 -0
  137. package/dist/mock_assistant/extensions/services.extension.d.ts +12 -0
  138. package/dist/mock_assistant/extensions/services.extension.js +20 -0
  139. package/dist/mock_assistant/extensions/services.extension.js.map +1 -0
  140. package/dist/mock_assistant/extensions/websocket-api.extension.d.ts +15 -0
  141. package/dist/mock_assistant/extensions/websocket-api.extension.js +68 -0
  142. package/dist/mock_assistant/extensions/websocket-api.extension.js.map +1 -0
  143. package/dist/mock_assistant/extensions/zone.extension.d.ts +8 -0
  144. package/dist/mock_assistant/extensions/zone.extension.js +51 -0
  145. package/dist/mock_assistant/extensions/zone.extension.js.map +1 -0
  146. package/dist/mock_assistant/helpers/fixtures.js +1 -2
  147. package/dist/mock_assistant/helpers/index.d.ts +0 -1
  148. package/dist/mock_assistant/helpers/index.js +1 -5
  149. package/dist/mock_assistant/helpers/index.js.map +1 -1
  150. package/dist/mock_assistant/index.js +3 -6
  151. package/dist/mock_assistant/index.js.map +1 -1
  152. package/dist/mock_assistant/main.js +11 -15
  153. package/dist/mock_assistant/main.js.map +1 -1
  154. package/dist/mock_assistant/mock-assistant.module.d.ts +156 -3
  155. package/dist/mock_assistant/mock-assistant.module.js +56 -11
  156. package/dist/mock_assistant/mock-assistant.module.js.map +1 -1
  157. package/dist/quickboot.module.js +5 -8
  158. package/dist/quickboot.module.js.map +1 -1
  159. package/dist/testing/area.spec.js +106 -194
  160. package/dist/testing/area.spec.js.map +1 -1
  161. package/dist/testing/backup.spec.js +97 -139
  162. package/dist/testing/backup.spec.js.map +1 -1
  163. package/dist/testing/config.spec.js +79 -153
  164. package/dist/testing/config.spec.js.map +1 -1
  165. package/dist/testing/device.spec.js +35 -69
  166. package/dist/testing/device.spec.js.map +1 -1
  167. package/dist/testing/entity.spec.js +94 -149
  168. package/dist/testing/entity.spec.js.map +1 -1
  169. package/dist/testing/events.spec.js +33 -57
  170. package/dist/testing/events.spec.js.map +1 -1
  171. package/dist/testing/fetch-api.spec.js +242 -427
  172. package/dist/testing/fetch-api.spec.js.map +1 -1
  173. package/dist/testing/fixtures.spec.d.ts +1 -0
  174. package/dist/testing/fixtures.spec.js +150 -0
  175. package/dist/testing/fixtures.spec.js.map +1 -0
  176. package/dist/testing/floor.spec.js +106 -194
  177. package/dist/testing/floor.spec.js.map +1 -1
  178. package/dist/testing/id-by.spec.js +68 -107
  179. package/dist/testing/id-by.spec.js.map +1 -1
  180. package/dist/testing/label.spec.js +106 -194
  181. package/dist/testing/label.spec.js.map +1 -1
  182. package/dist/testing/ref-by.spec.js +155 -219
  183. package/dist/testing/ref-by.spec.js.map +1 -1
  184. package/dist/testing/websocket.spec.d.ts +1 -8
  185. package/dist/testing/websocket.spec.js +35 -50
  186. package/dist/testing/websocket.spec.js.map +1 -1
  187. package/dist/testing/workflow.spec.js +82 -81
  188. package/dist/testing/workflow.spec.js.map +1 -1
  189. package/dist/testing/zone.spec.js +61 -113
  190. package/dist/testing/zone.spec.js.map +1 -1
  191. package/package.json +62 -44
  192. package/scripts/mock-assistant.sh +5 -0
  193. package/scripts/run-e2e.sh +7 -0
  194. package/scripts/test.sh +2 -0
  195. package/src/dynamic.ts +4254 -0
  196. package/src/extensions/area.extension.ts +118 -0
  197. package/src/extensions/backup.extension.ts +63 -0
  198. package/src/extensions/call-proxy.extension.ts +113 -0
  199. package/src/extensions/config.extension.ts +119 -0
  200. package/src/extensions/conversation.extension.ts +46 -0
  201. package/src/extensions/device.extension.ts +56 -0
  202. package/src/extensions/entity.extension.ts +344 -0
  203. package/src/extensions/events.extension.ts +25 -0
  204. package/src/extensions/fetch-api.extension.ts +269 -0
  205. package/src/extensions/floor.extension.ts +76 -0
  206. package/src/extensions/id-by.extension.ts +157 -0
  207. package/src/extensions/index.ts +16 -0
  208. package/src/extensions/internal.extension.ts +145 -0
  209. package/src/extensions/label.extension.ts +83 -0
  210. package/src/extensions/reference.extension.ts +330 -0
  211. package/src/extensions/registry.extension.ts +44 -0
  212. package/src/extensions/websocket-api.extension.ts +554 -0
  213. package/src/extensions/zone.extension.ts +69 -0
  214. package/src/hass.module.ts +217 -0
  215. package/src/helpers/backup.helper.ts +11 -0
  216. package/src/helpers/constants.helper.ts +30 -0
  217. package/src/helpers/device.helper.ts +25 -0
  218. package/src/helpers/entity-state.helper.ts +171 -0
  219. package/src/helpers/features.helper.ts +580 -0
  220. package/src/helpers/fetch/calendar.ts +54 -0
  221. package/src/helpers/fetch/configuration.ts +75 -0
  222. package/src/helpers/fetch/index.ts +5 -0
  223. package/src/helpers/fetch/server-log.ts +28 -0
  224. package/src/helpers/fetch/service-list.ts +64 -0
  225. package/src/helpers/fetch/weather-forecasts.ts +86 -0
  226. package/src/helpers/fetch.helper.ts +328 -0
  227. package/src/helpers/id-by.helper.ts +53 -0
  228. package/src/helpers/index.ts +13 -0
  229. package/src/helpers/interfaces.helper.ts +340 -0
  230. package/src/helpers/manifest.helper.ts +0 -0
  231. package/src/helpers/notify.helper.ts +302 -0
  232. package/src/helpers/registry.ts +281 -0
  233. package/src/helpers/utility.helper.ts +147 -0
  234. package/src/helpers/websocket.helper.ts +117 -0
  235. package/src/index.ts +5 -0
  236. package/src/mock_assistant/extensions/area.extension.ts +62 -0
  237. package/src/mock_assistant/extensions/config.extension.ts +33 -0
  238. package/src/mock_assistant/extensions/device.extension.ts +44 -0
  239. package/src/mock_assistant/extensions/entity-registry.extension.ts +41 -0
  240. package/src/mock_assistant/extensions/entity.extension.ts +114 -0
  241. package/src/mock_assistant/extensions/events.extension.ts +37 -0
  242. package/src/mock_assistant/extensions/fetch.extension.ts +3 -0
  243. package/src/mock_assistant/extensions/fixtures.extension.ts +79 -0
  244. package/src/mock_assistant/extensions/floor.extension.ts +64 -0
  245. package/src/mock_assistant/extensions/index.ts +12 -0
  246. package/src/mock_assistant/extensions/label.extension.ts +64 -0
  247. package/src/mock_assistant/extensions/services.extension.ts +25 -0
  248. package/src/mock_assistant/extensions/websocket-api.extension.ts +84 -0
  249. package/src/mock_assistant/extensions/zone.extension.ts +65 -0
  250. package/src/mock_assistant/helpers/fixtures.ts +22 -0
  251. package/src/mock_assistant/helpers/index.ts +1 -0
  252. package/src/mock_assistant/index.ts +3 -0
  253. package/src/mock_assistant/main.ts +46 -0
  254. package/src/mock_assistant/mock-assistant.module.ts +90 -0
  255. package/src/quickboot.module.ts +23 -0
  256. package/src/testing/area.spec.ts +189 -0
  257. package/src/testing/backup.spec.ts +157 -0
  258. package/src/testing/config.spec.ts +188 -0
  259. package/src/testing/device.spec.ts +89 -0
  260. package/src/testing/entity.spec.ts +171 -0
  261. package/src/testing/events.spec.ts +78 -0
  262. package/src/testing/fetch-api.spec.ts +410 -0
  263. package/src/testing/fixtures.spec.ts +158 -0
  264. package/src/testing/floor.spec.ts +186 -0
  265. package/src/testing/id-by.spec.ts +140 -0
  266. package/src/testing/label.spec.ts +186 -0
  267. package/src/testing/ref-by.spec.ts +300 -0
  268. package/src/testing/websocket.spec.ts +63 -0
  269. package/src/testing/workflow.spec.ts +195 -0
  270. package/src/testing/zone.spec.ts +109 -0
  271. package/dist/helpers/metrics.helper.d.ts +0 -29
  272. package/dist/helpers/metrics.helper.js +0 -62
  273. package/dist/helpers/metrics.helper.js.map +0 -1
  274. package/dist/mock_assistant/helpers/utils.d.ts +0 -4
  275. package/dist/mock_assistant/helpers/utils.js +0 -57
  276. package/dist/mock_assistant/helpers/utils.js.map +0 -1
@@ -0,0 +1,410 @@
1
+ import dayjs from "dayjs";
2
+
3
+ import { HassConfig } from "../helpers";
4
+ import { hassTestRunner } from "../mock_assistant";
5
+
6
+ describe("FetchAPI", () => {
7
+ const BASE_URL = "http://homeassistant.some.domain:9123";
8
+ const TOKEN =
9
+ // TODO: Replace hard coded token w/ faker when avail
10
+ // https://github.com/faker-js/faker/pull/2936
11
+ // eslint-disable-next-line @cspell/spellchecker
12
+ "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2aWRlbyI6Imh0dHBzOi8vd3d3LnlvdXR1YmUuY29tL3dhdGNoP3Y9ZFF3NHc5V2dYY1EifQ.gPIttZEaLZgov3VZziu3LovcgtDbj8H0-XfBg4f08Y0";
13
+
14
+ hassTestRunner.configure({
15
+ hass: { BASE_URL, TOKEN },
16
+ });
17
+ // values are hard coded into tests, update carefully
18
+ const start = dayjs("2024-01-01 00:00:00:00");
19
+ const end = start.add(1, "day");
20
+
21
+ afterEach(async () => {
22
+ await hassTestRunner.teardown();
23
+ jest.restoreAllMocks();
24
+ });
25
+
26
+ describe("Meta", () => {
27
+ it("Should send the correct headers", async () => {
28
+ expect.assertions(1);
29
+ await hassTestRunner.run(({ lifecycle, hass }) => {
30
+ lifecycle.onReady(async () => {
31
+ const spy = jest.spyOn(global, "fetch").mockImplementation(async () => {
32
+ return {
33
+ text: () => "[]",
34
+ } as unknown as Response;
35
+ });
36
+ // Calling the base level fetch provided by service
37
+ // The same call is wrapped internally to power everything else
38
+ await hass.fetch.fetch({ url: "/api/" });
39
+ expect(spy).toHaveBeenCalledWith(
40
+ `${BASE_URL}/api/`,
41
+ expect.objectContaining({
42
+ headers: {
43
+ Authorization: "Bearer " + TOKEN,
44
+ },
45
+ method: "get",
46
+ }),
47
+ );
48
+ });
49
+ });
50
+ });
51
+ });
52
+
53
+ describe("API Operations", () => {
54
+ it("should format calendarSearch properly", async () => {
55
+ expect.assertions(1);
56
+ await hassTestRunner.run(({ lifecycle, hass }) => {
57
+ const spy = jest.spyOn(global, "fetch").mockImplementation(async () => {
58
+ return {
59
+ text: () => "[]",
60
+ } as unknown as Response;
61
+ });
62
+ lifecycle.onReady(async () => {
63
+ await hass.fetch.calendarSearch({
64
+ calendar: "calendar.united_states_tx",
65
+ end,
66
+ start,
67
+ });
68
+ expect(spy).toHaveBeenCalledWith(
69
+ `${BASE_URL}/api/calendars/calendar.united_states_tx?end=${encodeURIComponent(end.toISOString())}&start=${encodeURIComponent(start.toISOString())}`,
70
+ expect.objectContaining({
71
+ method: "get",
72
+ }),
73
+ );
74
+ });
75
+ });
76
+ });
77
+
78
+ it("should format callService properly", async () => {
79
+ expect.assertions(1);
80
+ await hassTestRunner.run(({ lifecycle, hass }) => {
81
+ const spy = jest.spyOn(global, "fetch").mockImplementation(async () => {
82
+ return {
83
+ text: () => "{}",
84
+ } as unknown as Response;
85
+ });
86
+ lifecycle.onReady(async () => {
87
+ await hass.fetch.callService("switch.toggle", {
88
+ entity_id: "switch.porch_light",
89
+ });
90
+ expect(spy).toHaveBeenCalledWith(
91
+ `${BASE_URL}/api/services/switch/toggle`,
92
+ expect.objectContaining({
93
+ body: JSON.stringify({
94
+ entity_id: "switch.porch_light",
95
+ }),
96
+ method: "post",
97
+ }),
98
+ );
99
+ });
100
+ });
101
+ });
102
+
103
+ it("should format checkConfig properly", async () => {
104
+ expect.assertions(1);
105
+ await hassTestRunner.run(({ lifecycle, hass }) => {
106
+ const spy = jest.spyOn(global, "fetch").mockImplementation(async () => {
107
+ return {
108
+ text: () => "{}",
109
+ } as unknown as Response;
110
+ });
111
+ lifecycle.onReady(async () => {
112
+ await hass.fetch.checkConfig();
113
+ expect(spy).toHaveBeenCalledWith(
114
+ `${BASE_URL}/api/config/core/check_config`,
115
+ expect.objectContaining({
116
+ method: "post",
117
+ }),
118
+ );
119
+ });
120
+ });
121
+ });
122
+
123
+ // TODO: this results in an open handle
124
+ // not sure why
125
+ xit("exits for low version at boot", async () => {
126
+ expect.assertions(2);
127
+ const exitSpy = jest.spyOn(process, "exit");
128
+
129
+ // .mockImplementation(() => {
130
+ // throw new Error("EXPECTED TESTING ERROR");
131
+ // });
132
+ jest.spyOn(console, "error").mockImplementation(() => undefined);
133
+ try {
134
+ await hassTestRunner.run(({ lifecycle, mock_assistant }) => {
135
+ lifecycle.onPreInit(() => {
136
+ mock_assistant.config.loadFixtures({ version: "2024.1.0" } as HassConfig);
137
+ }, -1000);
138
+ });
139
+ } catch (error) {
140
+ expect(error).toBeDefined();
141
+ }
142
+ await hassTestRunner.teardown();
143
+ expect(exitSpy).toHaveBeenCalled();
144
+ });
145
+
146
+ // TODO: Need a way to make this pass without breaking all other tests
147
+ it.skip("passes for valid version", async () => {
148
+ expect.assertions(2);
149
+ let mock: jest.SpyInstance;
150
+ let exitSpy: jest.SpyInstance;
151
+ try {
152
+ await hassTestRunner.run(({ hass }) => {
153
+ mock = jest
154
+ .spyOn(hass.fetch, "getConfig")
155
+ .mockImplementation(async () => ({ version: "2024.4.1" }) as HassConfig);
156
+ exitSpy = jest.spyOn(process, "exit").mockImplementation(() => {
157
+ throw new Error("EXPECTED TESTING ERROR");
158
+ });
159
+ });
160
+ } finally {
161
+ expect(mock).toHaveBeenCalled();
162
+ expect(exitSpy).not.toHaveBeenCalled();
163
+ }
164
+ });
165
+
166
+ it("should format fetchEntityCustomizations properly", async () => {
167
+ expect.assertions(1);
168
+ await hassTestRunner.run(({ lifecycle, hass }) => {
169
+ const spy = jest.spyOn(global, "fetch").mockImplementation(async () => {
170
+ return {
171
+ text: () => "{}",
172
+ } as unknown as Response;
173
+ });
174
+ lifecycle.onReady(async () => {
175
+ await hass.fetch.fetchEntityCustomizations("switch.porch_light");
176
+ expect(spy).toHaveBeenCalledWith(
177
+ `${BASE_URL}/api/config/customize/config/switch.porch_light`,
178
+ expect.objectContaining({
179
+ method: "get",
180
+ }),
181
+ );
182
+ });
183
+ });
184
+ });
185
+
186
+ it("should format fetchEntityHistory properly", async () => {
187
+ expect.assertions(1);
188
+ await hassTestRunner.run(({ lifecycle, hass }) => {
189
+ const spy = jest.spyOn(global, "fetch").mockImplementation(async () => {
190
+ return {
191
+ text: () => "{}",
192
+ } as unknown as Response;
193
+ });
194
+ lifecycle.onReady(async () => {
195
+ await hass.fetch.fetchEntityHistory("switch.porch_light", start, end);
196
+ expect(spy).toHaveBeenCalledWith(
197
+ `${BASE_URL}/api/history/period/${encodeURIComponent(start.toISOString())}?end_time=${encodeURIComponent(end.toISOString())}&filter_entity_id=switch.porch_light`,
198
+ expect.objectContaining({
199
+ method: "get",
200
+ }),
201
+ );
202
+ });
203
+ });
204
+ });
205
+
206
+ it("should format fireEvent properly", async () => {
207
+ expect.assertions(1);
208
+ const body = { magic: true };
209
+ await hassTestRunner.run(({ lifecycle, hass }) => {
210
+ const spy = jest.spyOn(global, "fetch").mockImplementation(async () => {
211
+ return {
212
+ text: () => "{}",
213
+ } as unknown as Response;
214
+ });
215
+ lifecycle.onReady(async () => {
216
+ await hass.fetch.fireEvent("testing_event", body);
217
+ expect(spy).toHaveBeenCalledWith(
218
+ `${BASE_URL}/api/events/testing_event`,
219
+ expect.objectContaining({
220
+ body: JSON.stringify(body),
221
+ method: "post",
222
+ }),
223
+ );
224
+ });
225
+ });
226
+ });
227
+
228
+ it("should format getAllEntities properly", async () => {
229
+ expect.assertions(1);
230
+ await hassTestRunner.run(({ lifecycle, hass, mock_assistant }) => {
231
+ lifecycle.onReady(async () => {
232
+ mock_assistant.entity.monkeyReset();
233
+ const spy = jest.spyOn(hass.fetch, "fetch").mockImplementation(async () => []);
234
+ await hass.fetch.getAllEntities();
235
+ expect(spy).toHaveBeenCalledWith({ url: "/api/states" });
236
+ });
237
+ });
238
+ });
239
+
240
+ // it("should format getHassConfig properly", async () => {
241
+ // expect.assertions(1);
242
+ // application = CreateTestingApplication({
243
+ // Test({ lifecycle, hass }: TServiceParams) {
244
+ // const spy = jest.spyOn(global, "fetch").mockImplementation(async () => {
245
+ // return {
246
+ // text: () => "{}",
247
+ // } as unknown as Response;
248
+ // });
249
+ // lifecycle.onReady(async () => {
250
+ // await hass.fetch.getConfig();
251
+ // expect(spy).toHaveBeenCalledWith(
252
+ // `${BASE_URL}/api/config`,
253
+ // expect.objectContaining({
254
+ // method: "get",
255
+ // }),
256
+ // );
257
+ // });
258
+ // },
259
+ // });
260
+ // await application.bootstrap(
261
+ // SILENT_BOOT(
262
+ // {
263
+ // hass: {
264
+ // AUTO_CONNECT_SOCKET: false,
265
+ // AUTO_SCAN_CALL_PROXY: false,
266
+ // BASE_URL,
267
+ // TOKEN,
268
+ // },
269
+ // },
270
+ // false,
271
+ // false,
272
+ // ),
273
+ // );
274
+ // });
275
+
276
+ it("should format getLogs properly", async () => {
277
+ expect.assertions(1);
278
+ await hassTestRunner.run(({ lifecycle, hass }) => {
279
+ const spy = jest.spyOn(global, "fetch").mockImplementation(async () => {
280
+ return {
281
+ text: () => "[]",
282
+ } as unknown as Response;
283
+ });
284
+ lifecycle.onReady(async () => {
285
+ await hass.fetch.getLogs();
286
+ expect(spy).toHaveBeenCalledWith(
287
+ `${BASE_URL}/api/error/all`,
288
+ expect.objectContaining({
289
+ method: "get",
290
+ }),
291
+ );
292
+ });
293
+ });
294
+ });
295
+
296
+ it("should format getRawLogs properly", async () => {
297
+ expect.assertions(1);
298
+ await hassTestRunner.run(({ lifecycle, hass }) => {
299
+ const spy = jest.spyOn(global, "fetch").mockImplementation(async () => {
300
+ return {
301
+ text: () => "{}",
302
+ } as unknown as Response;
303
+ });
304
+ lifecycle.onReady(async () => {
305
+ await hass.fetch.getRawLogs();
306
+ expect(spy).toHaveBeenCalledWith(
307
+ `${BASE_URL}/api/error_log`,
308
+ expect.objectContaining({
309
+ method: "get",
310
+ }),
311
+ );
312
+ });
313
+ });
314
+ });
315
+
316
+ it("should format listServices properly", async () => {
317
+ expect.assertions(1);
318
+ await hassTestRunner.run(({ lifecycle, hass, mock_assistant }) => {
319
+ lifecycle.onReady(async () => {
320
+ const spy = jest.spyOn(global, "fetch").mockImplementation(async () => {
321
+ return {
322
+ text: () => "{}",
323
+ } as unknown as Response;
324
+ });
325
+ mock_assistant.services.monkeyReset();
326
+ await hass.fetch.listServices();
327
+ expect(spy).toHaveBeenCalledWith(
328
+ `${BASE_URL}/api/services`,
329
+ expect.objectContaining({
330
+ method: "get",
331
+ }),
332
+ );
333
+ });
334
+ });
335
+ });
336
+
337
+ it("should format updateEntity properly", async () => {
338
+ expect.assertions(1);
339
+ await hassTestRunner.run(({ lifecycle, hass }) => {
340
+ const spy = jest.spyOn(global, "fetch").mockImplementation(async () => {
341
+ return {
342
+ text: () => "{}",
343
+ } as unknown as Response;
344
+ });
345
+ lifecycle.onReady(async () => {
346
+ await hass.fetch.updateEntity("switch.porch_light", {
347
+ attributes: { something: "special" },
348
+ state: "off",
349
+ });
350
+ expect(spy).toHaveBeenCalledWith(
351
+ `${BASE_URL}/api/states/switch.porch_light`,
352
+ expect.objectContaining({
353
+ body: JSON.stringify({
354
+ // ! DO NOT CHANGE ORDER OF KEYS
355
+ // It changes the resulting JSON, breaking the test
356
+ attributes: { something: "special" },
357
+ state: "off",
358
+ }),
359
+ method: "post",
360
+ }),
361
+ );
362
+ });
363
+ });
364
+ });
365
+
366
+ it("should format webhook properly", async () => {
367
+ expect.assertions(1);
368
+ const body = {
369
+ magic: true,
370
+ };
371
+ await hassTestRunner.run(({ lifecycle, hass }) => {
372
+ const spy = jest.spyOn(global, "fetch").mockImplementation(async () => {
373
+ return {
374
+ text: () => "{}",
375
+ } as unknown as Response;
376
+ });
377
+ lifecycle.onReady(async () => {
378
+ await hass.fetch.webhook("magic_webhook", body);
379
+ expect(spy).toHaveBeenCalledWith(
380
+ `${BASE_URL}/api/webhook/magic_webhook`,
381
+ expect.objectContaining({
382
+ body: JSON.stringify(body),
383
+ method: "post",
384
+ }),
385
+ );
386
+ });
387
+ });
388
+ });
389
+
390
+ it("should format checkCredentials properly", async () => {
391
+ expect.assertions(1);
392
+ await hassTestRunner.run(({ lifecycle, hass }) => {
393
+ const spy = jest.spyOn(global, "fetch").mockImplementation(async () => {
394
+ return {
395
+ text: () => "{}",
396
+ } as unknown as Response;
397
+ });
398
+ lifecycle.onReady(async () => {
399
+ await hass.fetch.checkCredentials();
400
+ expect(spy).toHaveBeenCalledWith(
401
+ `${BASE_URL}/api/`,
402
+ expect.objectContaining({
403
+ method: "get",
404
+ }),
405
+ );
406
+ });
407
+ });
408
+ });
409
+ });
410
+ });
@@ -0,0 +1,158 @@
1
+ import { v4 } from "uuid";
2
+
3
+ import { createTestRunner } from "../mock_assistant";
4
+
5
+ describe("Fixtures", () => {
6
+ let runner: ReturnType<typeof createTestRunner>;
7
+ beforeEach(() => {
8
+ runner = createTestRunner();
9
+ });
10
+ afterEach(async () => {
11
+ await runner.teardown();
12
+ jest.restoreAllMocks();
13
+ });
14
+
15
+ describe("Entity setup", () => {
16
+ describe("bootLibrariesFirst", () => {
17
+ it("matches state using setup", async () => {
18
+ expect.assertions(1);
19
+ const state = v4();
20
+ await runner
21
+ .bootLibrariesFirst()
22
+ .setup(({ mock_assistant }) => {
23
+ mock_assistant.entity.setupState({
24
+ "switch.porch_light": { state },
25
+ });
26
+ })
27
+ .run(({ hass }) => {
28
+ const entity = hass.refBy.id("switch.porch_light");
29
+ expect(entity.state).toBe(state);
30
+ });
31
+ });
32
+
33
+ it("gets mad trying to set state late", async () => {
34
+ expect.assertions(1);
35
+ const state = v4();
36
+ await runner.bootLibrariesFirst().run(({ mock_assistant }) => {
37
+ expect(() => {
38
+ mock_assistant.entity.setupState({
39
+ "switch.porch_light": { state },
40
+ });
41
+ }).toThrow();
42
+ });
43
+ });
44
+ });
45
+
46
+ describe("lifecycle", () => {
47
+ it("matches state using setup", async () => {
48
+ expect.assertions(1);
49
+ const state = v4();
50
+ await runner
51
+ .setup(({ mock_assistant }) => {
52
+ mock_assistant.entity.setupState({
53
+ "switch.porch_light": { state },
54
+ });
55
+ })
56
+ .run(({ hass, lifecycle }) => {
57
+ lifecycle.onReady(() => {
58
+ const entity = hass.refBy.id("switch.porch_light");
59
+ expect(entity.state).toBe(state);
60
+ });
61
+ });
62
+ });
63
+
64
+ it("gets mad trying to set state late", async () => {
65
+ expect.assertions(1);
66
+ const state = v4();
67
+ await runner.run(({ mock_assistant, lifecycle }) => {
68
+ lifecycle.onReady(() => {
69
+ expect(() => {
70
+ mock_assistant.entity.setupState({
71
+ "switch.porch_light": { state },
72
+ });
73
+ }).toThrow();
74
+ });
75
+ });
76
+ });
77
+
78
+ it("gets mad trying to set state late in setup", async () => {
79
+ expect.assertions(1);
80
+ const state = v4();
81
+ await runner
82
+ .setup(({ mock_assistant, lifecycle }) => {
83
+ lifecycle.onReady(() => {
84
+ expect(() => {
85
+ mock_assistant.entity.setupState({
86
+ "switch.porch_light": { state },
87
+ });
88
+ }).toThrow();
89
+ });
90
+ })
91
+ .run(() => {});
92
+ });
93
+ });
94
+ });
95
+
96
+ describe("Change events", () => {
97
+ describe("unhappy path", () => {
98
+ it("throws errors for unknown entities", async () => {
99
+ expect.assertions(1);
100
+ await runner.bootLibrariesFirst().run(async ({ mock_assistant }) => {
101
+ try {
102
+ // @ts-expect-error it's the test
103
+ await mock_assistant.entity.emitChange("switch.fake", {});
104
+ } catch (error) {
105
+ expect(error).toBeDefined();
106
+ }
107
+ });
108
+ });
109
+
110
+ it("throws errors for early changes", async () => {
111
+ expect.assertions(1);
112
+ await runner.run(async ({ mock_assistant }) => {
113
+ try {
114
+ await mock_assistant.entity.emitChange("switch.porch_light", {});
115
+ } catch (error) {
116
+ expect(error).toBeDefined();
117
+ }
118
+ });
119
+ });
120
+ });
121
+
122
+ it("update events happen on emit", async () => {
123
+ expect.assertions(1);
124
+ await runner
125
+ .bootLibrariesFirst()
126
+ .setup(({ mock_assistant }) => {
127
+ mock_assistant.entity.setupState({
128
+ "switch.porch_light": { state: "on" },
129
+ });
130
+ })
131
+ .run(async ({ hass, mock_assistant }) => {
132
+ const entity = hass.refBy.id("switch.porch_light");
133
+ const spy = jest.fn();
134
+ entity.onUpdate(spy);
135
+ await mock_assistant.entity.emitChange("switch.porch_light", { state: "off" });
136
+ expect(spy).toHaveBeenCalled();
137
+ });
138
+ });
139
+
140
+ it("sends events for noop changes", async () => {
141
+ expect.assertions(1);
142
+ await runner
143
+ .bootLibrariesFirst()
144
+ .setup(({ mock_assistant }) => {
145
+ mock_assistant.entity.setupState({
146
+ "switch.porch_light": { state: "on" },
147
+ });
148
+ })
149
+ .run(async ({ hass, mock_assistant }) => {
150
+ const entity = hass.refBy.id("switch.porch_light");
151
+ const spy = jest.fn();
152
+ entity.onUpdate(spy);
153
+ await mock_assistant.entity.emitChange("switch.porch_light", { state: "on" });
154
+ expect(spy).toHaveBeenCalled();
155
+ });
156
+ });
157
+ });
158
+ });