Skip to content

Commit

Permalink
Merge pull request #20752 from code-dot-org/staging
Browse files Browse the repository at this point in the history
DTT (Staging > Test) [robo-dtt]
  • Loading branch information
deploy-code-org committed Feb 20, 2018
2 parents f83ffc4 + bdddd23 commit 6a5118a
Show file tree
Hide file tree
Showing 16 changed files with 254 additions and 55 deletions.
82 changes: 75 additions & 7 deletions apps/src/dropletUtils.js
Expand Up @@ -519,21 +519,89 @@ export function generateAceApiCompleter(functionFilter, dropletConfig) {

return {
getCompletions(editor, session, pos, prefix, callback) {
// Ensure our updateCompletionsOverride is installed - note that we must wait
// until a completor object has been created to install the hook. This
// getCompletions() function will be called from within the original
// updateCompletions() call, which is ok to run without our hook. We need
// to make sure subsequent calls to updateCompletions() are overriden
installAceUpdateCompletionsHook(editor);
const token = editor.session.getTokenAt(pos.row, pos.column);
if (prefix.length === 0 || token.type === 'comment') {
// Ignore cases where:
// * the prefix is empty
// * we are in a comment
// * the prefix is a number with less than 3 digits (matches Atom semantics)
if (prefix.length === 0 || token.type === 'comment' ||
(prefix.length < 3 && !isNaN(Number(prefix)))) {
callback(null, []);
return;
}
if (isPositionAfterDot(session, pos)) {
// Following a dot, we autocomplete from methodsAndProperties:
callback(null, methodsAndProperties);
} else {
callback(null, apis);
}
// Following a dot, we autocomplete from methodsAndProperties:
const list = isPositionAfterDot(session, pos) ? methodsAndProperties : apis;

// Filter our list to contain substring word matches:
const filteredList = filterListBasedOnWordMatches(prefix, list);
callback(null, filteredList);
}
};
}

function filterListBasedOnWordMatches(prefix, list) {
// Filter our list to contain substring word matches based on camelCase,
// snake_case or Global.method:
const modifiedPrefix = prefix.replace(/_|\./g, '').toLowerCase();
return list.filter(completion => {
const { value } = completion;
// https://stackoverflow.com/a/34680912
const edges = /([A-Z](?=[A-Z][a-z])|[^A-Z](?=[A-Z])|[a-zA-Z](?=[^a-zA-Z]))/g;
const words = value.replace('.', '_').replace(edges, '$1_').split('_');
// Transform words into phrases that we consider to be "matches": e.g.
// words ['get', 'Time'] become phrases ['getTime', 'Time']
const phrases = words.map((word, index) => words.slice(index).join(''));
for (const phrase of phrases) {
if (phrase.toLowerCase().indexOf(modifiedPrefix) === 0) {
return completion;
}
}
});
}

/**
* Install our own updateCompletions method to override the default ace
* behavior so that we can modify the filtering behavior
* @param {AceEditor} editor
*/
function installAceUpdateCompletionsHook(editor) {
if (editor.completer.updateCompletions !== updateCompletionsOverride) {
originalUpdateCompletions = editor.completer.updateCompletions;
editor.completer.updateCompletions = updateCompletionsOverride;
}
}

var originalUpdateCompletions;

/**
* Our overridden updateCompletions method, installed so that we can modify the
* filtering behavior
* @param {completor} this
* @param {boolean} keepPopupPosition
*/
function updateCompletionsOverride(keepPopupPosition) {
if (keepPopupPosition && this.base && this.completions) {
const pos = this.editor.getCursorPosition();
const prefix = this.editor.session.getTextRange({start: this.base, end: pos});

// Repopulate all 3 properties of the FilteredList (stored in this.completions)
// with a new list, filtered by our algorithm, but not yet filtered by ace's
// algorithm. Ensure that filterText is blank so that the original
// updateCompletions() won't just return immediately

this.completions.all = filterListBasedOnWordMatches(prefix, this.completions.all);
this.completions.filtered = this.completions.all;
this.completions.filterText = "";
}
return originalUpdateCompletions.call(this, keepPopupPosition);
}

/**
* Given a droplet config, create a mode option functions object
* @param {object} config
Expand Down
2 changes: 1 addition & 1 deletion apps/src/gamelab/AnimationPicker/animationPickerModule.js
Expand Up @@ -114,7 +114,7 @@ export function handleUploadComplete(result) {
loadImageMetadata(sourceUrl, metadata => {
const animation = _.assign({}, metadata, {
name: uploadFilename,
sourceUrl: null,
sourceUrl: sourceUrl,
size: result.size,
version: result.versionId
});
Expand Down
16 changes: 7 additions & 9 deletions apps/src/gamelab/animationListModule.js
Expand Up @@ -707,17 +707,15 @@ export function animationSourceUrl(key, props, withVersion = false) {

// 1. If the animation has a sourceUrl it's external (from the library
// or some other outside source, not the animation API) - and we may need
// to run it through the media proxy. (Note - Before 02/2018 -
// uploaded images may/may not have non-null sourceUrls. After 02/2018 -
// uploaded images will have null sourceUrls)
// to run it through the media proxy.
if (props.sourceUrl) {
return assetPrefix.fixPath(props.sourceUrl);
}

// 2. Otherwise it's local to this project, and we should use the animation
// key to look it up in the animations API.
let url = (props.sourceUrl) ?
assetPrefix.fixPath(props.sourceUrl) : (animationsApi.basePath(key) + '.png');

// Appending version here to support projects with uploaded images
// with sourceUrls.
return url + ((props.version) ? '?version=' + props.version : '');
return animationsApi.basePath(key) + '.png' +
((withVersion && props.version) ? '?version=' + props.version : '');
}

/**
Expand Down
6 changes: 5 additions & 1 deletion apps/src/templates/studioHomepages/TeacherHomepage.jsx
Expand Up @@ -2,6 +2,7 @@ import React, {PropTypes, Component} from 'react';
import ReactDOM from 'react-dom';
import $ from 'jquery';
import HeaderBanner from '../HeaderBanner';
import {SpecialAnnouncementActionBlock} from './TwoColumnActionBlock';
import RecentCourses from './RecentCourses';
import TeacherSections from './TeacherSections';
import StudentSections from './StudentSections';
Expand Down Expand Up @@ -132,7 +133,10 @@ export default class TeacherHomepage extends Component {
<ProtectedStatefulDiv
ref="termsReminder"
/>
{this.state.showCensusBanner && isEnglish && (
{(isEnglish &&
<SpecialAnnouncementActionBlock/>
)}
{this.state.showCensusBanner && (
<div>
<CensusTeacherBanner
ref={this.bindCensusBanner}
Expand Down
111 changes: 109 additions & 2 deletions apps/test/integration/levelSolutions/applab/ec_math.js
Expand Up @@ -97,19 +97,90 @@ module.exports = {
},

{
description: 'randomNumber shows up in autocomplete',
description: 'randomNumber and Math.random show up in autocomplete by matching ran',
editCode: true,
xml: '',
runBeforeClick: function (assert) {
$("#show-code-header").click();
assert.equal($(".ace_autocomplete").is(":visible"), false,
'no autocomplete to start');

testUtils.typeAceText('randomNumb');
testUtils.typeAceText('ran');
assert.equal($(".ace_autocomplete").is(":visible"), true,
'we have autocomplete options after typing');
assert.equal(/randomNumber/.test($(".ace_autocomplete").text()), true,
'our autocomplete options contain randomNumber');
assert.equal(/Math.random/.test($(".ace_autocomplete").text()), true,
'our autocomplete options contain Math.random');
assert.equal(/createCanvas/.test($(".ace_autocomplete").text()), false,
'our autocomplete options do not contain createCanvas');

// clear contents before run
testUtils.setAceText('');

tickWrapper.runOnAppTick(Applab, 2, function () {
Applab.onPuzzleComplete();
});
},
customValidator: function (assert) {
// No errors in output console
var debugOutput = document.getElementById('debug-output');
assert.equal(debugOutput.textContent, "");
return true;
},
expected: {
result: true,
testResult: TestResults.FREE_PLAY
}
},

{
description: 'randomNumber shows up in autocomplete by matching Numb',
editCode: true,
xml: '',
runBeforeClick: function (assert) {
$("#show-code-header").click();
assert.equal($(".ace_autocomplete").is(":visible"), false,
'no autocomplete to start');

testUtils.typeAceText('Numb');
assert.equal($(".ace_autocomplete").is(":visible"), true,
'we have autocomplete options after typing');
assert.equal(/randomNumber/.test($(".ace_autocomplete").text()), true,
'our autocomplete options contain randomNumber');

// clear contents before run
testUtils.setAceText('');

tickWrapper.runOnAppTick(Applab, 2, function () {
Applab.onPuzzleComplete();
});
},
customValidator: function (assert) {
// No errors in output console
var debugOutput = document.getElementById('debug-output');
assert.equal(debugOutput.textContent, "");
return true;
},
expected: {
result: true,
testResult: TestResults.FREE_PLAY
}
},

{
description: 'randomNumber does not show up in autocomplete when typing omNumb',
editCode: true,
xml: '',
runBeforeClick: function (assert) {
$("#show-code-header").click();
assert.equal($(".ace_autocomplete").is(":visible"), false,
'no autocomplete to start');

testUtils.typeAceText('omNumb');

assert.equal($(".ace_autocomplete").is(":visible"), false,
'no autocomplete after typing');

// clear contents before run
testUtils.setAceText('');
Expand Down Expand Up @@ -193,6 +264,42 @@ module.exports = {
result: true,
testResult: TestResults.FREE_PLAY
}
},

{
description: 'Math.min and Math.max shows up in autocomplete by matching math',
editCode: true,
xml: '',
runBeforeClick: function (assert) {
$("#show-code-header").click();
assert.equal($(".ace_autocomplete").is(":visible"), false,
'no autocomplete to start');

testUtils.typeAceText('math');
assert.equal($(".ace_autocomplete").is(":visible"), true,
'we have autocomplete options after typing');
assert.equal(/Math.min/.test($(".ace_autocomplete").text()), true,
'our autocomplete options contain Math.min');
assert.equal(/Math.max/.test($(".ace_autocomplete").text()), true,
'our autocomplete options contain Math.max');

// clear contents before run
testUtils.setAceText('');

tickWrapper.runOnAppTick(Applab, 2, function () {
Applab.onPuzzleComplete();
});
},
customValidator: function (assert) {
// No errors in output console
var debugOutput = document.getElementById('debug-output');
assert.equal(debugOutput.textContent, "");
return true;
},
expected: {
result: true,
testResult: TestResults.FREE_PLAY
}
}
]
};
10 changes: 0 additions & 10 deletions apps/test/unit/gamelab/animationListModuleTest.js
Expand Up @@ -52,16 +52,6 @@ describe('animationListModule', function () {
const props = {sourceUrl: null, version: 'baz'};
expect(animationSourceUrl(key, props, true)).to.equal('/v3/animations/fake_id/foo.png?version=baz');
});

it(`appends version query param if props has a version id and no version flag is passed`, function () {
const props = {sourceUrl: null, version: 'baz'};
expect(animationSourceUrl(key, props)).to.equal('/v3/animations/fake_id/foo.png?version=baz');
});

it(`appends version query param if props has a version id and sourceURL`, function () {
const props = {sourceUrl: 'bar', version: 'baz'};
expect(animationSourceUrl(key, props)).to.equal('bar?version=baz');
});
});

describe('loadAnimationFromSource', function () {
Expand Down
6 changes: 6 additions & 0 deletions dashboard/app/views/layouts/_analytics.html.haml
Expand Up @@ -29,3 +29,9 @@
function trackEvent(category, action, label, value) {
ga('send', 'event', category, action, label, value);
}

(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-TZZBRK5');
7 changes: 3 additions & 4 deletions lib/cdo/aws/s3_packaging.rb
Expand Up @@ -47,10 +47,9 @@ def update_from_s3
return true
end

# creates a package from the given assets location and upload it to s3
# @return tempfile object of package
def upload_package_to_s3(sub_path)
package = create_package(sub_path)
# Uploads the created package to s3
# @return package
def upload_package_to_s3(package)
raise "Generated different package for same contents" unless package_matches_download(package)
upload_package(package)
package
Expand Down
17 changes: 12 additions & 5 deletions lib/rake/package.rake
Expand Up @@ -32,15 +32,22 @@ namespace :package do

ChatClient.wrap('Building apps') {Rake::Task['build:apps'].invoke}

# Check that building apps did not generate unexpected changes either.
Rake::Task['circle:check_for_unexpected_apps_changes'].invoke
unless rack_env?(:adhoc)
# Check that building apps did not generate unexpected changes either.
Rake::Task['circle:check_for_unexpected_apps_changes'].invoke

ChatClient.wrap('Testing apps') {Rake::Task['test:apps'].invoke}
ChatClient.wrap('Testing apps') {Rake::Task['test:apps'].invoke}
end

# upload to s3
packager = apps_packager
package = packager.upload_package_to_s3('/build/package')
ChatClient.log "Uploaded apps package to S3: #{packager.commit_hash}"
package = packager.create_package('/build/package')

unless rack_env?(:adhoc)
packager.upload_package_to_s3(package)
ChatClient.log "Uploaded apps package to S3: #{packager.commit_hash}"
end

packager.decompress_package(package)
end

Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions pegasus/sites.v3/code.org/public/index.haml
Expand Up @@ -22,6 +22,7 @@ style_min: true
= view :homepage_hero

- if request.language == "en"
= view :homepage_professional_learning_2018
= view :stats_homepage
- else
#petitionexpand{style: "display: block"}
Expand Down
2 changes: 1 addition & 1 deletion pegasus/sites.v3/code.org/views/footer.haml
Expand Up @@ -35,4 +35,4 @@
%i.fa.fa-instagram.fa-lg
&nbsp; &nbsp;
%a.whitefooterlink{:href=>"https://medium.com/@codeorg", :target=>"_blank", :style=>'text-decoration: none;'}
%i.fa.fa-tumblr.fa-lg
%img{src: '/images/icons/medium-monogram-white.png', style: "width: 20px"}
@@ -0,0 +1,24 @@
= inline_css 'homepage_professional_learning_2018.css'

.homepage-professional-learning-2018-banner
%a{href: "/educate/professional-learning-2018"}
.nonphone.col-80.tablet-feature
.left.col-80.animateSlideInFromLeft
.banner
%img{src: "/images/homepage/professional-learning-2018-banner.png"}
.text
2018 Professional Learning for
%br/
Middle and High School is now available
.right.col-20
%button
Join us
.phone.phone-feature
.text
2018 Professional Learning for
%br/
Middle and High School is now available
%button
Join us
.clear

0 comments on commit 6a5118a

Please sign in to comment.