Skip to content

Commit 327481a

Browse files
committed
feat: text2speech extension block
1 parent 2b8056c commit 327481a

File tree

6 files changed

+188
-50
lines changed

6 files changed

+188
-50
lines changed

src/lib/ruby-generator/text2speech.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@
66
export default function (Generator) {
77
Generator.text2speech_speakAndWait = function (block) {
88
const words = Generator.valueToCode(block, 'WORDS', Generator.ORDER_NONE) || null;
9-
return `text2speech_speak(${words})\n`;
9+
return `text2speech.speak(${words})\n`;
1010
};
1111

1212
Generator.text2speech_setVoice = function (block) {
1313
const voice = Generator.valueToCode(block, 'VOICE', Generator.ORDER_NONE);
14-
return `self.text2speech_voice = ${voice}\n`;
14+
return `text2speech.voice = ${voice}\n`;
1515
};
1616

1717
Generator.text2speech_menu_voices = function (block) {
@@ -21,7 +21,7 @@ export default function (Generator) {
2121

2222
Generator.text2speech_setLanguage = function (block) {
2323
const language = Generator.valueToCode(block, 'LANGUAGE', Generator.ORDER_NONE);
24-
return `self.text2speech_language = ${language}\n`;
24+
return `text2speech.language = ${language}\n`;
2525
};
2626

2727
Generator.text2speech_menu_languages = function (block) {

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import BoostConverter from './boost';
3131
import TranslateConverter from './translate';
3232
import MakeyMakeyConverter from './makeymakey';
3333
import VideoConverter from './video';
34+
import Text2SpeechConverter from './text2speech';
3435

3536
const messages = defineMessages({
3637
couldNotConvertPremitive: {
@@ -110,7 +111,8 @@ class RubyToBlocksConverter {
110111
EventConverter,
111112
ControlConverter,
112113
MicroBitConverter,
113-
VideoConverter
114+
VideoConverter,
115+
Text2SpeechConverter
114116
].forEach(x => x.register(this));
115117
}
116118

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import Primitive from './primitive';
2+
3+
const Text2Speech = 'text2speech';
4+
5+
const VoiceMenu = [
6+
'ALTO',
7+
'TENOR',
8+
'SQUEAK',
9+
'GIANT',
10+
'KITTEN'
11+
];
12+
13+
const LanguageMenu = [
14+
'ar',
15+
'zh-cn',
16+
'da',
17+
'nl',
18+
'en',
19+
'fr',
20+
'de',
21+
'hi',
22+
'is',
23+
'it',
24+
'ja',
25+
'ko',
26+
'nb',
27+
'pl',
28+
'pt-br',
29+
'pt',
30+
'ro',
31+
'ru',
32+
'ES',
33+
'es-419',
34+
'sv',
35+
'tr',
36+
'cy'
37+
];
38+
39+
/**
40+
* Text to Speech converter
41+
*/
42+
const Text2SpeechConverter = {
43+
register: function (converter) {
44+
converter.registerCallMethod('self', Text2Speech, 0, params => {
45+
const {node} = params;
46+
47+
return converter.createRubyExpressionBlock(Text2Speech, node);
48+
});
49+
50+
converter.registerCallMethod(Text2Speech, 'speak', 1, params => {
51+
const {receiver, args} = params;
52+
53+
if (!converter.isStringOrBlock(args[0])) return null;
54+
55+
const block = converter.changeRubyExpressionBlock(receiver, 'text2speech_speakAndWait', 'statement');
56+
converter.addTextInput(block, 'WORDS', args[0], 'hello');
57+
return block;
58+
});
59+
60+
converter.registerCallMethod(Text2Speech, 'voice=', 1, params => {
61+
const {receiver, args} = params;
62+
63+
if (!converter.isStringOrBlock(args[0])) return null;
64+
if (converter.isString(args[0])) {
65+
const index = VoiceMenu.indexOf(args[0].toString().toUpperCase());
66+
if (index < 0) return null;
67+
68+
args[0] = new Primitive('str', VoiceMenu[index], args[0].node);
69+
}
70+
71+
const block = converter.changeRubyExpressionBlock(receiver, 'text2speech_setVoice', 'statement');
72+
converter.addFieldInput(
73+
block, 'VOICE', 'text2speech_menu_voices', 'voices', args[0], 'ALTO'
74+
);
75+
return block;
76+
});
77+
78+
converter.registerCallMethod(Text2Speech, 'language=', 1, params => {
79+
const {receiver, args} = params;
80+
81+
if (!converter.isStringOrBlock(args[0])) return null;
82+
if (converter.isString(args[0])) {
83+
const index = LanguageMenu.indexOf(args[0].toString().toLowerCase());
84+
if (index < 0) return null;
85+
86+
args[0] = new Primitive('str', LanguageMenu[index], args[0].node);
87+
}
88+
89+
const block = converter.changeRubyExpressionBlock(receiver, 'text2speech_setLanguage', 'statement');
90+
91+
let defaultLanguage = 'en';
92+
const stage = converter.vm.runtime.getTargetForStage();
93+
if (stage && stage.textToSpeechLanguage) {
94+
defaultLanguage = stage.textToSpeechLanguage;
95+
}
96+
converter.addFieldInput(
97+
block, 'LANGUAGE', 'text2speech_menu_languages', 'languages', args[0], defaultLanguage
98+
);
99+
return block;
100+
});
101+
}
102+
};
103+
104+
export default Text2SpeechConverter;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const VideoStateMenu = [
2424
*/
2525
const VideoConverter = {
2626
register: function (converter) {
27-
converter.registerCallMethod('self', 'video_sensing', 0, params => {
27+
converter.registerCallMethod('self', VideoSensing, 0, params => {
2828
const {node} = params;
2929

3030
return converter.createRubyExpressionBlock(VideoSensing, node);
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import dedent from 'dedent';
2+
import SeleniumHelper from '../../helpers/selenium-helper';
3+
import RubyHelper from '../../helpers/ruby-helper';
4+
5+
const seleniumHelper = new SeleniumHelper();
6+
const {
7+
clickText,
8+
clickXpath,
9+
getDriver,
10+
loadUri,
11+
urlFor
12+
} = seleniumHelper;
13+
14+
const rubyHelper = new RubyHelper(seleniumHelper);
15+
const {
16+
fillInRubyProgram,
17+
currentRubyProgram,
18+
expectInterconvertBetweenCodeAndRuby
19+
} = rubyHelper;
20+
21+
let driver;
22+
23+
describe('Ruby Tab: Text to Speech extension blocks', () => {
24+
beforeAll(() => {
25+
driver = getDriver();
26+
});
27+
28+
afterAll(async () => {
29+
await driver.quit();
30+
});
31+
32+
test('Ruby -> Code -> Ruby', async () => {
33+
await loadUri(urlFor('/'));
34+
35+
const code = dedent`
36+
text2speech.speak("hello")
37+
text2speech.voice = "ALTO"
38+
text2speech.voice = "TENOR"
39+
text2speech.voice = "SQUEAK"
40+
text2speech.voice = "GIANT"
41+
text2speech.voice = "KITTEN"
42+
text2speech.language = "en"
43+
text2speech.language = "ja"
44+
text2speech.language = "de"
45+
`;
46+
await expectInterconvertBetweenCodeAndRuby(code);
47+
});
48+
49+
test('Ruby -> Code -> Ruby (etc) ', async () => {
50+
await loadUri(urlFor('/'));
51+
52+
const beforeRuby = dedent`
53+
text2speech.voice = "alto"
54+
text2speech.voice = "Alto"
55+
text2speech.language = "EN"
56+
text2speech.language = "En"
57+
`;
58+
59+
const afterRuby = dedent`
60+
text2speech.voice = "ALTO"
61+
text2speech.voice = "ALTO"
62+
text2speech.language = "en"
63+
text2speech.language = "en"
64+
`;
65+
66+
await clickText('Ruby', '*[@role="tab"]');
67+
await fillInRubyProgram(beforeRuby);
68+
await clickText('Code', '*[@role="tab"]');
69+
await clickXpath(
70+
'//div[contains(@class, "menu-bar_menu-bar-item") and contains(@class, "menu-bar_hoverable")]' +
71+
'/*/span[text()="Edit"]'
72+
);
73+
await clickText('Generate Ruby from Code');
74+
await clickText('Ruby', '*[@role="tab"]');
75+
expect(await currentRubyProgram()).toEqual(`${afterRuby}\n`);
76+
});
77+
});

test/integration/ruby-tab/extension_text_to_speech.test.js

Lines changed: 0 additions & 45 deletions
This file was deleted.

0 commit comments

Comments
 (0)