blocks.test.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. import path from 'path';
  2. import SeleniumHelper from '../helpers/selenium-helper';
  3. const {
  4. clickText,
  5. clickButton,
  6. clickXpath,
  7. findByText,
  8. findByXpath,
  9. getDriver,
  10. getLogs,
  11. loadUri,
  12. rightClickText,
  13. scope
  14. } = new SeleniumHelper();
  15. const uri = path.resolve(__dirname, '../../build/index.html');
  16. let driver;
  17. describe('Working with the blocks', () => {
  18. beforeAll(() => {
  19. driver = getDriver();
  20. });
  21. afterAll(async () => {
  22. await driver.quit();
  23. });
  24. test('Blocks report when clicked in the toolbox', async () => {
  25. await loadUri(uri);
  26. await clickText('Code');
  27. await clickText('Operators', scope.blocksTab);
  28. await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for scroll animation
  29. await clickText('join', scope.blocksTab); // Click "join <hello> <world>" block
  30. await findByText('apple banana', scope.reportedValue); // Tooltip with result
  31. const logs = await getLogs();
  32. await expect(logs).toEqual([]);
  33. });
  34. test('Switching sprites updates the block menus', async () => {
  35. await loadUri(uri);
  36. await clickText('Sound', scope.blocksTab);
  37. await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for scroll animation
  38. // "Meow" sound block should be visible
  39. await findByText('Meow', scope.blocksTab);
  40. await clickText('Backdrops'); // Switch to the backdrop
  41. // Now "pop" sound block should be visible and motion blocks hidden
  42. await findByText('pop', scope.blocksTab);
  43. await clickText('Motion', scope.blocksTab);
  44. await findByText('Stage selected: no motion blocks');
  45. const logs = await getLogs();
  46. await expect(logs).toEqual([]);
  47. });
  48. test('Creating variables', async () => {
  49. await loadUri(uri);
  50. await clickText('Code');
  51. await clickText('Variables', scope.blocksTab);
  52. await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for scroll animation
  53. // Expect a default variable "my variable" to be visible
  54. await clickText('my\u00A0variable', scope.blocksTab);
  55. await findByText('0', scope.reportedValue);
  56. await clickText('Make a Variable');
  57. let el = await findByXpath("//input[@name='New variable name:']");
  58. await el.sendKeys('score');
  59. await clickButton('OK');
  60. await clickText('Make a Variable');
  61. el = await findByXpath("//input[@name='New variable name:']");
  62. await el.sendKeys('second variable');
  63. await clickButton('OK');
  64. // Make sure reporting works on a new variable
  65. await clickText('Variables', scope.blocksTab);
  66. await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for scroll animation
  67. await clickText('score', scope.blocksTab);
  68. await findByText('0', scope.reportedValue); // Tooltip with result
  69. // And there should be a monitor visible
  70. await rightClickText('score', scope.monitors);
  71. await clickText('slider');
  72. await findByXpath("//input[@step='1']");
  73. // Changing the slider to a decimal should make it have a step size of 0.01
  74. await rightClickText('score', scope.monitors);
  75. await clickText('change slider range');
  76. el = await findByXpath("//input[@name='Maximum value']");
  77. await el.sendKeys('.1');
  78. await clickButton('OK');
  79. await findByXpath("//input[@step='0.01'][@max='100.1']");
  80. const logs = await getLogs();
  81. await expect(logs).toEqual([]);
  82. });
  83. test('Creating a list', async () => {
  84. await loadUri(uri);
  85. await clickText('Code');
  86. await clickText('Variables', scope.blocksTab);
  87. await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for scroll animation
  88. await clickText('Make a List');
  89. let el = await findByXpath("//input[@name='New list name:']");
  90. await el.sendKeys('list1');
  91. await clickButton('OK');
  92. // Click the "add <thing> to list" block 3 times
  93. await clickText('add', scope.blocksTab);
  94. await clickText('add', scope.blocksTab);
  95. await clickText('add', scope.blocksTab);
  96. await clickText('list1', scope.blocksTab);
  97. await findByText('thing thing thing', scope.reportedValue); // Tooltip with result
  98. // Interact with the monitor, adding an item
  99. await findByText('list1', scope.monitors); // Just to be sure it is there
  100. await clickText('+', scope.monitors);
  101. el = await findByXpath(`//body//${scope.monitors}//input`);
  102. await el.sendKeys('thing2');
  103. await el.click(); // Regression for "clicking active input erases value" bug.
  104. await clickText('list1', scope.monitors); // Blur the input to submit
  105. // Check that the list value has been propagated.
  106. await clickText('list1', scope.blocksTab);
  107. await findByText('thing thing thing thing2', scope.reportedValue); // Tooltip with result
  108. const logs = await getLogs();
  109. await expect(logs).toEqual([]);
  110. });
  111. test('Custom procedures', async () => {
  112. await loadUri(uri);
  113. await clickText('My Blocks');
  114. await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for scroll animation
  115. await clickText('Make a Block');
  116. // Click on the "add an input" buttons
  117. await clickText('number or text', scope.modal);
  118. await clickText('boolean', scope.modal);
  119. await clickText('Add a label', scope.modal);
  120. await clickText('OK', scope.modal);
  121. // Make sure a "define" block has been added to the workspace
  122. await findByText('define', scope.blocksTab);
  123. const logs = await getLogs();
  124. await expect(logs).toEqual([]);
  125. });
  126. test('Adding an extension', async () => {
  127. await loadUri(uri);
  128. await clickXpath('//button[@title="Add Extension"]');
  129. await clickText('Pen');
  130. await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for scroll animation
  131. // Make sure toolbox has been scrolled to the pen extension
  132. await findByText('stamp', scope.blocksTab);
  133. const logs = await getLogs();
  134. await expect(logs).toEqual([]);
  135. });
  136. test('Record option from sound block menu opens sound recorder', async () => {
  137. await loadUri(uri);
  138. await clickText('Code');
  139. await clickText('Sound', scope.blocksTab);
  140. await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for scroll animation
  141. await clickText('Meow', scope.blocksTab); // Click "play sound <Meow> until done" block
  142. await clickText('record'); // Click "record..." option in the block's sound menu
  143. // Access has been force denied, so close the alert that comes up
  144. await driver.sleep(1000); // getUserMedia requests are very slow to fail for some reason
  145. await driver.switchTo().alert()
  146. .accept();
  147. await findByText('Record Sound'); // Sound recorder is open
  148. const logs = await getLogs();
  149. await expect(logs).toEqual([]);
  150. });
  151. test('Renaming costume changes the default costume name in the toolbox', async () => {
  152. await loadUri(uri);
  153. // Rename the costume
  154. await clickText('Costumes');
  155. await clickText('costume2', scope.costumesTab);
  156. const el = await findByXpath("//input[@value='costume2']");
  157. await el.sendKeys('newname');
  158. // Make sure it is updated in the block menu
  159. await clickText('Code');
  160. await clickText('Looks', scope.blocksTab);
  161. await driver.sleep(500); // Wait for scroll to finish
  162. await clickText('newname', scope.blocksTab);
  163. });
  164. test('Renaming costume with a special character should not break toolbox', async () => {
  165. await loadUri(uri);
  166. // Rename the costume
  167. await clickText('Costumes');
  168. await clickText('costume2', scope.costumesTab);
  169. const el = await findByXpath("//input[@value='costume2']");
  170. await el.sendKeys('<NewCostume>');
  171. // Make sure it is updated in the block menu
  172. await clickText('Code');
  173. await clickText('Looks', scope.blocksTab);
  174. await driver.sleep(500); // Wait for scroll to finish
  175. await clickText('<NewCostume>', scope.blocksTab);
  176. await clickText('Sound', scope.blocksTab);
  177. });
  178. test('Adding costumes DOES update the default costume name in the toolbox', async () => {
  179. await loadUri(uri);
  180. // By default, costume2 is in the costume tab
  181. await clickText('Looks', scope.blocksTab);
  182. await driver.sleep(500); // Wait for scroll to finish
  183. await clickText('costume2', scope.blocksTab);
  184. // Also check that adding a new costume does update the list
  185. await clickText('Costumes');
  186. const el = await findByXpath('//button[@aria-label="Choose a Costume"]');
  187. await driver.actions().mouseMove(el)
  188. .perform();
  189. await driver.sleep(500); // Wait for thermometer menu to come up
  190. await clickXpath('//button[@aria-label="Paint"]');
  191. await clickText('costume3', scope.costumesTab);
  192. // Check that the menu has been updated
  193. await clickText('Code');
  194. await clickText('costume3', scope.blocksTab);
  195. });
  196. // Skipped because it was flakey on travis, but seems to run locally ok
  197. test.skip('Adding a sound DOES update the default sound name in the toolbox', async () => {
  198. await loadUri(uri);
  199. await clickText('Sounds');
  200. await clickXpath('//button[@aria-label="Choose a Sound"]');
  201. await clickText('A Bass', scope.modal); // Should close the modal
  202. await clickText('Code');
  203. await clickText('Sound', scope.blocksTab);
  204. await driver.sleep(500); // Wait for scroll to finish
  205. await clickText('A\u00A0Bass', scope.blocksTab); // Need &nbsp; for block text
  206. });
  207. // Regression test for switching between editor/player causing toolbox to stop updating
  208. test('"See inside" after being on project page re-initializing variables', async () => {
  209. const playerUri = path.resolve(__dirname, '../../build/player.html');
  210. await loadUri(playerUri);
  211. await clickText('See inside');
  212. await clickText('Variables');
  213. await driver.sleep(500); // Wait for scroll to finish
  214. await clickText('my\u00A0variable');
  215. await clickText('See Project Page');
  216. await clickText('See inside');
  217. await clickText('Variables');
  218. await driver.sleep(500); // Wait for scroll to finish
  219. await clickText('my\u00A0variable');
  220. });
  221. // Regression test for switching editor tabs causing toolbox to stop updating
  222. test('Creating variables after adding extensions updates the toolbox', async () => {
  223. await loadUri(uri);
  224. await clickText('Costumes');
  225. await clickText('Code');
  226. await clickText('Variables', scope.blocksTab);
  227. await driver.sleep(500); // Wait for scroll
  228. await clickText('Make a List');
  229. const el = await findByXpath("//input[@name='New list name:']");
  230. await el.sendKeys('list1');
  231. await clickButton('OK');
  232. await clickText('list1', scope.blocksTab);
  233. });
  234. });