cloud-manager-hoc.test.jsx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. import 'web-audio-test-api';
  2. import React from 'react';
  3. import configureStore from 'redux-mock-store';
  4. import {mount} from 'enzyme';
  5. import VM from 'scratch-vm';
  6. import {LoadingState} from '../../../src/reducers/project-state';
  7. import CloudProvider from '../../../src/lib/cloud-provider';
  8. const mockCloudProviderInstance = {
  9. connection: true,
  10. requestCloseConnection: jest.fn()
  11. };
  12. jest.mock('../../../src/lib/cloud-provider', () =>
  13. jest.fn().mockImplementation(() => mockCloudProviderInstance)
  14. );
  15. import cloudManagerHOC from '../../../src/lib/cloud-manager-hoc.jsx';
  16. describe('CloudManagerHOC', () => {
  17. const mockStore = configureStore();
  18. let store;
  19. let vm;
  20. let stillLoadingStore;
  21. beforeEach(() => {
  22. store = mockStore({
  23. scratchGui: {
  24. projectState: {
  25. projectId: '1234',
  26. loadingState: LoadingState.SHOWING_WITH_ID
  27. },
  28. mode: {
  29. hasEverEnteredEditor: false
  30. }
  31. }
  32. });
  33. stillLoadingStore = mockStore({
  34. scratchGui: {
  35. projectState: {
  36. projectId: '1234',
  37. loadingState: LoadingState.LOADING_WITH_ID
  38. },
  39. mode: {
  40. hasEverEnteredEditor: false
  41. }
  42. }
  43. });
  44. vm = new VM();
  45. vm.setCloudProvider = jest.fn();
  46. vm.runtime = {
  47. hasCloudData: jest.fn(() => true)
  48. };
  49. CloudProvider.mockClear();
  50. mockCloudProviderInstance.requestCloseConnection.mockClear();
  51. });
  52. test('when it mounts, the cloud provider is set on the vm', () => {
  53. const Component = () => (<div />);
  54. const WrappedComponent = cloudManagerHOC(Component);
  55. const onShowCloudInfo = jest.fn();
  56. mount(
  57. <WrappedComponent
  58. hasCloudPermission
  59. cloudHost="nonEmpty"
  60. store={store}
  61. username="user"
  62. vm={vm}
  63. onShowCloudInfo={onShowCloudInfo}
  64. />
  65. );
  66. expect(vm.setCloudProvider.mock.calls.length).toBe(1);
  67. expect(CloudProvider).toHaveBeenCalledTimes(1);
  68. expect(vm.setCloudProvider).toHaveBeenCalledWith(mockCloudProviderInstance);
  69. expect(onShowCloudInfo).not.toHaveBeenCalled();
  70. });
  71. test('when cloudHost is missing, the cloud provider is not set on the vm', () => {
  72. const Component = () => (<div />);
  73. const WrappedComponent = cloudManagerHOC(Component);
  74. mount(
  75. <WrappedComponent
  76. hasCloudPermission
  77. store={store}
  78. username="user"
  79. vm={vm}
  80. />
  81. );
  82. expect(vm.setCloudProvider.mock.calls.length).toBe(0);
  83. expect(CloudProvider).not.toHaveBeenCalled();
  84. });
  85. test('when projectID is missing, the cloud provider is not set on the vm', () => {
  86. const Component = () => (<div />);
  87. const WrappedComponent = cloudManagerHOC(Component);
  88. mount(
  89. <WrappedComponent
  90. hasCloudPermission
  91. cloudHost="nonEmpty"
  92. store={store}
  93. vm={vm}
  94. />
  95. );
  96. expect(vm.setCloudProvider.mock.calls.length).toBe(0);
  97. expect(CloudProvider).not.toHaveBeenCalled();
  98. });
  99. test('when project is not showingWithId, the cloud provider is not set on the vm', () => {
  100. const Component = () => (<div />);
  101. const WrappedComponent = cloudManagerHOC(Component);
  102. mount(
  103. <WrappedComponent
  104. hasCloudPermission
  105. cloudHost="nonEmpty"
  106. store={stillLoadingStore}
  107. username="user"
  108. vm={vm}
  109. />
  110. );
  111. expect(vm.setCloudProvider.mock.calls.length).toBe(0);
  112. expect(CloudProvider).not.toHaveBeenCalled();
  113. });
  114. test('when hasCloudPermission is false, the cloud provider is not set on the vm', () => {
  115. const Component = () => <div />;
  116. const WrappedComponent = cloudManagerHOC(Component);
  117. mount(
  118. <WrappedComponent
  119. cloudHost="nonEmpty"
  120. hasCloudPermission={false}
  121. store={store}
  122. username="user"
  123. vm={vm}
  124. />
  125. );
  126. expect(vm.setCloudProvider.mock.calls.length).toBe(0);
  127. expect(CloudProvider).not.toHaveBeenCalled();
  128. });
  129. test('if the isShowingWithId prop becomes true, it sets the cloud provider on the vm', () => {
  130. const Component = () => <div />;
  131. const WrappedComponent = cloudManagerHOC(Component);
  132. const onShowCloudInfo = jest.fn();
  133. vm.runtime.hasCloudData = jest.fn(() => false);
  134. const mounted = mount(
  135. <WrappedComponent
  136. hasCloudPermission
  137. cloudHost="nonEmpty"
  138. store={stillLoadingStore}
  139. username="user"
  140. vm={vm}
  141. onShowCloudInfo={onShowCloudInfo}
  142. />
  143. );
  144. expect(onShowCloudInfo).not.toHaveBeenCalled();
  145. vm.runtime.hasCloudData = jest.fn(() => true);
  146. vm.emit('HAS_CLOUD_DATA_UPDATE', true);
  147. mounted.setProps({
  148. isShowingWithId: true,
  149. loadingState: LoadingState.SHOWING_WITH_ID
  150. });
  151. expect(vm.setCloudProvider.mock.calls.length).toBe(1);
  152. expect(CloudProvider).toHaveBeenCalledTimes(1);
  153. expect(vm.setCloudProvider).toHaveBeenCalledWith(mockCloudProviderInstance);
  154. expect(onShowCloudInfo).not.toHaveBeenCalled();
  155. });
  156. test('projectId change should not trigger cloudProvider connection unless isShowingWithId becomes true', () => {
  157. const Component = () => <div />;
  158. const WrappedComponent = cloudManagerHOC(Component);
  159. const mounted = mount(
  160. <WrappedComponent
  161. hasCloudPermission
  162. cloudHost="nonEmpty"
  163. store={stillLoadingStore}
  164. username="user"
  165. vm={vm}
  166. />
  167. );
  168. mounted.setProps({
  169. projectId: 'a different id'
  170. });
  171. expect(vm.setCloudProvider.mock.calls.length).toBe(0);
  172. expect(CloudProvider).not.toHaveBeenCalled();
  173. mounted.setProps({
  174. isShowingWithId: true,
  175. loadingState: LoadingState.SHOWING_WITH_ID
  176. });
  177. expect(vm.setCloudProvider.mock.calls.length).toBe(1);
  178. expect(CloudProvider).toHaveBeenCalledTimes(1);
  179. expect(vm.setCloudProvider).toHaveBeenCalledWith(mockCloudProviderInstance);
  180. });
  181. test('when it unmounts, the cloud provider is reset to null on the vm', () => {
  182. const Component = () => (<div />);
  183. const WrappedComponent = cloudManagerHOC(Component);
  184. const mounted = mount(
  185. <WrappedComponent
  186. hasCloudPermission
  187. cloudHost="nonEmpty"
  188. store={store}
  189. username="user"
  190. vm={vm}
  191. />
  192. );
  193. expect(CloudProvider).toHaveBeenCalled();
  194. const requestCloseConnection = mockCloudProviderInstance.requestCloseConnection;
  195. mounted.unmount();
  196. // vm.setCloudProvider is called twice,
  197. // once during mount and once during unmount
  198. expect(vm.setCloudProvider.mock.calls.length).toBe(2);
  199. expect(vm.setCloudProvider).toHaveBeenCalledWith(null);
  200. expect(requestCloseConnection).toHaveBeenCalledTimes(1);
  201. });
  202. test('projectId changing should trigger cloudProvider disconnection', () => {
  203. const Component = () => <div />;
  204. const WrappedComponent = cloudManagerHOC(Component);
  205. const mounted = mount(
  206. <WrappedComponent
  207. hasCloudPermission
  208. cloudHost="nonEmpty"
  209. store={store}
  210. username="user"
  211. vm={vm}
  212. />
  213. );
  214. expect(CloudProvider).toHaveBeenCalled();
  215. const requestCloseConnection = mockCloudProviderInstance.requestCloseConnection;
  216. mounted.setProps({
  217. projectId: 'a different id'
  218. });
  219. expect(vm.setCloudProvider.mock.calls.length).toBe(2);
  220. expect(vm.setCloudProvider).toHaveBeenCalledWith(null);
  221. expect(requestCloseConnection).toHaveBeenCalledTimes(1);
  222. });
  223. test('username changing should trigger cloudProvider disconnection', () => {
  224. const Component = () => <div />;
  225. const WrappedComponent = cloudManagerHOC(Component);
  226. const mounted = mount(
  227. <WrappedComponent
  228. hasCloudPermission
  229. cloudHost="nonEmpty"
  230. store={store}
  231. username="user"
  232. vm={vm}
  233. />
  234. );
  235. expect(CloudProvider).toHaveBeenCalled();
  236. const requestCloseConnection = mockCloudProviderInstance.requestCloseConnection;
  237. mounted.setProps({
  238. username: 'a different user'
  239. });
  240. expect(vm.setCloudProvider.mock.calls.length).toBe(2);
  241. expect(vm.setCloudProvider).toHaveBeenCalledWith(null);
  242. expect(requestCloseConnection).toHaveBeenCalledTimes(1);
  243. });
  244. test('project without cloud data should not trigger cloud connection', () => {
  245. // Mock the vm runtime function so that has cloud data is not
  246. // initially true
  247. vm.runtime.hasCloudData = jest.fn(() => false);
  248. const Component = () => <div />;
  249. const WrappedComponent = cloudManagerHOC(Component);
  250. mount(
  251. <WrappedComponent
  252. hasCloudPermission
  253. cloudHost="nonEmpty"
  254. store={store}
  255. username="user"
  256. vm={vm}
  257. />
  258. );
  259. expect(vm.setCloudProvider.mock.calls.length).toBe(0);
  260. expect(CloudProvider).not.toHaveBeenCalled();
  261. });
  262. test('projectHasCloudData becoming true should trigger a cloud connection', () => {
  263. // Mock the vm runtime function so that has cloud data is not
  264. // initially true
  265. vm.runtime.hasCloudData = jest.fn(() => false);
  266. const onShowCloudInfo = jest.fn();
  267. const Component = () => <div />;
  268. const WrappedComponent = cloudManagerHOC(Component);
  269. mount(
  270. <WrappedComponent
  271. hasCloudPermission
  272. cloudHost="nonEmpty"
  273. store={store}
  274. username="user"
  275. vm={vm}
  276. onShowCloudInfo={onShowCloudInfo}
  277. />
  278. );
  279. expect(vm.setCloudProvider.mock.calls.length).toBe(0);
  280. expect(CloudProvider).not.toHaveBeenCalled();
  281. expect(onShowCloudInfo).not.toHaveBeenCalled();
  282. // Mock VM hasCloudData becoming true and emitting an update
  283. vm.runtime.hasCloudData = jest.fn(() => true);
  284. vm.emit('HAS_CLOUD_DATA_UPDATE', true);
  285. expect(vm.setCloudProvider.mock.calls.length).toBe(1);
  286. expect(CloudProvider).toHaveBeenCalledTimes(1);
  287. expect(vm.setCloudProvider).toHaveBeenCalledWith(mockCloudProviderInstance);
  288. expect(onShowCloudInfo).toHaveBeenCalled();
  289. });
  290. test('projectHasCloudDataUpdate becoming false should trigger cloudProvider disconnection', () => {
  291. const Component = () => <div />;
  292. const WrappedComponent = cloudManagerHOC(Component);
  293. mount(
  294. <WrappedComponent
  295. hasCloudPermission
  296. cloudHost="nonEmpty"
  297. store={store}
  298. username="user"
  299. vm={vm}
  300. />
  301. );
  302. expect(CloudProvider).toHaveBeenCalled();
  303. const requestCloseConnection = mockCloudProviderInstance.requestCloseConnection;
  304. vm.runtime.hasCloudData = jest.fn(() => false);
  305. vm.emit('HAS_CLOUD_DATA_UPDATE', false);
  306. expect(vm.setCloudProvider.mock.calls.length).toBe(2);
  307. expect(vm.setCloudProvider).toHaveBeenCalledWith(null);
  308. expect(requestCloseConnection).toHaveBeenCalledTimes(1);
  309. });
  310. // Editor Mode Connection/Disconnection Tests
  311. test('Entering editor mode and can\'t save project should disconnect cloud provider', () => {
  312. const Component = () => <div />;
  313. const WrappedComponent = cloudManagerHOC(Component);
  314. const mounted = mount(
  315. <WrappedComponent
  316. hasCloudPermission
  317. cloudHost="nonEmpty"
  318. store={store}
  319. username="user"
  320. vm={vm}
  321. />
  322. );
  323. expect(CloudProvider).toHaveBeenCalled();
  324. const requestCloseConnection = mockCloudProviderInstance.requestCloseConnection;
  325. mounted.setProps({
  326. canModifyCloudData: false
  327. });
  328. expect(vm.setCloudProvider.mock.calls.length).toBe(2);
  329. expect(vm.setCloudProvider).toHaveBeenCalledWith(null);
  330. expect(requestCloseConnection).toHaveBeenCalledTimes(1);
  331. });
  332. });