vm-listener-hoc.test.jsx 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. import React from 'react';
  2. import configureStore from 'redux-mock-store';
  3. import {mount} from 'enzyme';
  4. import VM from 'scratch-vm';
  5. import vmListenerHOC from '../../../src/lib/vm-listener-hoc.jsx';
  6. describe('VMListenerHOC', () => {
  7. const mockStore = configureStore();
  8. let store;
  9. let vm;
  10. beforeEach(() => {
  11. vm = new VM();
  12. store = mockStore({
  13. scratchGui: {
  14. mode: {},
  15. modals: {},
  16. vm: vm
  17. }
  18. });
  19. });
  20. test('vm green flag event is bound to the passed in prop callback', () => {
  21. const Component = () => (<div />);
  22. const WrappedComponent = vmListenerHOC(Component);
  23. const onGreenFlag = jest.fn();
  24. mount(
  25. <WrappedComponent
  26. store={store}
  27. vm={vm}
  28. onGreenFlag={onGreenFlag}
  29. />
  30. );
  31. expect(onGreenFlag).not.toHaveBeenCalled();
  32. vm.emit('PROJECT_START');
  33. expect(onGreenFlag).toHaveBeenCalled();
  34. });
  35. test('onGreenFlag is not passed to the children', () => {
  36. const Component = () => (<div />);
  37. const WrappedComponent = vmListenerHOC(Component);
  38. const wrapper = mount(
  39. <WrappedComponent
  40. store={store}
  41. vm={vm}
  42. onGreenFlag={jest.fn()}
  43. />
  44. );
  45. const child = wrapper.find(Component);
  46. expect(child.props().onGreenFlag).toBeUndefined();
  47. });
  48. test('targetsUpdate event from vm triggers targets update action', () => {
  49. const Component = () => (<div />);
  50. const WrappedComponent = vmListenerHOC(Component);
  51. mount(
  52. <WrappedComponent
  53. store={store}
  54. vm={vm}
  55. />
  56. );
  57. const targetList = [];
  58. const editingTarget = 'id';
  59. vm.emit('targetsUpdate', {targetList, editingTarget});
  60. const actions = store.getActions();
  61. expect(actions[0].type).toEqual('scratch-gui/targets/UPDATE_TARGET_LIST');
  62. expect(actions[0].targets).toEqual(targetList);
  63. expect(actions[0].editingTarget).toEqual(editingTarget);
  64. });
  65. test('targetsUpdate does not dispatch if the sound recorder is visible', () => {
  66. const Component = () => (<div />);
  67. const WrappedComponent = vmListenerHOC(Component);
  68. store = mockStore({
  69. scratchGui: {
  70. mode: {},
  71. modals: {soundRecorder: true},
  72. vm: vm
  73. }
  74. });
  75. mount(
  76. <WrappedComponent
  77. store={store}
  78. vm={vm}
  79. />
  80. );
  81. const targetList = [];
  82. const editingTarget = 'id';
  83. vm.emit('targetsUpdate', {targetList, editingTarget});
  84. const actions = store.getActions();
  85. expect(actions.length).toEqual(0);
  86. });
  87. test('PROJECT_CHANGED does dispatch if the sound recorder is visible', () => {
  88. const Component = () => (<div />);
  89. const WrappedComponent = vmListenerHOC(Component);
  90. store = mockStore({
  91. scratchGui: {
  92. mode: {},
  93. modals: {soundRecorder: true},
  94. vm: vm
  95. }
  96. });
  97. mount(
  98. <WrappedComponent
  99. store={store}
  100. vm={vm}
  101. />
  102. );
  103. vm.emit('PROJECT_CHANGED');
  104. const actions = store.getActions();
  105. expect(actions.length).toEqual(1);
  106. });
  107. test('PROJECT_CHANGED does not dispatch if in fullscreen mode', () => {
  108. const Component = () => (<div />);
  109. const WrappedComponent = vmListenerHOC(Component);
  110. store = mockStore({
  111. scratchGui: {
  112. mode: {isFullScreen: true},
  113. modals: {soundRecorder: true},
  114. vm: vm
  115. }
  116. });
  117. mount(
  118. <WrappedComponent
  119. store={store}
  120. vm={vm}
  121. />
  122. );
  123. vm.emit('PROJECT_CHANGED');
  124. const actions = store.getActions();
  125. expect(actions.length).toEqual(0);
  126. });
  127. test('keypresses go to the vm', () => {
  128. const Component = () => (<div />);
  129. const WrappedComponent = vmListenerHOC(Component);
  130. // Mock document.addEventListener so we can trigger keypresses manually
  131. // Cannot use the enzyme simulate method because that only works on synthetic events
  132. const eventTriggers = {};
  133. document.addEventListener = jest.fn((event, cb) => {
  134. eventTriggers[event] = cb;
  135. });
  136. vm.postIOData = jest.fn();
  137. store = mockStore({
  138. scratchGui: {
  139. mode: {isFullScreen: true},
  140. modals: {soundRecorder: true},
  141. vm: vm
  142. }
  143. });
  144. mount(
  145. <WrappedComponent
  146. attachKeyboardEvents
  147. store={store}
  148. vm={vm}
  149. />
  150. );
  151. // keyboard events that do not target the document or body are ignored
  152. eventTriggers.keydown({key: 'A', target: null});
  153. expect(vm.postIOData).not.toHaveBeenLastCalledWith('keyboard', {key: 'A', isDown: true});
  154. // keydown/up with target as the document are sent to the vm via postIOData
  155. eventTriggers.keydown({key: 'A', target: document});
  156. expect(vm.postIOData).toHaveBeenLastCalledWith('keyboard', {key: 'A', isDown: true});
  157. eventTriggers.keyup({key: 'A', target: document});
  158. expect(vm.postIOData).toHaveBeenLastCalledWith('keyboard', {key: 'A', isDown: false});
  159. // When key is 'Dead' e.g. bluetooth keyboards on iOS, it sends keyCode instead
  160. // because the VM can process both named keys or keyCodes as the `key` property
  161. eventTriggers.keyup({key: 'Dead', keyCode: 10, target: document});
  162. expect(vm.postIOData).toHaveBeenLastCalledWith('keyboard', {key: 10, isDown: false});
  163. });
  164. });