Skip to content

Commit 646f28b

Browse files
authored
Merge pull request #325 from smalruby/309-test-block-to-ruby
test: block to ruby
2 parents baff667 + 769397e commit 646f28b

35 files changed

+1674
-96
lines changed

src/lib/ruby-generator/boost.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export default function (Generator) {
4646

4747
Generator.boost_setMotorDirection = function (block) {
4848
const motorid = Generator.valueToCode(block, 'MOTOR_ID', Generator.ORDER_NONE) || null;
49-
const motordirection = Generator.valueToCode(block, 'MOTOR_DIRECTION') || null;
49+
const motordirection = Generator.valueToCode(block, 'MOTOR_DIRECTION', Generator.ORDER_NONE) || null;
5050
return `boost_motor_set_direction_for(${motorid}, ${motordirection})\n`;
5151
};
5252

src/lib/ruby-generator/smalrubot_s1.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,5 +61,10 @@ export default function (Generator) {
6161
return `smalrubot_s1.set_motor_speed(${position}, ${speed})\n`;
6262
};
6363

64+
Generator.smalrubotS1_setArmCalibration = function (block) {
65+
const degree = Generator.valueToCode(block, 'DEGREE', Generator.ORDER_NONE) || 0;
66+
return `smalrubot_s1.arm_calibration = ${degree}\n`;
67+
};
68+
6469
return Generator;
6570
}

src/lib/ruby-to-blocks-converter/boost.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ const BoostConverter = {
8787
break;
8888
case 'boost_seeing_color?':
8989
if (args.length === 1 && this._isStringOrBlock(args[0])) {
90-
block = this._createBlock('boost_seeingColor', 'boolean');
90+
block = this._createBlock('boost_seeingColor', 'value_boolean');
9191
this._addInput(
9292
block,
9393
'COLOR',

src/lib/ruby-to-blocks-converter/operators.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@ const OperatorsConverter = {
2525
args.length === 1 && this._isNumberOrBlock(args[0])) {
2626
block = this._createBlock('operator_letter_of', 'value');
2727
this._addTextInput(block, 'STRING', receiver, 'apple');
28-
this._addNumberInput(block, 'LETTER', 'math_number', args[0], 1);
28+
let letter = args[0];
29+
if (this._isNumber(args[0]) && !_.isNumber(args[0]) && args[0].type === 'int') {
30+
letter = letter.value + 1;
31+
}
32+
this._addNumberInput(block, 'LETTER', 'math_number', letter, 1);
2933
return block;
3034
}
3135
break;

src/lib/ruby-to-blocks-converter/smalrubot_s1.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ const POSITIONS = [
1919
const SENSOR_POSITIONS = [
2020
'left',
2121
'right',
22-
'touch'
22+
'touch',
23+
'light',
24+
'sound'
2325
];
2426

2527
/**
@@ -105,6 +107,14 @@ const SmalrubotS1Converter = {
105107
this._addNumberInput(block, 'SPEED', 'math_number', args[1], 100);
106108
}
107109
break;
110+
case 'arm_calibration=':
111+
if (args.length === 1 && this._isNumberOrBlock(args[0])) {
112+
block = this._changeRubyExpressionBlock(
113+
receiver, 'smalrubotS1_setArmCalibration', 'statement'
114+
);
115+
this._addNumberInput(block, 'DEGREE', 'math_number', args[0], 0);
116+
}
117+
break;
108118
}
109119
}
110120
return block;

test/helpers/ruby-helper.js

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,43 @@
1-
const setRubyCode = function (driver, code) {
2-
code = code.replace(/\n/g, '\\n');
3-
return driver.executeScript(`ace.edit('ruby-editor').setValue('${code}');`);
4-
};
5-
6-
const getRubyCode = function (driver) {
7-
return driver.executeScript(`return ace.edit('ruby-editor').getValue();`);
8-
};
9-
10-
export {
11-
setRubyCode,
12-
getRubyCode
13-
};
1+
import bindAll from 'lodash.bindall';
2+
3+
class RubyHelper {
4+
constructor (seleniumHelper) {
5+
bindAll(this, [
6+
'fillInRubyProgram',
7+
'currentRubyProgram',
8+
'expectInterconvertBetweenCodeAndRuby'
9+
]);
10+
11+
this.seleniumHelper = seleniumHelper;
12+
this.clickText = seleniumHelper.clickText;
13+
this.clickXpath = seleniumHelper.clickXpath;
14+
}
15+
16+
get driver () {
17+
return this.seleniumHelper.driver;
18+
}
19+
20+
currentRubyProgram () {
21+
return this.driver.executeScript(`return ace.edit('ruby-editor').getValue();`);
22+
}
23+
24+
fillInRubyProgram (code) {
25+
code = code.replace(/\n/g, '\\n').replace(/'/g, "\\'");
26+
return this.driver.executeScript(`ace.edit('ruby-editor').setValue('${code}');`);
27+
}
28+
29+
async expectInterconvertBetweenCodeAndRuby (code) {
30+
await this.clickText('Ruby', '*[@role="tab"]');
31+
await this.fillInRubyProgram(code);
32+
await this.clickText('Code', '*[@role="tab"]');
33+
await this.clickXpath(
34+
'//div[contains(@class, "menu-bar_menu-bar-item") and contains(@class, "menu-bar_hoverable")]' +
35+
'/*/span[text()="Edit"]'
36+
);
37+
await this.clickText('Generate Ruby from Code');
38+
await this.clickText('Ruby', '*[@role="tab"]');
39+
expect(await this.currentRubyProgram()).toEqual(`${code}\n`);
40+
}
41+
}
42+
43+
export default RubyHelper;

test/helpers/selenium-helper.js

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ jest.setTimeout(30000); // eslint-disable-line no-undef
33
import bindAll from 'lodash.bindall';
44
import 'chromedriver'; // register path
55
import webdriver from 'selenium-webdriver';
6+
import pathModule from 'path';
67
const fs = require('fs');
78

89
const {By, until, Button} = webdriver;
@@ -53,7 +54,8 @@ class SeleniumHelper {
5354
soundsTab: "*[@id='react-tabs-5']",
5455
spriteTile: '*[starts-with(@class,"react-contextmenu-wrapper")]',
5556
monitors: '*[starts-with(@class,"stage_monitor-wrapper")]',
56-
contextMenu: '*[starts-with(@class,"react-contextmenu")]'
57+
contextMenu: '*[starts-with(@class,"react-contextmenu")]',
58+
spriteItems: '*[starts-with(@class,"sprite-selector_items-wrapper")]'
5759
};
5860
}
5961

@@ -127,9 +129,10 @@ class SeleniumHelper {
127129
.then(elements => elements.length > 0);
128130
}
129131

130-
notExistsByXpath (xpath) {
131-
return this.driver.findElements(By.xpath(xpath))
132-
.then(elements => elements.length === 0 || elements.every(i => !i.isDisplayed()));
132+
notExistsByXpath (xpath, timeoutMessage = `notExistsByXpath timed out for path: ${xpath}`) {
133+
return this.driver.wait(() => this.driver.findElements(By.xpath(xpath))
134+
.then(elements => elements.length === 0 || elements.every(i => !i.isDisplayed())),
135+
DEFAULT_TIMEOUT_MILLISECONDS, timeoutMessage);
133136
}
134137

135138
loadUri (uri) {
@@ -219,6 +222,15 @@ class SeleniumHelper {
219222
fs.writeFileSync(path, image, 'base64');
220223
});
221224
}
225+
226+
urlFor (path) {
227+
switch (path) {
228+
case '/':
229+
return pathModule.resolve(__dirname, '../../build/index.html');
230+
default:
231+
throw new Error(`Invalid path: ${path}`);
232+
}
233+
}
222234
}
223235

224236
export default SeleniumHelper;

test/integration/connection-modal.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ describe('Hardware extension connection modal', () => {
4444

4545
await clickText('micro:bit');
4646
await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for modal to open
47-
findByText('Scratch Link'); // Scratch Link is mentioned in the error modal
47+
await findByText('Scratch Link'); // Scratch Link is mentioned in the error modal
4848

4949
const logs = await getLogs();
5050
await expect(logs).toEqual([]);
@@ -59,7 +59,7 @@ describe('Hardware extension connection modal', () => {
5959

6060
await clickText('EV3');
6161
await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for modal to open
62-
findByText('Scratch Link'); // Scratch Link is mentioned in the error modal
62+
await findByText('Scratch Link'); // Scratch Link is mentioned in the error modal
6363

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

test/integration/costumes.test.js

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ describe('Working with costumes', () => {
4141
await findByXpath("//input[@value='Abby-a']"); // Should show editor for new costume
4242
const logs = await getLogs();
4343
await expect(logs).toEqual([]);
44-
});
44+
}, 60 * 1000);
4545

4646
test('Adding a costume by surprise button', async () => {
4747
await loadUri(uri);
@@ -53,7 +53,7 @@ describe('Working with costumes', () => {
5353
await clickXpath('//button[@aria-label="Surprise"]');
5454
const logs = await getLogs();
5555
await expect(logs).toEqual([]);
56-
});
56+
}, 60 * 1000);
5757

5858
test('Adding a costume by paint button', async () => {
5959
await loadUri(uri);
@@ -65,7 +65,7 @@ describe('Working with costumes', () => {
6565
await clickXpath('//button[@aria-label="Paint"]');
6666
const logs = await getLogs();
6767
await expect(logs).toEqual([]);
68-
});
68+
}, 60 * 1000);
6969

7070
test('Duplicating a costume', async () => {
7171
await loadUri(uri);
@@ -80,7 +80,7 @@ describe('Working with costumes', () => {
8080

8181
const logs = await getLogs();
8282
await expect(logs).toEqual([]);
83-
});
83+
}, 60 * 1000);
8484

8585
test('Converting bitmap/vector in paint editor', async () => {
8686
await loadUri(uri);
@@ -107,7 +107,7 @@ describe('Working with costumes', () => {
107107

108108
const logs = await getLogs();
109109
await expect(logs).toEqual([]);
110-
});
110+
}, 60 * 1000);
111111

112112
test('Undo/redo in the paint editor', async () => {
113113
await loadUri(uri);
@@ -121,7 +121,7 @@ describe('Working with costumes', () => {
121121
await clickText('Convert to Vector', scope.costumesTab);
122122
const logs = await getLogs();
123123
await expect(logs).toEqual([]);
124-
});
124+
}, 60 * 1000);
125125

126126
test('Adding an svg from file', async () => {
127127
await loadUri(uri);
@@ -136,7 +136,7 @@ describe('Working with costumes', () => {
136136
await clickText('100 x 100', scope.costumesTab); // Size is right
137137
const logs = await getLogs();
138138
await expect(logs).toEqual([]);
139-
});
139+
}, 60 * 1000);
140140

141141
test('Adding a png from file (gh-3582)', async () => {
142142
await loadUri(uri);
@@ -150,7 +150,7 @@ describe('Working with costumes', () => {
150150
await clickText('gh-3582-png', scope.costumesTab);
151151
const logs = await getLogs();
152152
await expect(logs).toEqual([]);
153-
});
153+
}, 60 * 1000);
154154

155155
test('Adding a bmp from file', async () => {
156156
await loadUri(uri);
@@ -164,7 +164,7 @@ describe('Working with costumes', () => {
164164
await clickText('bmpfile', scope.costumesTab);
165165
const logs = await getLogs();
166166
await expect(logs).toEqual([]);
167-
});
167+
}, 60 * 1000);
168168

169169
test('Adding several costumes with a gif', async () => {
170170
await loadUri(uri);
@@ -185,7 +185,7 @@ describe('Working with costumes', () => {
185185

186186
const logs = await getLogs();
187187
await expect(logs).toEqual([]);
188-
});
188+
}, 60 * 1000);
189189

190190
test('Adding a letter costume through the Letters filter in the library', async () => {
191191
await loadUri(uri);
@@ -199,7 +199,7 @@ describe('Working with costumes', () => {
199199
await rightClickText('Block-a', scope.costumesTab); // Make sure it is there
200200
const logs = await getLogs();
201201
await expect(logs).toEqual([]);
202-
});
202+
}, 60 * 1000);
203203

204204
test('Costumes animate on mouseover', async () => {
205205
await loadUri(uri);
@@ -214,7 +214,7 @@ describe('Working with costumes', () => {
214214
await findByXpath('//img[@src="https://cdn.assets.scratch.mit.edu/internalapi/asset/45de34b47a2ce22f6f5d28bb35a44ff5.svg/get/"]');
215215
const logs = await getLogs();
216216
await expect(logs).toEqual([]);
217-
});
217+
}, 60 * 1000);
218218

219219
test('Adding multiple costumes at the same time', async () => {
220220
const files = [
@@ -235,7 +235,7 @@ describe('Working with costumes', () => {
235235

236236
const logs = await getLogs();
237237
await expect(logs).toEqual([]);
238-
});
238+
}, 60 * 1000);
239239

240240
test('Load an invalid svg from scratch3 as costume', async () => { // eslint-disable-line no-disabled-tests
241241
await loadUri(uri);
@@ -249,7 +249,7 @@ describe('Working with costumes', () => {
249249
const costumeTile = await findByText('corrupt-from-scratch3', scope.costumesTab); // Name from filename
250250
const tileVisible = await costumeTile.isDisplayed();
251251
await expect(tileVisible).toBe(true);
252-
});
252+
}, 60 * 1000);
253253

254254
test('Load an invalid svg from scratch2 as costume', async () => { // eslint-disable-line no-disabled-tests
255255
await loadUri(uri);
@@ -263,5 +263,5 @@ describe('Working with costumes', () => {
263263
const costumeTile = await findByText('scratch2-corrupted', scope.costumesTab); // Name from filename
264264
const tileVisible = await costumeTile.isDisplayed();
265265
await expect(tileVisible).toBe(true);
266-
});
266+
}, 60 * 1000);
267267
});
Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import path from 'path';
21
import SeleniumHelper from '../helpers/selenium-helper';
32

43
const {
@@ -7,12 +6,10 @@ const {
76
findByXpath,
87
notExistsByXpath,
98
getDriver,
10-
getLogs,
11-
loadUri
9+
loadUri,
10+
urlFor
1211
} = new SeleniumHelper();
1312

14-
const uri = path.resolve(__dirname, '../../build/index.html');
15-
1613
let driver;
1714

1815
const trademarkNames = [
@@ -36,39 +33,29 @@ describe('Removed trademarks (ex: Scratch Cat)', () => {
3633
await driver.quit();
3734
});
3835

39-
4036
test('Removed trademark sprites', async () => {
41-
await loadUri(uri);
37+
await loadUri(urlFor('/'));
4238
await clickXpath('//button[@aria-label="Choose a Sprite"]');
4339
const searchElement = await findByXpath("//input[@placeholder='Search']");
4440

4541
for (const name of trademarkNames) {
46-
searchElement.clear();
4742
await searchElement.sendKeys(name);
48-
await new Promise(resolve => setTimeout(resolve, 500));
4943
expect(await notExistsByXpath(`//*[span[text()="${name}"]]`)).toBeTruthy();
44+
searchElement.clear();
5045
}
51-
52-
const logs = await getLogs();
53-
await expect(logs).toEqual([]);
54-
});
46+
}, 60 * 1000);
5547

5648
test('Removed trademark costumes', async () => {
57-
await loadUri(uri);
49+
await loadUri(urlFor('/'));
5850
await clickText('Costumes');
5951
await clickXpath('//button[@aria-label="Choose a Costume"]');
6052
const searchElement = await findByXpath("//input[@placeholder='Search']");
6153

6254
for (const name of trademarkNames) {
63-
searchElement.clear();
6455
const costumePrefix = `${name}-`;
6556
await searchElement.sendKeys(costumePrefix);
66-
await new Promise(resolve => setTimeout(resolve, 500));
6757
expect(await notExistsByXpath(`//*[span[contains(text(), "${costumePrefix}")]]`)).toBeTruthy();
58+
searchElement.clear();
6859
}
69-
70-
const logs = await getLogs();
71-
await expect(logs).toEqual([]);
72-
});
73-
60+
}, 60 * 1000);
7461
});

0 commit comments

Comments
 (0)