Skip to content

Commit

Permalink
Merge pull request #36401 from code-dot-org/test
Browse files Browse the repository at this point in the history
Test
  • Loading branch information
cforkish committed Aug 21, 2020
2 parents 709e774 + fdd97db commit 7f97aed
Show file tree
Hide file tree
Showing 47 changed files with 2,089 additions and 409 deletions.
1 change: 1 addition & 0 deletions apps/Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -716,6 +716,7 @@ describe('entry tests', () => {
// The blockly dependency is huge, so we currently control when it is
// loaded explicitly via script tags rather than via normal imports.
blockly: './src/sites/studio/pages/blockly.js',
googleblockly: './src/sites/studio/pages/googleblockly.js',

// Build embedVideo.js in its own step (skipping factor-bundle) so that
// we don't have to include the large code-studio-common file in the
Expand Down
1 change: 1 addition & 0 deletions apps/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@
"dependencies": {
"@code-dot-org/js-interpreter": "1.3.13",
"@microsoft/immersive-reader-sdk": "^1.1.0",
"blockly": "3.20200625.2",
"crypto-js": "^3.1.9-1",
"details-element-polyfill": "https://github.com/javan/details-element-polyfill",
"filesaver.js": "0.2.0",
Expand Down
1 change: 1 addition & 0 deletions apps/script/checkEntryPoints.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ const SILENCED = [

// other entry points
'blockly',
'googleblockly',
'brambleHost',
'levelbuilder'
];
Expand Down
5 changes: 5 additions & 0 deletions apps/src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -187,3 +187,8 @@ export const TOOLBOX_EDIT_MODE = 'toolbox_blocks';
export const PROFANITY_FOUND = 'profanity_found';

export const NOTIFICATION_ALERT_TYPE = 'notification';

export const BlocklyVersion = {
CDO: 'CDO',
GOOGLE: 'Google'
};
2 changes: 1 addition & 1 deletion apps/src/flappy/blocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ exports.install = function(blockly, blockInstallOptions) {
var skin = blockInstallOptions.skin;
var isK1 = blockInstallOptions.isK1;

var generator = blockly.Generator.get('JavaScript');
var generator = blockly.getGenerator();
blockly.JavaScript = generator;

blockly.Blocks.flappy_whenClick = {
Expand Down
13 changes: 13 additions & 0 deletions apps/src/lib/script-editor/LessonCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,10 @@ export class UnconnectedLessonCard extends Component {
this.setState({levelPosToRemove: null});
};

handleEditLessonDetails = () => {
window.open(`/lessons/${this.props.lesson.id}/edit`, '_blank');
};

handleEditLessonGroup = () => {
this.setState({
editingLessonGroup: true
Expand Down Expand Up @@ -318,6 +322,15 @@ export class UnconnectedLessonCard extends Component {
<i style={{marginRight: 7}} className="fa fa-plus-circle" />
Add Level
</button>
<button
onMouseDown={this.handleEditLessonDetails}
className="btn"
style={styles.addLevel}
type="button"
>
<i style={{marginRight: 7}} className="fa fa-pencil" />
Edit Lesson Details
</button>
{this.props.lessonGroupsCount > 1 && (
<button
onMouseDown={this.handleEditLessonGroup}
Expand Down
16 changes: 16 additions & 0 deletions apps/src/lib/ui/accounts/RemoveParentEmailController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Removes the parent email from the student's account.
*/
export default class RemoveParentEmailController {
/**
* @param {jQuery} form The form which sets the parent email to nil.
* @param {jQuery} link The link the student clicks in order to remove the parent email address from their account.
*/
constructor({form, link}) {
this.form = form;
link.click(event => {
event.preventDefault();
form.submit();
});
}
}
19 changes: 17 additions & 2 deletions apps/src/required_block_utils.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* global Text */

var constants = require('@cdo/apps/constants');
var xml = require('./xml');
var msg = require('@cdo/locale');
var _ = require('lodash');
Expand Down Expand Up @@ -223,8 +224,22 @@ export function elementsEquivalent(expected, given) {
}
// Not fully clear to me why, but blockToDom seems to return us an element
// with a tagName in all caps
if (expected.tagName.toLowerCase() !== given.tagName.toLowerCase()) {
return false;
var expectedTag = expected.tagName.toLowerCase();
var givenTag = given.tagName.toLowerCase();
// Google's Blockly has slightly different tag names, but our validation code
// expects all the tag names to be consistent with CDO Blockly. This mapping
// ensures we will validate correctly in both versions of Blockly.
var equivalentTagNamesMap = {
field: 'title'
};
if (expectedTag !== givenTag) {
if (Blockly.version === constants.BlocklyVersion.GOOGLE) {
if (expectedTag !== equivalentTagNamesMap[givenTag]) {
return false;
}
} else {
return false;
}
}

if (!attributesEquivalent(expected, given)) {
Expand Down
4 changes: 2 additions & 2 deletions apps/src/sites/studio/pages/blockly.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Blockly from '@code-dot-org/blockly';
import CDOBlockly from '@code-dot-org/blockly';
import initializeCdoBlocklyWrapper from './cdoBlocklyWrapper';

window.Blockly = initializeCdoBlocklyWrapper(Blockly);
window.Blockly = initializeCdoBlocklyWrapper(CDOBlockly);
15 changes: 13 additions & 2 deletions apps/src/sites/studio/pages/cdoBlocklyWrapper.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import {BlocklyVersion} from '@cdo/apps/constants';
/**
* Wrapper class for https://github.com/code-dot-org/blockly
* This wrapper will facilitate migrating from CDO Blockly to Google Blockly
* by allowing us to unify the APIs so that we can switch out the underlying Blockly
* object without affecting apps code.
* See also ./googleBlocklyWrapper.js
*/
const BlocklyWrapper = function(blocklyInstance) {
this.version = BlocklyVersion.CDO;
this.blockly_ = blocklyInstance;
this.Msg = this.blockly_.Msg;
this.inject = this.blockly_.inject;
this.wrapReadOnlyProperty = function(propertyName) {
Object.defineProperty(this, propertyName, {
get: function() {
Expand Down Expand Up @@ -102,6 +109,10 @@ function initializeBlocklyWrapper(blocklyInstance) {
blocklyWrapper.wrapSettableProperty('typeHints');
blocklyWrapper.wrapSettableProperty('valueTypeTabShapeMap');

blocklyWrapper.getGenerator = function() {
return blocklyWrapper.Generator.get('JavaScript');
};

return blocklyWrapper;
}

Expand Down
6 changes: 5 additions & 1 deletion apps/src/sites/studio/pages/devise/registrations/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {Provider} from 'react-redux';
import {getStore} from '@cdo/apps/redux';
import MigrateToMultiAuth from '@cdo/apps/lib/ui/accounts/MigrateToMultiAuth';
import AddParentEmailController from '@cdo/apps/lib/ui/accounts/AddParentEmailController';
import RemoveParentEmailController from '@cdo/apps/lib/ui/accounts/RemoveParentEmailController';
import ChangeEmailController from '@cdo/apps/lib/ui/accounts/ChangeEmailController';
import AddPasswordController from '@cdo/apps/lib/ui/accounts/AddPasswordController';
import ChangeUserTypeController from '@cdo/apps/lib/ui/accounts/ChangeUserTypeController';
Expand Down Expand Up @@ -59,7 +60,10 @@ $(document).ready(() => {
link: $('#add-parent-email-link'),
onSuccessCallback: updateDisplayedParentEmail
});

new RemoveParentEmailController({
form: $('#remove-parent-email-form'),
link: $('#remove-parent-email-link')
});
new ChangeEmailController({
form: $('#change-email-modal-form'),
link: $('#edit-email-link'),
Expand Down
224 changes: 224 additions & 0 deletions apps/src/sites/studio/pages/googleBlocklyWrapper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
import {BlocklyVersion} from '@cdo/apps/constants';
/**
* Wrapper class for https://github.com/google/blockly
* This wrapper will facilitate migrating from CDO Blockly to Google Blockly
* by allowing us to unify the APIs so that we can switch out the underlying Blockly
* object without affecting apps code.
* This wrapper will contain all of our customizations to Google Blockly.
* See also ./cdoBlocklyWrapper.js
*/
const BlocklyWrapper = function(blocklyInstance) {
this.version = BlocklyVersion.GOOGLE;
this.blockly_ = blocklyInstance;
this.wrapReadOnlyProperty = function(propertyName) {
Object.defineProperty(this, propertyName, {
get: function() {
return this.blockly_[propertyName];
}
});
};
this.wrapSettableProperty = function(propertyName) {
Object.defineProperty(this, propertyName, {
get: function() {
return this.blockly_[propertyName];
},
set: function(newValue) {
this.blockly_[propertyName] = newValue;
}
});
};
};

function initializeBlocklyWrapper(blocklyInstance) {
const blocklyWrapper = new BlocklyWrapper(blocklyInstance);

blocklyWrapper.wrapReadOnlyProperty('ALIGN_CENTRE');
blocklyWrapper.wrapReadOnlyProperty('ALIGN_LEFT');
blocklyWrapper.wrapReadOnlyProperty('ALIGN_RIGHT');
blocklyWrapper.wrapReadOnlyProperty('applab_locale');
blocklyWrapper.wrapReadOnlyProperty('bindEvent_');
blocklyWrapper.wrapReadOnlyProperty('Block');
blocklyWrapper.wrapReadOnlyProperty('BlockFieldHelper');
blocklyWrapper.wrapReadOnlyProperty('Blocks');
blocklyWrapper.wrapReadOnlyProperty('BlockSvg');
blocklyWrapper.wrapReadOnlyProperty('BlockValueType');
blocklyWrapper.wrapReadOnlyProperty('common_locale');
blocklyWrapper.wrapReadOnlyProperty('Connection');
blocklyWrapper.wrapReadOnlyProperty('contractEditor');
blocklyWrapper.wrapReadOnlyProperty('createSvgElement');
blocklyWrapper.wrapReadOnlyProperty('Css');
blocklyWrapper.wrapReadOnlyProperty('disableVariableEditing');
blocklyWrapper.wrapReadOnlyProperty('FieldAngleDropdown');
blocklyWrapper.wrapReadOnlyProperty('FieldAngleInput');
blocklyWrapper.wrapReadOnlyProperty('FieldAngleTextInput');
blocklyWrapper.wrapReadOnlyProperty('FieldButton');
blocklyWrapper.wrapReadOnlyProperty('FieldColour');
blocklyWrapper.wrapReadOnlyProperty('FieldColourDropdown');
blocklyWrapper.wrapReadOnlyProperty('FieldDropdown');
blocklyWrapper.wrapReadOnlyProperty('FieldIcon');
blocklyWrapper.wrapReadOnlyProperty('FieldImage');
blocklyWrapper.wrapReadOnlyProperty('FieldImageDropdown');
blocklyWrapper.wrapReadOnlyProperty('FieldLabel');
blocklyWrapper.wrapReadOnlyProperty('FieldParameter');
blocklyWrapper.wrapReadOnlyProperty('FieldRectangularDropdown');
blocklyWrapper.wrapReadOnlyProperty('FieldTextInput');
blocklyWrapper.wrapReadOnlyProperty('FieldVariable');
blocklyWrapper.wrapReadOnlyProperty('fireUiEvent');
blocklyWrapper.wrapReadOnlyProperty('fish_locale');
blocklyWrapper.wrapReadOnlyProperty('Flyout');
blocklyWrapper.wrapReadOnlyProperty('FunctionalBlockUtils');
blocklyWrapper.wrapReadOnlyProperty('FunctionalTypeColors');
blocklyWrapper.wrapReadOnlyProperty('FunctionEditor');
blocklyWrapper.wrapReadOnlyProperty('functionEditor');
blocklyWrapper.wrapReadOnlyProperty('gamelab_locale');
blocklyWrapper.wrapReadOnlyProperty('Generator');
blocklyWrapper.wrapReadOnlyProperty('getRelativeXY');
blocklyWrapper.wrapReadOnlyProperty('googlecode');
blocklyWrapper.wrapReadOnlyProperty('hasCategories');
blocklyWrapper.wrapReadOnlyProperty('html');
blocklyWrapper.wrapReadOnlyProperty('inject');
blocklyWrapper.wrapReadOnlyProperty('Input');
blocklyWrapper.wrapReadOnlyProperty('INPUT_VALUE');
blocklyWrapper.wrapReadOnlyProperty('js');
blocklyWrapper.wrapReadOnlyProperty('modalBlockSpace');
blocklyWrapper.wrapReadOnlyProperty('Msg');
blocklyWrapper.wrapReadOnlyProperty('Names');
blocklyWrapper.wrapReadOnlyProperty('netsim_locale');
blocklyWrapper.wrapReadOnlyProperty('Procedures');
blocklyWrapper.wrapReadOnlyProperty('removeChangeListener');
blocklyWrapper.wrapReadOnlyProperty('RTL');
blocklyWrapper.wrapReadOnlyProperty('tutorialExplorer_locale');
blocklyWrapper.wrapReadOnlyProperty('useContractEditor');
blocklyWrapper.wrapReadOnlyProperty('useModalFunctionEditor');
blocklyWrapper.wrapReadOnlyProperty('Variables');
blocklyWrapper.wrapReadOnlyProperty('weblab_locale');
blocklyWrapper.wrapReadOnlyProperty('Workspace');

// These are also wrapping read only properties, but can't use wrapReadOnlyProperty
// because the alias name is not the same as the underlying property name.
Object.defineProperty(blocklyWrapper, 'mainBlockSpace', {
get: function() {
return this.blockly_.mainWorkspace;
}
});
Object.defineProperty(blocklyWrapper, 'mainBlockSpaceEditor', {
get: function() {
return this.blockly_.mainWorkspace;
}
});
Object.defineProperty(blocklyWrapper, 'SVG_NS', {
get: function() {
return this.blockly_.utils.dom.SVG_NS;
}
});

blocklyWrapper.wrapSettableProperty('assetUrl');
blocklyWrapper.wrapSettableProperty('behaviorEditor');
blocklyWrapper.wrapSettableProperty('BROKEN_CONTROL_POINTS');
blocklyWrapper.wrapSettableProperty('BUMP_UNCONNECTED');
blocklyWrapper.wrapSettableProperty('HSV_SATURATION');
blocklyWrapper.wrapSettableProperty('JavaScript');
blocklyWrapper.wrapSettableProperty('readOnly');
blocklyWrapper.wrapSettableProperty('showUnusedBlocks');
blocklyWrapper.wrapSettableProperty('SNAP_RADIUS');
blocklyWrapper.wrapSettableProperty('typeHints');
blocklyWrapper.wrapSettableProperty('valueTypeTabShapeMap');
blocklyWrapper.wrapSettableProperty('Xml');

blocklyWrapper.getGenerator = function() {
return this.JavaScript;
};
blocklyWrapper.findEmptyContainerBlock = function() {}; // TODO
blocklyWrapper.BlockSpace = {
EVENTS: {
MAIN_BLOCK_SPACE_CREATED: 'mainBlockSpaceCreated',
EVENT_BLOCKS_IMPORTED: 'blocksImported',
BLOCK_SPACE_CHANGE: 'blockSpaceChange',
BLOCK_SPACE_SCROLLED: 'blockSpaceScrolled',
RUN_BUTTON_CLICKED: 'runButtonClicked'
},
onMainBlockSpaceCreated: () => {}, // TODO
createReadOnlyBlockSpace: () => {} // TODO
};

// CDO Blockly titles are equivalent to Google Blockly fields.
blocklyWrapper.Block.prototype.getTitles = function() {
let fields = [];
this.inputList.forEach(input => {
input.fieldRow.forEach(field => {
fields.push(field);
});
});
return fields;
};
blocklyWrapper.Block.prototype.getTitleValue =
blocklyWrapper.Block.prototype.getFieldValue;
blocklyWrapper.Block.prototype.isUserVisible = () => false; // TODO
// Google Blockly only allows you to set the hue, not saturation or value.
// TODO: determine if this will work for us, or if there's a workaround to
// allow us to keep our colors the same
blocklyWrapper.Block.prototype.setHSV = function(h, s, v) {
return this.setColour(h);
};

// This function was a custom addition in CDO Blockly, so we need to add it here
// so that our code generation logic still works with Google Blockly
blocklyWrapper.Generator.blockSpaceToCode = function(name, opt_typeFilter) {
let blocksToGenerate = blocklyWrapper.mainBlockSpace.getTopBlocks(
true /* ordered */
);
if (opt_typeFilter) {
if (typeof opt_typeFilter === 'string') {
opt_typeFilter = [opt_typeFilter];
}
blocksToGenerate = blocksToGenerate.filter(block =>
opt_typeFilter.includes(block.type)
);
}
let code = [];
blocksToGenerate.forEach(block => {
code.push(blocklyWrapper.JavaScript.blockToCode(block));
});
return code.join('\n');
};

blocklyWrapper.Input.prototype.appendTitle = function(a, b) {
return this.appendField(a, b);
};

blocklyWrapper.Workspace.prototype.getToolboxWidth = function() {
return blocklyWrapper.mainBlockSpace.getMetrics().toolboxWidth;
};
blocklyWrapper.Workspace.prototype.addUnusedBlocksHelpListener = () => {}; // TODO
blocklyWrapper.Workspace.prototype.getAllUsedBlocks =
blocklyWrapper.Workspace.prototype.getAllBlocks; // TODO
blocklyWrapper.Workspace.prototype.isReadOnly = () => false; // TODO
blocklyWrapper.Workspace.prototype.setEnableToolbox = () => {}; // TODO
blocklyWrapper.Workspace.prototype.blockSpaceEditor = {
blockLimits: {
blockLimitExceeded: () => false, // TODO
getLimit: () => {} // TODO
}
};

// Aliasing Google's blockToDom() so that we can override it, but still be able
// to call Google's blockToDom() in the override function.
blocklyWrapper.Xml.originalBlockToDom = blocklyWrapper.Xml.blockToDom;
blocklyWrapper.Xml.blockToDom = function(block, ignoreChildBlocks) {
const blockXml = blocklyWrapper.Xml.originalBlockToDom(block);
if (ignoreChildBlocks) {
Blockly.Xml.deleteNext(blockXml);
}
return blockXml;
};

blocklyWrapper.Xml = {
...blocklyWrapper.Xml,
domToBlockSpace: blocklyWrapper.Xml.domToWorkspace,
blockSpaceToDom: blocklyWrapper.Xml.workspaceToDom
};

return blocklyWrapper;
}

module.exports = initializeBlocklyWrapper;
Loading

0 comments on commit 7f97aed

Please sign in to comment.