File size: 11,841 Bytes
f2bee8a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
import path from 'path';
import SeleniumHelper from '../helpers/selenium-helper';

const {
    clickText,
    clickBlocksCategory,
    clickButton,
    clickXpath,
    findByText,
    findByXpath,
    textExists,
    getDriver,
    getLogs,
    Key,
    loadUri,
    rightClickText,
    scope
} = new SeleniumHelper();

const uri = path.resolve(__dirname, '../../build/index.html');

let driver;

describe('Working with the blocks', () => {
    beforeAll(() => {
        driver = getDriver();
    });

    afterAll(async () => {
        await driver.quit();
    });

    test('Blocks report when clicked in the toolbox', async () => {
        await loadUri(uri);
        await clickText('Code');
        await clickBlocksCategory('Operators');
        await clickText('join', scope.blocksTab); // Click "join <hello> <world>" block
        await findByText('apple banana', scope.reportedValue); // Tooltip with result
        const logs = await getLogs();
        await expect(logs).toEqual([]);
    });

    test('Switching sprites updates the block menus', async () => {
        await loadUri(uri);
        await clickBlocksCategory('Sound');
        // "Meow" sound block should be visible
        await findByText('Meow', scope.blocksTab);
        await clickText('Backdrops'); // Switch to the backdrop
        // Now "pop" sound block should be visible and motion blocks hidden
        await findByText('pop', scope.blocksTab);
        await clickBlocksCategory('Motion');
        await findByText('Stage selected: no motion blocks');

        const logs = await getLogs();
        await expect(logs).toEqual([]);
    });

    test('Creating variables', async () => {
        await loadUri(uri);
        await clickText('Code');
        await clickBlocksCategory('Variables');

        // Expect a default variable "my variable" to be visible
        await clickText('my\u00A0variable', scope.blocksTab);
        await findByText('0', scope.reportedValue);

        await clickText('Make a Variable');
        let el = await findByXpath("//input[@name='New variable name:']");
        await el.sendKeys('score');
        await clickButton('OK');
        await clickText('Make a Variable');
        el = await findByXpath("//input[@name='New variable name:']");
        await el.sendKeys('second variable');
        await clickButton('OK');

        // Make sure reporting works on a new variable
        await clickBlocksCategory('Variables');
        await clickText('score', scope.blocksTab);
        await findByText('0', scope.reportedValue); // Tooltip with result

        // And there should be a monitor visible
        await rightClickText('score', scope.monitors);
        await clickText('slider');
        await findByXpath("//input[@step='1']");

        // Changing the slider to a decimal should make it have a step size of 0.01
        await rightClickText('score', scope.monitors);
        await clickText('change slider range');
        el = await findByXpath("//input[@name='Maximum value']");
        await el.sendKeys('.1');
        await clickButton('OK');
        await findByXpath("//input[@step='0.01'][@max='100.1']");

        // Hiding the monitor via context menu should work
        await rightClickText('score', scope.monitors);
        await clickText('hide', scope.contextMenu);
        await driver.sleep(100);
        const monitorExists = await textExists('score', scope.monitors);
        await expect(monitorExists).toBeFalsy();

        const logs = await getLogs();
        await expect(logs).toEqual([]);
    });

    test('Creating a list', async () => {
        await loadUri(uri);
        await clickText('Code');
        await clickBlocksCategory('Variables');

        await clickText('Make a List');
        let el = await findByXpath("//input[@name='New list name:']");
        await el.sendKeys('list1');
        await clickButton('OK');

        // Click the "add <thing> to list" block 3 times
        await clickText('add', scope.blocksTab);
        await clickText('add', scope.blocksTab);
        await clickText('add', scope.blocksTab);
        await clickText('list1', scope.blocksTab);
        await findByText('thing thing thing', scope.reportedValue); // Tooltip with result

        // Interact with the monitor, adding an item
        await findByText('list1', scope.monitors); // Just to be sure it is there
        await clickText('+', scope.monitors);
        el = await findByXpath(`//body//${scope.monitors}//input`);
        await el.sendKeys('thing2');
        await el.click(); // Regression for "clicking active input erases value" bug.
        await clickText('list1', scope.monitors); // Blur the input to submit

        // Check that the list value has been propagated.
        await clickText('list1', scope.blocksTab);
        await findByText('thing thing thing thing2', scope.reportedValue); // Tooltip with result

        // Hiding the monitor via context menu should work
        await rightClickText('list1', scope.monitors);
        await clickText('hide', scope.contextMenu);
        await driver.sleep(100);
        const monitorExists = await textExists('list1', scope.monitors);
        await expect(monitorExists).toBeFalsy();

        const logs = await getLogs();
        await expect(logs).toEqual([]);
    });

    test('Custom procedures', async () => {
        await loadUri(uri);
        await clickBlocksCategory('My Blocks');
        await clickText('Make a Block');
        // Click on the "add an input" buttons
        await clickText('number or text', scope.modal);
        await clickText('boolean', scope.modal);
        await clickText('Add a label', scope.modal);
        await clickText('OK', scope.modal);

        // Make sure a "define" block has been added to the workspace
        await findByText('define', scope.blocksTab);

        const logs = await getLogs();
        await expect(logs).toEqual([]);
    });

    test('Adding an extension', async () => {
        await loadUri(uri);
        await clickXpath('//button[@title="Add Extension"]');

        await clickText('Pen');
        await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for scroll animation
        // Make sure toolbox has been scrolled to the pen extension
        await findByText('stamp', scope.blocksTab);

        const logs = await getLogs();
        await expect(logs).toEqual([]);
    });

    test('Record option from sound block menu opens sound recorder', async () => {
        await loadUri(uri);
        await clickText('Code');
        await clickBlocksCategory('Sound');
        await clickText('Meow', scope.blocksTab); // Click "play sound <Meow> until done" block
        await clickText('record'); // Click "record..." option in the block's sound menu
        // Access has been force denied, so close the alert that comes up
        await driver.sleep(1000); // getUserMedia requests are very slow to fail for some reason
        await driver.switchTo().alert()
            .accept();
        await findByText('Record Sound'); // Sound recorder is open
        const logs = await getLogs();
        await expect(logs).toEqual([]);
    });

    test('Renaming costume changes the default costume name in the toolbox', async () => {
        await loadUri(uri);

        // Rename the costume
        await clickText('Costumes');
        await clickText('costume2', scope.costumesTab);
        const el = await findByXpath("//input[@value='costume2']");
        await el.sendKeys('newname');
        await el.sendKeys(Key.ENTER);
        // wait until the updated costume appears in costume item list panel
        await findByXpath("//div[contains(@class,'sprite-selector-item_is-selected_')]" +
            "//div[contains(text(), 'newname')]");

        // Make sure it is updated in the block menu
        await clickText('Code');
        await clickBlocksCategory('Looks');
        await clickText('newname', scope.blocksTab);
    });

    test('Renaming costume with a special character should not break toolbox', async () => {
        await loadUri(uri);

        // Rename the costume
        await clickText('Costumes');
        await clickText('costume2', scope.costumesTab);
        const el = await findByXpath("//input[@value='costume2']");
        await el.sendKeys('<NewCostume>');
        await el.sendKeys(Key.ENTER);
        // wait until the updated costume appears in costume item list panel
        await findByXpath("//div[contains(@class,'sprite-selector-item_is-selected_')]" +
            "//div[contains(text(), '<NewCostume>')]");

        // Make sure it is updated in the block menu
        await clickText('Code');
        await clickBlocksCategory('Looks');
        await clickText('<NewCostume>', scope.blocksTab);

        await clickBlocksCategory('Sound');
    });

    test('Adding costumes DOES update the default costume name in the toolbox', async () => {
        await loadUri(uri);

        // By default, costume2 is in the costume tab
        await clickBlocksCategory('Looks');
        await clickText('costume2', scope.blocksTab);

        // Also check that adding a new costume does update the list
        await clickText('Costumes');
        const el = await findByXpath('//button[@aria-label="Choose a Costume"]');
        await driver.actions().mouseMove(el)
            .perform();
        await driver.sleep(500); // Wait for thermometer menu to come up
        await clickXpath('//button[@aria-label="Paint"]');
        // wait until the new costume appears in costume item list panel
        await findByXpath("//div[contains(@class,'sprite-selector-item_is-selected_')]" +
            "//div[contains(text(), 'costume3')]");
        await clickText('costume3', scope.costumesTab);
        // Check that the menu has been updated
        await clickText('Code');
        await clickText('costume3', scope.blocksTab);
    });

    // Skipped because it was flakey on travis, but seems to run locally ok
    test('Adding a sound DOES update the default sound name in the toolbox', async () => {
        await loadUri(uri);
        await clickText('Sounds');
        await clickXpath('//button[@aria-label="Choose a Sound"]');
        await clickText('A Bass', scope.modal); // Should close the modal
        // wait until the selected sound appears in sounds item list panel
        await findByXpath("//div[contains(@class,'sprite-selector-item_is-selected_')]" +
            "//div[contains(text(), 'A Bass')]");
        await clickText('Code');
        await clickBlocksCategory('Sound');
        await clickText('A\u00A0Bass', scope.blocksTab); // Need &nbsp; for block text
    });

    // Regression test for switching between editor/player causing toolbox to stop updating
    test('"See inside" after being on project page re-initializing variables', async () => {
        const playerUri = path.resolve(__dirname, '../../build/player.html');
        await loadUri(playerUri);
        await clickText('See inside');
        await clickBlocksCategory('Variables');
        await clickText('my\u00A0variable');

        await clickText('See Project Page');
        await clickText('See inside');

        await clickBlocksCategory('Variables');
        await clickText('my\u00A0variable');
    });

    // Regression test for switching editor tabs causing toolbox to stop updating
    test('Creating variables after adding extensions updates the toolbox', async () => {
        await loadUri(uri);
        await clickText('Costumes');
        await clickText('Code');
        await clickBlocksCategory('Variables');
        await clickText('Make a List');
        const el = await findByXpath("//input[@name='New list name:']");
        await el.sendKeys('list1');
        await clickButton('OK');
        await clickText('list1', scope.blocksTab);
    });
});