Commit aeaaea29 authored by Evan W. Patton's avatar Evan W. Patton Committed by Jeffrey Schiller

Fix missing tooltips in extension blocks (#1804)

* Fix missing tooltips in extension blocks
* Add regression testing for component blocks translation

Change-Id: I524b4639234b51d55b82f84a02abdca0db40a4a7
parent d104f2e8
...@@ -280,7 +280,7 @@ Blockly.Blocks.component_event = { ...@@ -280,7 +280,7 @@ Blockly.Blocks.component_event = {
this.setParameterOrientation(horizParams); this.setParameterOrientation(horizParams);
var tooltipDescription; var tooltipDescription;
if (eventType) { if (eventType) {
tooltipDescription = componentDb.getInternationalizedEventDescription(eventType.name); tooltipDescription = componentDb.getInternationalizedEventDescription(eventType.name, eventType.description);
} }
else { else {
tooltipDescription = componentDb.getInternationalizedEventDescription(this.eventName); tooltipDescription = componentDb.getInternationalizedEventDescription(this.eventName);
...@@ -708,7 +708,7 @@ Blockly.Blocks.component_method = { ...@@ -708,7 +708,7 @@ Blockly.Blocks.component_method = {
var tooltipDescription; var tooltipDescription;
if (methodTypeObject) { if (methodTypeObject) {
tooltipDescription = componentDb.getInternationalizedMethodDescription(methodTypeObject.name); tooltipDescription = componentDb.getInternationalizedMethodDescription(methodTypeObject.name, methodTypeObject.description);
} else { } else {
tooltipDescription = componentDb.getInternationalizedMethodDescription(this.typeName); tooltipDescription = componentDb.getInternationalizedMethodDescription(this.typeName);
} }
...@@ -995,7 +995,7 @@ Blockly.Blocks.component_set_get = { ...@@ -995,7 +995,7 @@ Blockly.Blocks.component_set_get = {
} }
var tooltipDescription; var tooltipDescription;
if (this.propertyName) { if (this.propertyName) {
tooltipDescription = componentDb.getInternationalizedPropertyDescription(this.propertyName); tooltipDescription = componentDb.getInternationalizedPropertyDescription(this.propertyName, this.propertyObject.description);
} else { } else {
tooltipDescription = Blockly.Msg.UNDEFINED_BLOCK_TOOLTIP; tooltipDescription = Blockly.Msg.UNDEFINED_BLOCK_TOOLTIP;
} }
...@@ -1011,7 +1011,7 @@ Blockly.Blocks.component_set_get = { ...@@ -1011,7 +1011,7 @@ Blockly.Blocks.component_set_get = {
thisBlock.propertyObject = thisBlock.getPropertyObject(selection); thisBlock.propertyObject = thisBlock.getPropertyObject(selection);
thisBlock.setTypeCheck(); thisBlock.setTypeCheck();
if (thisBlock.propertyName) { if (thisBlock.propertyName) {
thisBlock.setTooltip(componentDb.getInternationalizedPropertyDescription(thisBlock.propertyName)); thisBlock.setTooltip(componentDb.getInternationalizedPropertyDescription(thisBlock.propertyName, thisBlock.propertyObject.description));
} else { } else {
thisBlock.setTooltip(Blockly.Msg.UNDEFINED_BLOCK_TOOLTIP); thisBlock.setTooltip(Blockly.Msg.UNDEFINED_BLOCK_TOOLTIP);
} }
......
...@@ -498,71 +498,79 @@ Blockly.ComponentDatabase.prototype.getGetterNamesForType = function(typeName) { ...@@ -498,71 +498,79 @@ Blockly.ComponentDatabase.prototype.getGetterNamesForType = function(typeName) {
/** /**
* Get the internationalized string for the given component type. * Get the internationalized string for the given component type.
* @param {!string} name String naming a component type * @param {!string} name String naming a component type
* @param {?string=name} opt_default Optional default value (default: name parameter)
* @returns {string} The localized string if available, otherwise the unlocalized name. * @returns {string} The localized string if available, otherwise the unlocalized name.
*/ */
Blockly.ComponentDatabase.prototype.getInternationalizedComponentType = function(name) { Blockly.ComponentDatabase.prototype.getInternationalizedComponentType = function(name, opt_default) {
return this.i18nComponentTypes_[name] || name; return this.i18nComponentTypes_[name] || opt_default || name;
}; };
/** /**
* Get the internationalized string for the given event name. * Get the internationalized string for the given event name.
* @param {!string} name String naming a component event * @param {!string} name String naming a component event
* @param {?string=name} opt_default Optional default value (default: name parameter)
* @returns {string} The localized string if available, otherwise the unlocalized name. * @returns {string} The localized string if available, otherwise the unlocalized name.
*/ */
Blockly.ComponentDatabase.prototype.getInternationalizedEventName = function(name) { Blockly.ComponentDatabase.prototype.getInternationalizedEventName = function(name, opt_default) {
return this.i18nEventNames_[name] || name; return this.i18nEventNames_[name] || opt_default || name;
}; };
/** /**
* Get the internationalized string for the given event description tooltip. * Get the internationalized string for the given event description tooltip.
* @param {!string} name String naming a component event * @param {!string} name String naming a component event
* @param {?string=name} opt_default Optional default value (default: name parameter)
* @returns {string} The localized string if available, otherwise the unlocalized name. * @returns {string} The localized string if available, otherwise the unlocalized name.
*/ */
Blockly.ComponentDatabase.prototype.getInternationalizedEventDescription = function(name) { Blockly.ComponentDatabase.prototype.getInternationalizedEventDescription = function(name, opt_default) {
return this.i18nEventDescriptions_[name] || name; return this.i18nEventDescriptions_[name] || opt_default || name;
}; };
/** /**
* Get the internationalized string for the given method name. * Get the internationalized string for the given method name.
* @param {!string} name String naming a component method * @param {!string} name String naming a component method
* @param {?string=name} opt_default Optional default value (default: name parameter)
* @returns {string} The localized string if available, otherwise the unlocalized name. * @returns {string} The localized string if available, otherwise the unlocalized name.
*/ */
Blockly.ComponentDatabase.prototype.getInternationalizedMethodName = function(name) { Blockly.ComponentDatabase.prototype.getInternationalizedMethodName = function(name, opt_default) {
return this.i18nMethodNames_[name] || name; return this.i18nMethodNames_[name] || opt_default || name;
}; };
/** /**
* Get the internationalized string for the given method name. * Get the internationalized string for the given method name.
* @param {!string} name String naming a component method * @param {!string} name String naming a component method
* @param {?string=name} opt_default Optional default value (default: name parameter)
* @returns {string} The localized string if available, otherwise the unlocalized name. * @returns {string} The localized string if available, otherwise the unlocalized name.
*/ */
Blockly.ComponentDatabase.prototype.getInternationalizedMethodDescription = function(name) { Blockly.ComponentDatabase.prototype.getInternationalizedMethodDescription = function(name, opt_default) {
return this.i18nMethodDescriptions_[name] || name; return this.i18nMethodDescriptions_[name] || opt_default || name;
}; };
/** /**
* Get the internationalized string for the given parameter name. * Get the internationalized string for the given parameter name.
* @param {!string} name String naming a component event or method parameter * @param {!string} name String naming a component event or method parameter
* @param {?string=name} opt_default Optional default value (default: name parameter)
* @returns {string} The localized string if available, otherwise the unlocalized name. * @returns {string} The localized string if available, otherwise the unlocalized name.
*/ */
Blockly.ComponentDatabase.prototype.getInternationalizedParameterName = function(name) { Blockly.ComponentDatabase.prototype.getInternationalizedParameterName = function(name, opt_default) {
return this.i18nParamNames_[name] || name; return this.i18nParamNames_[name] || opt_default || name;
}; };
/** /**
* Get the internationalized string for the given property name. * Get the internationalized string for the given property name.
* @param {!string} name String naming a component property * @param {!string} name String naming a component property
* @param {?string=name} opt_default Optional default value (default: name parameter)
* @returns {string} The localized string if available, otherwise the unlocalized name. * @returns {string} The localized string if available, otherwise the unlocalized name.
*/ */
Blockly.ComponentDatabase.prototype.getInternationalizedPropertyName = function(name) { Blockly.ComponentDatabase.prototype.getInternationalizedPropertyName = function(name, opt_default) {
return this.i18nPropertyNames_[name] || name; return this.i18nPropertyNames_[name] || opt_default || name;
}; };
/** /**
* Get the internationalized string for the given property description tooltip. * Get the internationalized string for the given property description tooltip.
* @param {!string} name String naming a component property * @param {!string} name String naming a component property
* @param {?string=name} opt_default Optional default value (default: name parameter)
* @returns {string} The localized string if available, otherwise the unlocalized name. * @returns {string} The localized string if available, otherwise the unlocalized name.
*/ */
Blockly.ComponentDatabase.prototype.getInternationalizedPropertyDescription = function(name) { Blockly.ComponentDatabase.prototype.getInternationalizedPropertyDescription = function(name, opt_default) {
return this.i18nPropertyDescriptions_[name] || name; return this.i18nPropertyDescriptions_[name] || opt_default || name;
}; };
...@@ -460,6 +460,66 @@ function doTheyMatch(expected, given) { ...@@ -460,6 +460,66 @@ function doTheyMatch(expected, given) {
return false; return false;
} }
/**
* Creates a Blockly block of the given type with the given mutation.
*
* @param {!string} type The type of the block to create, e.g., component_event
* @param {?string=} mutation Any mutation information required for the block
* @returns {!Blockly.Block} A freshly constructed Blockly block.
*/
function blockFromMutation(type, mutation) {
mutation = mutation || ''
var block_text = "<xml><block type='" + type + "'>" + mutation + "</block></xml>";
var block_xml = Blockly.Xml.textToDom(block_text);
return Blockly.Xml.domToBlock(block_xml.firstElementChild, Blockly.mainWorkspace);
}
/**
* Gets the rendered name of an event block.
*
* @param {!Blockly.Block} block An instance of a component_event block
* @returns {string} The name shown on the block (after the period)
*/
function getEventBlockPresentedName(block) {
return block.inputList[0].fieldRow[2].getText().substring(1); // strip the leading .
}
/**
* Gets the rendered name of a method block.
*
* @param {!Blockly.Block} block An instance of a component_method block
* @returns {string} The name shown on the block (after the period)
*/
function getMethodBlockPresentedName(block) {
return block.inputList[0].fieldRow[2].getText().substring(1); // strip the leading .
}
/**
* Gets the rendered name of a property block.
*
* @param {!Blockly.Block} block An instance of a component_set_get block
* @returns {string} The name shown on the block (after the period)
*/
function getPropertyBlockPresentedName(block) {
if (block.set_or_get === 'get') {
return block.inputList[0].fieldRow[3].getText();
} else {
return block.inputList[0].fieldRow[2].getText();
}
}
/**
* Asserts that two values are identical using JavaScript strict comparison.
*
* @param {*} expected the expected value
* @param {*} given the actual value
*/
function assertEquals(expected, given) {
if (expected !== given) {
throw Error('Expected ' + JSON.stringify(expected) + '; Got ' + JSON.stringify(given));
}
}
// Check if a string contains a pattern, ignoring whitespace // Check if a string contains a pattern, ignoring whitespace
function doesContain(data, pattern) { function doesContain(data, pattern) {
......
...@@ -3,10 +3,10 @@ ...@@ -3,10 +3,10 @@
// http://www.apache.org/licenses/LICENSE-2.0 // http://www.apache.org/licenses/LICENSE-2.0
package com.google.appinventor.blocklyeditor; package com.google.appinventor.blocklyeditor;
import java.io.IOException;
import com.google.appinventor.blocklyeditor.BlocklyTestUtils;
import com.google.appinventor.common.testutils.TestUtils; import com.google.appinventor.common.testutils.TestUtils;
import com.google.appinventor.components.common.YaVersion; import com.google.appinventor.components.common.YaVersion;
import java.io.IOException;
import junit.framework.TestCase; import junit.framework.TestCase;
/** /**
...@@ -261,5 +261,19 @@ public class BlocklyEvalTest extends TestCase { ...@@ -261,5 +261,19 @@ public class BlocklyEvalTest extends TestCase {
assertEquals("true", result.toString()); assertEquals("true", result.toString());
} }
/**
* Runs regression tests for internationalization functionality on the component database.
*
* @throws IOException if running the test is unable to execute
*/
public void testTooltipInternationalization() throws IOException {
String[] params = {
"phantomjs",
testpath + "/tests/com/google/appinventor/blocklyeditor/component_database_tests.js"
};
String result = CodeBlocksProcessHelper.exec(params, true).trim();
assertEquals("true", result);
}
} }
// -*- mode: javascript; js-indent-level: 2; -*-
// Copyright © 2019 Massachusetts Institute of Technology, All rights reserved.
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0
// PhantomJS page object to open and load an URL
var page = require('webpage').create();
// Some debugging from PhantomJS
page.onConsoleMessage = function (msg) { console.log(msg); };
page.onError = function (msg, trace) {
console.log(msg);
trace.forEach(function(item) {
console.log(' ', item.file, ':', item.line);
})
};
// Open the actual page and load all the JavaScript in it
// If success is true, all went well
page.open('src/demos/yail/yail_testing_index.html', function(status) {
if (status !== 'success') {
console.log('load unsuccessful');
return false;
}
// Evaluate the following:
var passed = page.evaluate(function() {
var db = Blockly.mainWorkspace.getComponentDatabase();
// Register a fake component that we will use for testing.
db.populateTypes(/** @type {ComponentInfo[]} */ [{
name: 'TestExtension',
external: 'true',
version: '1',
categoryString: 'UNINITIALIZED',
helpString: 'TestExtension',
showOnPalette: 'true',
nonVisible: 'true',
iconName: '',
events: [
{name: 'TranslatedEvent', description: 'This is a translated test.', deprecated: false, parameters: []},
{name: 'UntranslatedEvent', description: 'This is an untranslated test.', deprecated: false, parameters: []}
],
methods: [
{name: 'TranslatedMethod', description: 'This is a translated test.', deprecated: false, parameters: []},
{name: 'UntranslatedMethod', description: 'This is an untranslated test.', deprecated: false, parameters: []}
],
blockProperties: [
{name: 'TranslatedProperty', description: 'This is a translated test.', deprecated: true, rw: 'read-write'},
{name: 'UntranslatedProperty', description: 'This is an untranslated test.', deprecated: false, rw: 'read-write'}
],
properties: [
{name: 'TranslatedProperty', type: 'text'},
{name: 'UntranslatedProperty', type: 'text'}
]
}]);
// Translate some of the component.
db.populateTranslations({
'EVENT-TranslatedEvent': 'SuccessfulEvent',
'METHOD-TranslatedMethod': 'SuccessfulMethod',
'PROPERTY-TranslatedProperty': 'SuccessfulProperty',
'EVENTDESC-TranslatedEvent': 'Successfully translated event test.',
'METHDESC-TranslatedMethod': 'Successfully translated method test.',
'PROPDESC-TranslatedProperty': 'Successfully translated property test.'
});
var block;
block = blockFromMutation('component_event', '<mutation component_type="TestExtension" event_name="TranslatedEvent" is_generic="false" />');
// Event name should be translated
assertEquals('SuccessfulEvent', getEventBlockPresentedName(block));
// Event tooltip should be translated
assertEquals('Successfully translated event test.', block.tooltip);
block = blockFromMutation('component_event', '<mutation component_type="TestExtension" event_name="UntranslatedEvent" is_generic="false" />');
// Event name should not be translated
assertEquals('UntranslatedEvent', getEventBlockPresentedName(block));
// Event tooltip should not be translated
assertEquals('This is an untranslated test.', block.tooltip);
block = blockFromMutation('component_method', '<mutation component_type="TestExtension" method_name="TranslatedMethod" is_generic="false" />');
// Method name should be translated
assertEquals('SuccessfulMethod', getMethodBlockPresentedName(block));
// Method tooltip should be translated
assertEquals('Successfully translated method test.', block.tooltip);
block = blockFromMutation('component_method', '<mutation component_type="TestExtension" method_name="UntranslatedMethod" is_generic="false" />');
// Method name should not be translated
assertEquals('UntranslatedMethod', getMethodBlockPresentedName(block));
// Method tooltip should not be translated
assertEquals('This is an untranslated test.', block.tooltip);
block = blockFromMutation('component_set_get', '<mutation component_type="TestExtension" property_name="TranslatedProperty" set_or_get="get" is_generic="false" />');
// Property name should be translated
assertEquals('SuccessfulProperty', getPropertyBlockPresentedName(block));
// Property tooltip should be translated
assertEquals('Successfully translated property test.', block.tooltip);
block = blockFromMutation('component_set_get', '<mutation component_type="TestExtension" property_name="UntranslatedProperty" set_or_get="get" is_generic="false" />');
// Property name should not be translated
assertEquals('UntranslatedProperty', getPropertyBlockPresentedName(block));
// Property tooltip should not be translated
assertEquals('This is an untranslated test.', block.tooltip);
return true;
});
//This is the actual result of the test
console.log(passed);
//Exit the phantom process
phantom.exit();
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment