Unverified Commit 805224dd authored by Colby Gutierrez-Kraybill's avatar Colby Gutierrez-Kraybill Committed by GitHub

Merge branch 'develop' into hotfix/langauges_fix

parents 4895425c fb28558c
...@@ -42,23 +42,43 @@ async.auto({ ...@@ -42,23 +42,43 @@ async.auto({
// on any of those route conditions. // on any of those route conditions.
var notPassStatement = fastlyConfig.getAppRouteCondition('../build/*', routes, extraAppRoutes, __dirname); var notPassStatement = fastlyConfig.getAppRouteCondition('../build/*', routes, extraAppRoutes, __dirname);
// For all the routes in routes.json, construct a varnish-style regex that matches
// only if NONE of those routes are matched.
var passStatement = fastlyConfig.negateConditionStatement(notPassStatement);
// For a non-pass condition, point backend at s3 // For a non-pass condition, point backend at s3
var backendCondition = fastlyConfig.setBackend( var recvCondition = '' +
'F_s3', 'if (' + notPassStatement + ') {\n' +
S3_BUCKET_NAME, ' set req.backend = F_s3;\n' +
notPassStatement ' set req.http.host = \"' + S3_BUCKET_NAME + '\";\n' +
); '} else {\n' +
// For a pass condition, set forwarding headers ' if (!req.http.Fastly-FF) {\n' +
var forwardCondition = fastlyConfig.setForwardHeaders(passStatement); ' if (req.http.X-Forwarded-For) {\n' +
' set req.http.Fastly-Temp-XFF = req.http.X-Forwarded-For \", \" client.ip;\n' +
' } else {\n' +
' set req.http.Fastly-Temp-XFF = client.ip;\n' +
' }\n' +
' } else {\n' +
' set req.http.Fastly-Temp-XFF = req.http.X-Forwarded-For;\n' +
' }\n' +
' set req.grace = 60s;\n' +
' if (req.http.Cookie:scratchlanguage) {\n' +
' set req.http.Accept-Language = req.http.Cookie:scratchlanguage;\n' +
' } else {\n' +
' set req.http.Accept-Language = accept.language_lookup("' +
Object.keys(languages).join(':') + '", ' +
'"en", ' +
'std.tolower(req.http.Accept-Language)' +
');\n' +
' }\n' +
' if (req.url ~ "^/projects/" && !req.http.Cookie:scratchsessionid) {\n' +
' set req.http.Cookie = req.http.Cookie:scratchlanguage;\n' +
' } else {\n' +
' return(pass);\n' +
' }\n' +
'}\n';
fastly.setCustomVCL( fastly.setCustomVCL(
results.version, results.version,
'recv-condition', 'recv-condition',
backendCondition + forwardCondition, recvCondition,
cb cb
); );
}], }],
......
...@@ -84,45 +84,6 @@ var FastlyConfigMethods = { ...@@ -84,45 +84,6 @@ var FastlyConfigMethods = {
return 'redirects/' + route.pattern; return 'redirects/' + route.pattern;
}, },
/**
* Returns custom vcl configuration as a string for setting the backend
* of a request to the given backend/host.
*
* @param {string} backend name of the backend declared in fastly
* @param {string} host name of the s3 bucket to be set as the host
* @param {string} condition condition under which backend should be set
*/
setBackend: function (backend, host, condition) {
return '' +
'if (' + condition + ') {\n' +
' set req.backend = ' + backend + ';\n' +
' set req.http.host = \"' + host + '\";\n' +
'}\n';
},
/**
* Returns custom vcl configuration as a string for headers that
* should be added for the condition in which a request is forwarded.
*
* @param {string} condition condition under which to set pass headers
*/
setForwardHeaders: function (condition) {
return '' +
'if (' + condition + ') {\n' +
' if (!req.http.Fastly-FF) {\n' +
' if (req.http.X-Forwarded-For) {\n' +
' set req.http.Fastly-Temp-XFF = req.http.X-Forwarded-For \", \" client.ip;\n' +
' } else {\n' +
' set req.http.Fastly-Temp-XFF = client.ip;\n' +
' }\n' +
' } else {\n' +
' set req.http.Fastly-Temp-XFF = req.http.X-Forwarded-For;\n' +
' }\n' +
' set req.grace = 60s;\n' +
' return(pass);\n' +
'}\n';
},
/** /**
* Returns custom vcl configuration as a string that sets the varnish * Returns custom vcl configuration as a string that sets the varnish
* Time to Live (TTL) for responses that come from s3. * Time to Live (TTL) for responses that come from s3.
...@@ -132,9 +93,13 @@ var FastlyConfigMethods = { ...@@ -132,9 +93,13 @@ var FastlyConfigMethods = {
setResponseTTL: function (condition) { setResponseTTL: function (condition) {
return '' + return '' +
'if (' + condition + ') {\n' + 'if (' + condition + ') {\n' +
' if (req.url ~ "^/projects/" && !req.http.Cookie:scratchsessionid) {\n' +
' set beresp.http.Vary = "Accept-Encoding, Accept-Language";\n' +
' } else {\n' +
' set beresp.ttl = 0s;\n' + ' set beresp.ttl = 0s;\n' +
' set beresp.grace = 0s;\n' + ' set beresp.grace = 0s;\n' +
' return(pass);\n' + ' return(pass);\n' +
' }\n' +
'}\n'; '}\n';
} }
}; };
......
...@@ -32,7 +32,8 @@ var Grid = React.createClass({ ...@@ -32,7 +32,8 @@ var Grid = React.createClass({
if (this.props.itemType == 'projects') { if (this.props.itemType == 'projects') {
return ( return (
<Thumbnail key={key} <Thumbnail
key={key}
showLoves={this.props.showLoves} showLoves={this.props.showLoves}
showFavorites={this.props.showFavorites} showFavorites={this.props.showFavorites}
showRemixes={this.props.showRemixes} showRemixes={this.props.showRemixes}
...@@ -42,23 +43,30 @@ var Grid = React.createClass({ ...@@ -42,23 +43,30 @@ var Grid = React.createClass({
href={href} href={href}
title={item.title} title={item.title}
src={item.image} src={item.image}
avatar={'https://cdn2.scratch.mit.edu/get_image/user/' avatar={
+ item.author.id + '_32x32.png'} 'https://uploads.scratch.mit.edu/users/avatars/' +
item.author.id +
'.png'
}
creator={item.author.username} creator={item.author.username}
loves={item.stats.loves} loves={item.stats.loves}
favorites={item.stats.favorites} favorites={item.stats.favorites}
remixes={item.stats.remixes} remixes={item.stats.remixes}
views={item.stats.views} /> views={item.stats.views}
/>
); );
} }
else { else {
return ( return (
<Thumbnail key={key} <Thumbnail
key={key}
type={'gallery'} type={'gallery'}
href={href} href={href}
title={item.title} title={item.title}
src={item.image} src={item.image}
owner={item.owner} /> srcDefault={'https://uploads.scratch.mit.edu/galleries/thumbnails/default.png'}
owner={item.owner}
/>
); );
} }
}.bind(this))} }.bind(this))}
......
...@@ -11,7 +11,7 @@ var Navigation = React.createClass({ ...@@ -11,7 +11,7 @@ var Navigation = React.createClass({
<NavigationBox> <NavigationBox>
<ul className="ul mod-2018"> <ul className="ul mod-2018">
<li className="li-left mod-logo mod-2018"> <li className="li-left mod-logo mod-2018">
<a href="/conference" className="logo-a"> <a href="/" className="logo-a">
<img <img
src="/images/logo_sm.png" src="/images/logo_sm.png"
alt="Scratch Logo" alt="Scratch Logo"
......
...@@ -8,12 +8,20 @@ var Thumbnail = React.createClass({ ...@@ -8,12 +8,20 @@ var Thumbnail = React.createClass({
propTypes: { propTypes: {
src: React.PropTypes.string src: React.PropTypes.string
}, },
getInitialState: function () {
return {
srcFallback: false,
avatarFallback: false
};
},
getDefaultProps: function () { getDefaultProps: function () {
return { return {
href: '#', href: '#',
title: 'Project', title: 'Project',
src: '', src: '',
srcDefault: 'https://uploads.scratch.mit.edu/projects/thumbnails/default.png',
avatar: '', avatar: '',
avatarDefault: 'https://uploads.scratch.mit.edu/users/avatars/default.png',
type: 'project', type: 'project',
showLoves: false, showLoves: false,
showFavorites: false, showFavorites: false,
...@@ -24,6 +32,12 @@ var Thumbnail = React.createClass({ ...@@ -24,6 +32,12 @@ var Thumbnail = React.createClass({
alt: '' alt: ''
}; };
}, },
handleSrcError: function () {
this.setState({srcFallback: true});
},
handleAvatarError: function () {
this.setState({avatarFallback: true});
},
render: function () { render: function () {
var classes = classNames( var classes = classNames(
'thumbnail', 'thumbnail',
...@@ -58,7 +72,8 @@ var Thumbnail = React.createClass({ ...@@ -58,7 +72,8 @@ var Thumbnail = React.createClass({
<div <div
key="remixes" key="remixes"
className="thumbnail-remixes" className="thumbnail-remixes"
title={this.props.remixes + ' remixes'}> title={this.props.remixes + ' remixes'}
>
{this.props.remixes} {this.props.remixes}
</div> </div>
); );
...@@ -68,19 +83,48 @@ var Thumbnail = React.createClass({ ...@@ -68,19 +83,48 @@ var Thumbnail = React.createClass({
<div <div
key="views" key="views"
className="thumbnail-views" className="thumbnail-views"
title={this.props.views + ' views'}> title={this.props.views + ' views'}
>
{this.props.views} {this.props.views}
</div> </div>
); );
} }
var imgElement,titleElement,avatarElement;
var imgElement, titleElement, avatarElement;
if (this.props.linkTitle) { if (this.props.linkTitle) {
imgElement = <a className="thumbnail-image" href={this.props.href} key="imgElement"> if (this.state.srcFallback) {
<img src={this.props.src} alt={this.props.alt} /> imgElement = (
</a>; <a
titleElement = <a href={this.props.href} key="titleElement"> className="thumbnail-image"
href={this.props.href}
key="imgElement"
>
<img
alt={this.props.alt}
src={this.props.srcDefault}
/>
</a>
);
} else {
imgElement = (
<a
className="thumbnail-image"
href={this.props.href}
key="imgElement"
>
<img
alt={this.props.alt}
src={this.props.src}
onError={this.handleSrcError}
/>
</a>
);
}
titleElement = (
<a href={this.props.href} key="titleElement">
{this.props.title} {this.props.title}
</a>; </a>
);
} else { } else {
imgElement = <img src={this.props.src} />; imgElement = <img src={this.props.src} />;
titleElement = this.props.title; titleElement = this.props.title;
...@@ -97,10 +141,32 @@ var Thumbnail = React.createClass({ ...@@ -97,10 +141,32 @@ var Thumbnail = React.createClass({
} }
if (this.props.avatar && this.props.showAvatar) { if (this.props.avatar && this.props.showAvatar) {
avatarElement = if (this.state.avatarFallback) {
<a className="creator-image" href={'/users/' + this.props.creator + '/'}> avatarElement = (
<img src={this.props.avatar} alt={this.props.creator} /> <a
</a>; className="creator-image"
href={'/users/' + this.props.creator + '/'}
>
<img
alt={this.props.creator}
src={this.props.avatarDefault}
/>
</a>
);
} else {
avatarElement = (
<a
className="creator-image"
href={'/users/' + this.props.creator + '/'}
>
<img
alt={this.props.creator}
src={this.props.avatar}
onError={this.handleAvatarError}
/>
</a>
);
}
} }
return ( return (
<div className={classes} > <div className={classes} >
......
...@@ -47,7 +47,7 @@ var TTTTile = React.createClass({ ...@@ -47,7 +47,7 @@ var TTTTile = React.createClass({
{this.props.onGuideClick && ( {this.props.onGuideClick && (
<div className="ttt-tile-guides" onClick={this.props.onGuideClick}> <div className="ttt-tile-guides" onClick={this.props.onGuideClick}>
<FormattedMessage id='tile.guides' defaultMessage='See Cards and Guides'/> <FormattedMessage id='tile.guides' defaultMessage='See Cards and Guides'/>
<img className="ttt-tile-see-more" src="/svgs/ttt/see-more.svg" /> <img className="ttt-tile-open-modal" src="/svgs/modal/open-blue.svg" />
</div> </div>
)} )}
</div> </div>
......
...@@ -105,14 +105,15 @@ ...@@ -105,14 +105,15 @@
font-size: .75rem; font-size: .75rem;
font-weight: 500; font-weight: 500;
&:hover { &:hover {
background-color: lighten($link-blue, 40%); background-color: lighten($link-blue, 40%);
} }
} }
.ttt-tile-see-more { .ttt-tile-open-modal {
display: inline-block; display: inline-block;
padding: 0 .25rem; padding: 0 .25rem;
vertical-align: middle; width: 1rem;
height: 1rem;
vertical-align: text-bottom;
} }
...@@ -64,7 +64,7 @@ var Developers = React.createClass({ ...@@ -64,7 +64,7 @@ var Developers = React.createClass({
<div className="inner"> <div className="inner">
<section id="projects"> <section id="projects">
<span className="nav-spacer"></span> <span className="nav-spacer"></span>
<h2>Projects</h2> <h2><FormattedMessage id='developers.projectsTitle' /></h2>
<p className="intro"> <p className="intro">
<FormattedMessage id='developers.projectsIntro' /> <FormattedMessage id='developers.projectsIntro' />
</p> </p>
...@@ -94,13 +94,7 @@ var Developers = React.createClass({ ...@@ -94,13 +94,7 @@ var Developers = React.createClass({
<div className="body-copy column"> <div className="body-copy column">
<h3>ScratchJr</h3> <h3>ScratchJr</h3>
<p> <p>
ScratchJr is an introductory programming language{' '} <FormattedHTMLMessage id='developers.jrBody' />
that enables young children (ages 5-7) to create{' '}
their own interactive stories and games. For more{' '}
information, visit the{' '}
<a href="https://www.scratchjr.org/">ScratchJr website</a>{' '}
or access the code and documentation{' '}
<a href="https://github.com/LLK/scratchjr">here</a>.
</p> </p>
</div> </div>
</FlexRow> </FlexRow>
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
"developers.wwwTitle": "Scratch WWW", "developers.wwwTitle": "Scratch WWW",
"developers.wwwIntro": "Scratch-www is a standalone web client for the Scratch Community, built using React and Redux. Access the code and documentation through Github <a href=\"https://github.com/LLK/scratch-www\">here</a>.", "developers.wwwIntro": "Scratch-www is a standalone web client for the Scratch Community, built using React and Redux. Access the code and documentation through Github <a href=\"https://github.com/LLK/scratch-www\">here</a>.",
"developers.principlesIntro": "We created Scratch to empower young people to think creatively, reason systematically, and work collaboratively. We are guided by a set of <b>Learning Principles</b> and <b>Design Principles</b> that we hope you will follow as you develop new tools and technologies with Scratch Blocks.", "developers.principlesIntro": "We created Scratch to empower young people to think creatively, reason systematically, and work collaboratively. We are guided by a set of <b>Learning Principles</b> and <b>Design Principles</b> that we hope you will follow as you develop new tools and technologies with Scratch Blocks.",
"developers.jrBody": "ScratchJr is an introductory programming language that enables young children (ages 5-7) to create their own interactive stories and games. For more information, visit the <a href=\"https://www.scratchjr.org/\">ScratchJr website</a> or access the code and documentation <a href=\"https://github.com/LLK/scratchjr\">on GitHub</a>.",
"developers.learningPrinciplesTitle": "Learning Principles", "developers.learningPrinciplesTitle": "Learning Principles",
"developers.learningPrinciplesProjectsBody": "People learn best when they are actively working on projects — generating new ideas, designing prototypes, making improvements and creating final products.", "developers.learningPrinciplesProjectsBody": "People learn best when they are actively working on projects — generating new ideas, designing prototypes, making improvements and creating final products.",
"developers.learningPrinciplesPassionTitle": "Passion", "developers.learningPrinciplesPassionTitle": "Passion",
...@@ -37,7 +38,7 @@ ...@@ -37,7 +38,7 @@
"developers.donateThanks": "Thanks for supporting Scratch!", "developers.donateThanks": "Thanks for supporting Scratch!",
"developers.partnersIntro": "The creation and maintenance of this open source code would not be possible without generous technical and financial support from our partners:", "developers.partnersIntro": "The creation and maintenance of this open source code would not be possible without generous technical and financial support from our partners:",
"developers.faqAboutTitle": "Where can I learn more about Scratch?", "developers.faqAboutTitle": "Where can I learn more about Scratch?",
"developers.faqAboutBody": "Scratch is a free programming language and online community where young people can create their own interactive stories, games, and animations. Scratch is a project of the <a href=\"https://llk.media.mit.edu/\">Lifelong Kindergarten</a> Group at the <a href=\"http://media.mit.edu/\">MIT Media Lab</a>. You can learn more about Scratch <a href=\"/about\">here</a>.", "developers.faqAboutBody": "Scratch is a free programming language and online community where young people can create their own interactive stories, games, and animations. Scratch is a project of the <a href=\"https://www.media.mit.edu/groups/lifelong-kindergarten/overview\">Lifelong Kindergarten</a> Group at the <a href=\"http://media.mit.edu/\">MIT Media Lab</a>. You can learn more about Scratch <a href=\"/about\">here</a>.",
"developers.faqRulesTitle": "Are there rules to using this code in my application?", "developers.faqRulesTitle": "Are there rules to using this code in my application?",
"developers.faqRulesBody": "You may use this code in accordance with the license which governs each project. We also strongly encourage you to consider the learning and design principles (above, on this page) when building creative learning experiences for kids of all ages.", "developers.faqRulesBody": "You may use this code in accordance with the license which governs each project. We also strongly encourage you to consider the learning and design principles (above, on this page) when building creative learning experiences for kids of all ages.",
"developers.faqNameTitle": "Am I allowed to use the name \"Scratch Blocks\" in the description of my app and other public messaging?", "developers.faqNameTitle": "Am I allowed to use the name \"Scratch Blocks\" in the description of my app and other public messaging?",
......
...@@ -231,6 +231,7 @@ var Download = injectIntl(React.createClass({ ...@@ -231,6 +231,7 @@ var Download = injectIntl(React.createClass({
<p><FormattedMessage id='download.knownIssuesOne' /></p> <p><FormattedMessage id='download.knownIssuesOne' /></p>
<p><FormattedMessage id='download.knownIssuesTwo' /></p> <p><FormattedMessage id='download.knownIssuesTwo' /></p>
<p><FormattedHTMLMessage id='download.knownIssuesThree' /></p> <p><FormattedHTMLMessage id='download.knownIssuesThree' /></p>
<p><FormattedMessage id='download.knownIssuesFour' /></p>
<a href="https://scratch.mit.edu/discuss/3/" className='button mod-link'> <a href="https://scratch.mit.edu/discuss/3/" className='button mod-link'>
<FormattedMessage id='download.reportBugs' /> <FormattedMessage id='download.reportBugs' />
</a> </a>
......
{ {
"download.title": "Scratch 2.0 Offline Editor", "download.title": "Scratch 2.0 Offline Editor",
"download.intro": "You can install the Scratch 2.0 editor to work on projects without an internet connection. This version will work on Mac, Windows, and some versions of Linux (32 bit).", "download.intro": "You can install the Scratch 2.0 editor to work on projects without an internet connection. This version will work on Mac, Windows, and some versions of Linux (32 bit).",
"download.introMac": "<b>Note for Mac Users:</b> the latest version of Scratch 2.0 Offline requires Adobe Air 20. To upgrade to Adobe Air 20 manually, go <a href=\"https://get.adobe.com/air/\">here</a>.", "download.introMac": "<b>Note for Mac Users:</b> the latest version of Scratch 2.0 Offline requires Adobe AIR 20. To upgrade to Adobe AIR 20 manually, go <a href=\"https://get.adobe.com/air/\">here</a>.",
"download.installation": "Installation", "download.installation": "Installation",
"download.airTitle": "Adobe AIR", "download.airTitle": "Adobe AIR",
"download.airBody": "If you don't already have it, download and install the latest <a href=\"http://get.adobe.com/air/\">Adobe AIR</a>", "download.airBody": "If you don't already have it, download and install the latest <a href=\"http://get.adobe.com/air/\">Adobe AIR</a>",
...@@ -24,9 +24,10 @@ ...@@ -24,9 +24,10 @@
"download.otherVersionsOlder": "If you have an older computer, or cannot install the Scratch 2.0 offline editor, you can try installing <a href=\"http://scratch.mit.edu/scratch_1.4/\">Scratch 1.4</a>.", "download.otherVersionsOlder": "If you have an older computer, or cannot install the Scratch 2.0 offline editor, you can try installing <a href=\"http://scratch.mit.edu/scratch_1.4/\">Scratch 1.4</a>.",
"download.otherVersionsAdmin": "If you are a network administrator: a Scratch 2.0 MSI has been created and maintained by a member of the community and hosted for public download <a href=\"http://llk.github.io/scratch-msi/\">here</a>.", "download.otherVersionsAdmin": "If you are a network administrator: a Scratch 2.0 MSI has been created and maintained by a member of the community and hosted for public download <a href=\"http://llk.github.io/scratch-msi/\">here</a>.",
"download.knownIssuesTitle": "Known issues", "download.knownIssuesTitle": "Known issues",
"download.knownIssuesOne": "If your offline editor is crashing directly after Scratch is opened, install the Scratch 2 offline editor again (see step 2 above). This issue is due to a bug introduced in Adobe Air version 14 (released April 2014).", "download.knownIssuesOne": "If your offline editor is crashing directly after Scratch is opened, install the Scratch 2 offline editor again (see step 2 above). This issue is due to a bug introduced in Adobe AIR version 14 (released April 2014).",
"download.knownIssuesTwo": "Graphic effects blocks (in \"Looks\") may slow down projects due to a known Flash bug.", "download.knownIssuesTwo": "Graphic effects blocks (in \"Looks\") may slow down projects due to a known Flash bug.",
"download.knownIssuesThree": "The <b>backpack</b> is not yet available.", "download.knownIssuesThree": "The <b>backpack</b> is not yet available.",
"download.knownIssuesFour": "On Mac OS you may see a prompt indicating that \"Scratch 2 is trying to install a new helper tool\" and asking for your user name and password. We are currently investigating a solution to this problem.",
"download.reportBugs": "Report Bugs and Glitches", "download.reportBugs": "Report Bugs and Glitches",
"download.notAvailable": "Hmm, editor downloads are not available right now - please refresh the page to try again." "download.notAvailable": "Hmm, editor downloads are not available right now - please refresh the page to try again."
} }
...@@ -31,8 +31,8 @@ var Jobs = React.createClass({ ...@@ -31,8 +31,8 @@ var Jobs = React.createClass({
<h3><FormattedMessage id='jobs.openings' /></h3> <h3><FormattedMessage id='jobs.openings' /></h3>
<ul> <ul>
<li> <li>
<a href="https://www.media.mit.edu/about/job-opportunities/junior-web-designer-scratch/"> <a href="https://www.media.mit.edu/about/job-opportunities/web-designer-scratch/">
Junior Designer Designer
</a> </a>
<span> <span>
MIT Media Lab, Cambridge, MA MIT Media Lab, Cambridge, MA
......
var FormattedMessage = require('react-intl').FormattedMessage;
var injectIntl = require('react-intl').injectIntl;
var MediaQuery = require('react-responsive');
var React = require('react');
var FlexRow = require('../../../components/flex-row/flex-row.jsx');
var TitleBanner = require('../../../components/title-banner/title-banner.jsx');
var TTTModal = require('../../../components/modal/ttt/modal.jsx');
var TTTTile = require('../../../components/ttt-tile/ttt-tile.jsx');
var frameless = require('../../../lib/frameless');
var tiles = require('../../tips/ttt');
require('../../../components/forms/button.scss');
require('./middle-banner.scss');
var MiddleBanner = injectIntl(React.createClass({
getInitialState: function () {
return {
currentTile: tiles[1],
TTTModalOpen: false
};
},
showTTTModal: function (tile) {
return this.setState({
currentTile: tile,
TTTModalOpen: true
});
},
hideTTTModal: function () {
return this.setState({TTTModalOpen: false});
},
renderTTTTiles: function () {
var formatMessage = this.props.intl.formatMessage;
var tileObjects = {
flyTile: {
title: formatMessage({id: tiles[1].title}),
description: formatMessage({id: tiles[1].description}),
tutorialLoc: tiles[1].tutorialLoc,
activityLoc: formatMessage({id: tiles[1].activityLoc}),
guideLoc: formatMessage({id: tiles[1].guideLoc}),
thumbUrl: tiles[1].thumbUrl,
bannerUrl: tiles[1].bannerUrl
},
musicTile: {
title: formatMessage({id: tiles[2].title}),
description: formatMessage({id: tiles[2].description}),
tutorialLoc: tiles[2].tutorialLoc,
activityLoc: formatMessage({id: tiles[2].activityLoc}),
guideLoc: formatMessage({id: tiles[2].guideLoc}),
thumbUrl: tiles[2].thumbUrl,
bannerUrl: tiles[2].bannerUrl
},
pongTile: {
title: formatMessage({id: tiles[7].title}),
description: formatMessage({id: tiles[7].description}),
tutorialLoc: tiles[7].tutorialLoc,
activityLoc: formatMessage({id: tiles[7].activityLoc}),
guideLoc: formatMessage({id: tiles[7].guideLoc}),
thumbUrl: tiles[7].thumbUrl,
bannerUrl: tiles[7].bannerUrl
}
};
return [
<TTTTile
key={1}
className="mod-banner"
onGuideClick={this.showTTTModal.bind(this, tileObjects.flyTile)}
{...tileObjects.flyTile}
/>,
<TTTTile
key={2}
className="mod-banner"
onGuideClick={this.showTTTModal.bind(this, tileObjects.musicTile)}
{...tileObjects.musicTile}
/>,
<TTTTile
key={7}
className="mod-banner mod-last-tile"
onGuideClick={this.showTTTModal.bind(this, tileObjects.pongTile)}
{...tileObjects.pongTile}
/>
];
},
render: function () {
return (
<TitleBanner className="mod-splash-middle">
<div className="middle-banner inner">
<FlexRow className="middle-banner-header">
<h1 className="middle-banner-header-h1">
<FormattedMessage id="middleBanner.header" />
</h1>
<a href="/tips" className="button mod-ttt-try-button">
<FormattedMessage id="middleBanner.ttt" />
</a>
</FlexRow>
<MediaQuery minWidth={frameless.tablet}>
<FlexRow className="middle-banner-tiles">
{this.renderTTTTiles()}
</FlexRow>
<TTTModal
isOpen={this.state.TTTModalOpen}
onRequestClose={this.hideTTTModal}
{...this.state.currentTile}
/>
</MediaQuery>
</div>
</TitleBanner>
);
}
}));
module.exports = MiddleBanner;
@import "../../../colors";
@import "../../../frameless";
.title-banner.mod-splash-middle {
background: url("/images/blocks-pattern.png");
background-color: $ui-purple;
background-repeat: repeat;
background-size: 180px 180px;
}
.middle-banner-header {
margin-bottom: 1rem;
justify-content: space-between;
align-items: center;
}
.middle-banner-header-h1 {
color: $type-white;
}
.mod-ttt-try-button {
&:link,
&:visited,
&:active
&:hover {
color: $type-white;
}
}
.ttt-tile.mod-banner {
background-color: $background-color;
}
@media only screen and (min-width: $tablet) and (max-width: $desktop - 1) {
.mod-last-tile {
display: none;
}
.middle-banner-header {
flex-direction: column;
align-items: center;
}
}
@media only screen and (max-width: $tablet - 1) {
.title-banner.mod-splash-middle {
display: none;
}
}
var FormattedMessage = require('react-intl').FormattedMessage;
var injectIntl = require('react-intl').injectIntl;
var React = require('react');
var FlexRow = require('../../../components/flex-row/flex-row.jsx');
var TitleBanner = require('../../../components/title-banner/title-banner.jsx');
var TTTModal = require('../../../components/modal/ttt/modal.jsx');
require('../../../components/forms/button.scss');
require('./top-banner.scss');
var nameTile = {
title: 'ttt.AnimateYourNameTitle',
description: 'ttt.AnimateYourNameDescription',
thumbUrl: '/images/ttt/animate-your-name.jpg',
bannerUrl: '/images/ttt/animate-your-name-banner.jpg',
tutorialLoc: '/projects/editor/?tip_bar=name',
activityLoc: 'cards.nameCardsLink',
guideLoc: 'guides.NameGuideLink'
};
var TopBanner = injectIntl(React.createClass({
type: 'TopBanner',
propTypes: {
loggedIn: React.PropTypes.bool.isRequired
},
getInitialState: function () {
// use translated tile
var formatMessage = this.props.intl.formatMessage;
var translatedTile = {};
translatedTile = {
title: formatMessage({id: nameTile.title}),
description: formatMessage({id: nameTile.description}),
tutorialLoc: nameTile.tutorialLoc,
activityLoc: formatMessage({id: nameTile.activityLoc}),
guideLoc: formatMessage({id: nameTile.guideLoc}),
thumbUrl: nameTile.thumbUrl,
bannerUrl: nameTile.bannerUrl
};
return {currentTile: translatedTile, TTTModalOpen: false};
},
showTTTModal: function () {
this.setState({TTTModalOpen: true});
},
hideTTTModal: function () {
this.setState({TTTModalOpen: false});
},
render: function () {
return (
<TitleBanner className="mod-splash-top">
<FlexRow className="banner-top inner">
<a href="/projects/editor/?tip_bar=name">
<FlexRow className="top-animation">
<img
src="/images/hoc/s.png"
alt=""
className="top-animation-letter mod-letter-s"
/>
<img
src="/images/hoc/c1.png"
alt=""
className="top-animation-letter mod-letter-c1"
/>
<img
src="/images/hoc/r.png"
alt=""
className="top-animation-letter mod-letter-r"
/>
<img
src="/images/hoc/a.png"
alt=""
className="top-animation-letter mod-letter-a"
/>
<img
src="/images/hoc/t.png"
alt=""
className="top-animation-letter mod-letter-t"
/>
<img
src="/images/hoc/c2.png"
alt=""
className="top-animation-letter mod-letter-c2"
/>
<img
src="/images/hoc/h.png"
alt=""
className="top-animation-letter mod-letter-h"
/>
</FlexRow>
</a>
<div className="top-links">
<a href="/projects/editor/?tip_bar=name" className="button mod-top-button">
{ this.props.loggedIn ?
<FormattedMessage id="ttt.AnimateYourNameTitle" /> :
<FormattedMessage id="topBanner.getStarted" />
}
</a>
<div className="mod-guides-link" onClick={this.showTTTModal}>
&nbsp;&nbsp;
<FormattedMessage id="tile.guides" />
<img className="top-open-modal" src="/svgs/modal/open-white.svg" />
<TTTModal
isOpen={this.state.TTTModalOpen}
onRequestClose={this.hideTTTModal}
{...this.state.currentTile}/>
</div>
</div>
</FlexRow>
</TitleBanner>
);
}
}));
module.exports = TopBanner;
@import "../../../colors";
@import "../../../frameless";
.title-banner.mod-splash-top {
background-color: $ui-aqua;
background-image: url("/images/hoc/splash-left.png"), url("/images/hoc/splash-right.png");
background-repeat: no-repeat, no-repeat;
background-position: left bottom, right bottom;
background-size: 40% auto, 40% auto;
}
.banner-top {
background-image: url("/images/hoc/doodads.png");
background-repeat: no-repeat;
background-position: top;
background-size: 70%;
flex-direction: column;
}
.top-banner-header {
justify-content: space-between;
}
.top-banner-header-h1 {
margin-bottom: 1.25rem;
color: $type-white;
}
.banner-image.mod-top {
width: 100%;
}
.top-animation {
margin: auto;
padding-top: 2rem;
padding-bottom: 1rem;
width: 70%;
}
.top-animation-letter {
animation-duration: 1s;
animation-iteration-count: infinite;
animation-fill-mode: both;
width: 100%;
}
@keyframes jump {
from,
to {
transform: translate3d(0, 0, 0);
}
12.5%,
62.5% {
transform: translate3d(0, 10px, 0);
}
37.5%,
87.5% {
transform: translate3d(0, -10px, 0);
}
}
.mod-letter-s {
// width: 16.6%;
animation-name: jump;
width: 13.3%;
}
@keyframes pulse {
from {
transform: scale3d(1, 1, 1);
}
50% {
transform: scale3d(1.25, 1.25, 1.25);
}
to {
transform: scale3d(1, 1, 1);
}
}
.mod-letter-c1 {
// width: 12.5%;
animation-name: pulse;
width: 10%;
}
@keyframes spin-left {
90% {
transform: rotate3d(0, 0, 1, -360deg);
}
to {
transform: rotate3d(0, 0, 1, -360deg);
}
}
.mod-letter-r {
// width: 14%;
animation-name: spin-left;
width: 11.2%;
}
@keyframes swing {
25% {
transform: rotate3d(0, 0, 1, 40deg);
}
75% {
transform: rotate3d(0, 0, 1, -40deg);
}
from,
to {
transform: rotate3d(0, 0, 1, 0deg);
}
}
.mod-letter-a {
// width: 14.4%;
animation-name: swing;
width: 11.5%;
}
@keyframes shake {
from,
to {
transform: translate3d(0, 0, 0);
}
12.5%,
62.5% {
transform: translate3d(-10px, 0, 0);
}
37.5%,
87.5% {
transform: translate3d(10px, 0, 0);
}
}
.mod-letter-t {
// width: 15.5%;
animation-name: shake;
width: 12.4%;
}
@keyframes spin-right {
90% {
transform: rotate3d(0, 0, 1, 360deg);
}
to {
transform: rotate3d(0, 0, 1, 360deg);
}
}
.mod-letter-c2 {
// width: 12.5%;
animation-name: spin-right;
width: 10%;
}
@keyframes inverse-jump {
from,
to {
transform: translate3d(0, 0, 0);
}
12.5%,
62.5% {
transform: translate3d(0, -10px, 0);
}
37.5%,
87.5% {
transform: translate3d(0, 10px, 0);
}
}
.mod-letter-h {
// width: 14.4%;
animation-name: inverse-jump;
width: 11.5%;
}
.mod-top-button {
&:active &:hover,
&:link,
&:visited {
color: $type-white;
font-size: 1rem;
}
}
.mod-top-button {
border: 1px solid $active-gray;
box-shadow: none;
background-color: $ui-blue;
}
.top-links {
display: flex;
flex-direction: column;
justify-content: space-around;
align-items: center;
}
.mod-guides-link {
cursor: pointer;
padding: 1.25rem 0;
color: $ui-white;
font-size: 1rem;
font-weight: 500;
}
.top-open-modal {
display: inline-block;
padding: 0 .25rem;
vertical-align: top;
}
@media only screen and (max-width: $tablet - 1) {
.flex-row.top-animation {
flex-direction: row;
}
}
{
"cards.nameCardsLink": "https://resources.scratch.mit.edu/www/cards/en/nameCards.pdf",
"cards.flyCardsLink": "https://resources.scratch.mit.edu/www/cards/en/flyCards.pdf",
"cards.musicCardsLink": "https://resources.scratch.mit.edu/www/cards/en/musicCards.pdf",
"cards.pongCardsLink": "https://resources.scratch.mit.edu/www/cards/en/pongCards.pdf",
"guides.NameGuideLink": "https://resources.scratch.mit.edu/www/guides/en/NameGuide.pdf",
"guides.FlyGuideLink": "https://resources.scratch.mit.edu/www/guides/en/FlyGuide.pdf",
"guides.MusicGuideLink": "https://resources.scratch.mit.edu/www/guides/en/MusicGuide.pdf",
"guides.PongGuideLink": "https://resources.scratch.mit.edu/www/guides/en/PongGuide.pdf"
}
...@@ -29,6 +29,28 @@ ...@@ -29,6 +29,28 @@
"teacherbanner.classesButton": "My Classes", "teacherbanner.classesButton": "My Classes",
"teacherbanner.faqButton": "Teacher Account FAQ", "teacherbanner.faqButton": "Teacher Account FAQ",
"middleBanner.header": "Get Creative with Coding",
"middleBanner.ttt": "See more activities",
"topBanner.getStarted": "Get Started with Coding!",
"ttt.tutorial": "Tutorial",
"ttt.open": "Open",
"ttt.tutorialSubtitle": "Find out how to make this project using a step-by-step tutorial in Scratch.",
"ttt.activityTitle": "Activity Cards",
"ttt.activitySubtitle": "Explore new coding ideas using this set of illustrated cards you can print out.",
"ttt.educatorTitle": "Educator Guide",
"ttt.educatorSubtitle": "Use this educator guide to plan and lead a one-hour Scratch workshop.",
"tile.tryIt": "Try It",
"tile.guides": "See Cards and Guides",
"ttt.download": "Download",
"ttt.AnimateYourNameTitle": "Animate a Name",
"ttt.AnimateYourNameDescription": "Animate the letters of your username, initials, or favorite word.",
"ttt.MakeItFlyTitle": "Make It Fly",
"ttt.MakeItFlyDescription": "Animate the Scratch Cat, The Powerpuff Girls, or even a taco!",
"ttt.MakeMusicTitle": "Make Music",
"ttt.MakeMusicDescription": "Choose instruments, add sounds, and press keys to play music.",
"ttt.PongTitle": "Pong Game",
"ttt.PongDescription": "Make a bouncing ball game with sounds, points, and other effects.",
"welcome.welcomeToScratch": "Welcome to Scratch!", "welcome.welcomeToScratch": "Welcome to Scratch!",
"welcome.learn": "Learn how to make a project in Scratch", "welcome.learn": "Learn how to make a project in Scratch",
"welcome.tryOut": "Try out starter projects", "welcome.tryOut": "Try out starter projects",
......
...@@ -11,6 +11,8 @@ var Box = require('../../components/box/box.jsx'); ...@@ -11,6 +11,8 @@ var Box = require('../../components/box/box.jsx');
var Button = require('../../components/forms/button.jsx'); var Button = require('../../components/forms/button.jsx');
var Carousel = require('../../components/carousel/carousel.jsx'); var Carousel = require('../../components/carousel/carousel.jsx');
var LegacyCarousel = require('../../components/carousel/legacy-carousel.jsx'); var LegacyCarousel = require('../../components/carousel/legacy-carousel.jsx');
var TopBanner = require('./hoc/top-banner.jsx');
var MiddleBanner = require('./hoc/middle-banner.jsx');
var Intro = require('../../components/intro/intro.jsx'); var Intro = require('../../components/intro/intro.jsx');
var IframeModal = require('../../components/modal/iframe/modal.jsx'); var IframeModal = require('../../components/modal/iframe/modal.jsx');
var News = require('../../components/news/news.jsx'); var News = require('../../components/news/news.jsx');
...@@ -253,6 +255,8 @@ var SplashPresentation = injectIntl(React.createClass({ ...@@ -253,6 +255,8 @@ var SplashPresentation = injectIntl(React.createClass({
{this.props.isEducator ? [ {this.props.isEducator ? [
<TeacherBanner key="teacherbanner" messages={messages} /> <TeacherBanner key="teacherbanner" messages={messages} />
] : []} ] : []}
<TopBanner loggedIn={this.props.sessionStatus === sessionActions.Status.FETCHED
&& Object.keys(this.props.user).length !== 0}/>
<div key="inner" className="inner mod-splash"> <div key="inner" className="inner mod-splash">
{this.props.sessionStatus === sessionActions.Status.FETCHED ? ( {this.props.sessionStatus === sessionActions.Status.FETCHED ? (
Object.keys(this.props.user).length !== 0 ? [ Object.keys(this.props.user).length !== 0 ? [
...@@ -273,6 +277,12 @@ var SplashPresentation = injectIntl(React.createClass({ ...@@ -273,6 +277,12 @@ var SplashPresentation = injectIntl(React.createClass({
]) : [] ]) : []
} }
{featured.shift()}
{featured.shift()}
</div>
<MiddleBanner />
<div key="inner2" className="inner mod-splash">
{featured} {featured}
{this.props.isAdmin ? [ {this.props.isAdmin ? [
......
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch -->
<title>open-blue</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="open-blue" fill="#4C97FF">
<path d="M16.904265,9.16353035 L15.4298099,7.69520276 L10.2383662,11.6050888 C9.64794591,12.057779 8.79050666,11.9379804 8.33625534,11.3315664 C7.9692373,10.844951 7.97668404,10.1706865 8.33625534,9.70951497 L12.2606889,4.53591307 L10.8213398,3.10151073 C10.4107052,2.6922873 10.7011281,2.00848131 11.2670806,2.00848131 L17.3585163,2 C17.709577,2.00848131 18,2.29896634 18,2.64033925 L18,8.71932149 C18,9.28332892 17.3063891,9.56427246 16.904265,9.16353035 Z M15.3807332,18 L3.03722491,18 C2.46488952,18 2,17.5367082 2,16.9652796 L2,4.66419295 C2,4.09382454 2.46488952,3.62947257 3.03722491,3.62947257 L7.82335296,3.62947257 C8.39781598,3.62947257 8.86164168,4.09382454 8.86164168,4.66419295 C8.86164168,5.23562152 8.39781598,5.69891333 7.82335296,5.69891333 L4.07657745,5.69891333 L4.07657745,15.9305592 L14.3424445,15.9305592 L14.3424445,13.2101776 C14.3424445,12.638749 14.807334,12.1754572 15.3807332,12.1754572 C15.9541324,12.1754572 16.4200857,12.638749 16.4200857,13.2101776 L16.4200857,16.9652796 C16.4200857,17.5367082 15.9541324,18 15.3807332,18 Z" id="open-white"></path>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch -->
<title>open-modal-icon</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="open-modal-icon" fill="#FFFFFF">
<path d="M16.904265,9.16353035 L15.4298099,7.69520276 L10.2383662,11.6050888 C9.64794591,12.057779 8.79050666,11.9379804 8.33625534,11.3315664 C7.9692373,10.844951 7.97668404,10.1706865 8.33625534,9.70951497 L12.2606889,4.53591307 L10.8213398,3.10151073 C10.4107052,2.6922873 10.7011281,2.00848131 11.2670806,2.00848131 L17.3585163,2 C17.709577,2.00848131 18,2.29896634 18,2.64033925 L18,8.71932149 C18,9.28332892 17.3063891,9.56427246 16.904265,9.16353035 Z M15.3807332,18 L3.03722491,18 C2.46488952,18 2,17.5367082 2,16.9652796 L2,4.66419295 C2,4.09382454 2.46488952,3.62947257 3.03722491,3.62947257 L7.82335296,3.62947257 C8.39781598,3.62947257 8.86164168,4.09382454 8.86164168,4.66419295 C8.86164168,5.23562152 8.39781598,5.69891333 7.82335296,5.69891333 L4.07657745,5.69891333 L4.07657745,15.9305592 L14.3424445,15.9305592 L14.3424445,13.2101776 C14.3424445,12.638749 14.807334,12.1754572 15.3807332,12.1754572 C15.9541324,12.1754572 16.4200857,12.638749 16.4200857,13.2101776 L16.4200857,16.9652796 C16.4200857,17.5367082 15.9541324,18 15.3807332,18 Z"></path>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
<style type="text/css">
.st0{opacity:0.2;fill:#4C97FF;stroke:#4C97FF;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st1{fill:none;stroke:#4C97FF;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
.st2{fill:#4C97FF;}
</style>
<path class="st0" d="M13,11H6c-0.6,0-1-0.4-1-1V3c0-0.6,0.4-1,1-1h7c0.6,0,1,0.4,1,1v7C14,10.6,13.6,11,13,11z"/>
<path class="st1" d="M9,13c0,0.6-0.4,1-1,1H3c-0.6,0-1-0.4-1-1V8c0-0.6,0.4-1,1-1"/>
<path class="st1" d="M13,11H6c-0.6,0-1-0.4-1-1V3c0-0.6,0.4-1,1-1h7c0.6,0,1,0.4,1,1v7C14,10.6,13.6,11,13,11z"/>
<g>
<path class="st2" d="M12,4.2v2.4c0,0.2-0.3,0.3-0.4,0.2L10.8,6l-3,2.7c-0.1,0.1-0.3,0.1-0.4,0c-0.1-0.1-0.1-0.3,0-0.4L10,5.2
L9.2,4.4C9,4.3,9.1,4,9.4,4h2.4C11.9,4,12,4.1,12,4.2z"/>
</g>
</svg>
...@@ -18,6 +18,10 @@ const clickText = (text) => { ...@@ -18,6 +18,10 @@ const clickText = (text) => {
return clickXpath(`//*[contains(text(), '${text}')]`); return clickXpath(`//*[contains(text(), '${text}')]`);
}; };
const findText = (text) => {
return driver.wait(until.elementLocated(By.xpath(`//*[contains(text(), '${text}')]`), 5 * 1000));
};
const clickButton = (text) => { const clickButton = (text) => {
return clickXpath(`//button[contains(text(), '${text}')]`); return clickXpath(`//button[contains(text(), '${text}')]`);
}; };
...@@ -57,6 +61,7 @@ module.exports = { ...@@ -57,6 +61,7 @@ module.exports = {
clickXpath, clickXpath,
findByXpath, findByXpath,
clickText, clickText,
findText,
clickButton, clickButton,
findByCss, findByCss,
getLogs getLogs
......
{ {
"testDependencies": { "devDependencies": {
"selenium-webdriver": "2.44.0", "selenium-webdriver": "2.45.0",
"chromedriver": "2.27.0" "chromedriver": "2.33.0"
} }
} }
...@@ -4,20 +4,20 @@ ...@@ -4,20 +4,20 @@
* Test cases: https://github.com/LLK/scratch-www/wiki/Most-Important-Workflows * Test cases: https://github.com/LLK/scratch-www/wiki/Most-Important-Workflows
*/ */
require('chromedriver');
var tap = require('tap'); var tap = require('tap');
var seleniumWebdriver = require('selenium-webdriver');
// Selenium's promise driver will be deprecated, so we should not rely on it const {
seleniumWebdriver.SELENIUM_PROMISE_MANAGER=0; driver,
webdriver
} = require('../../helpers/selenium-helpers.js');
//chrome driver // Selenium's promise driver will be deprecated, so we should not rely on it
var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build(); webdriver.SELENIUM_PROMISE_MANAGER=0;
var rootUrl = process.env.ROOT_URL || 'https://scratch.ly'; var rootUrl = process.env.ROOT_URL || 'https://scratch.ly';
//timeout for each test; timeout for suite set at command line level //timeout for each test; timeout for suite set at command line level
var options = { timeout: 20000 }; var options = { timeout: 30000 };
//number of tests in the plan //number of tests in the plan
tap.plan(25); tap.plan(25);
...@@ -33,20 +33,16 @@ tap.beforeEach(function () { ...@@ -33,20 +33,16 @@ tap.beforeEach(function () {
}); });
// Function clicks the link and returns the url of the resulting page // Function clicks the link and returns the url of the resulting page
function clickFooterLinks ( linkText ) {
// Not sure if I need this first wait - maybe it solved intermittent initial failure problem? function clickFooterLinks (linkText) {
return driver.wait(seleniumWebdriver.until.elementLocated(seleniumWebdriver.By.id('view'))) return driver.wait(webdriver.until.elementLocated(webdriver.By.id('footer')))
.then( function () { .then( function (element) {
return driver.wait(seleniumWebdriver.until.elementLocated(seleniumWebdriver.By return element.findElement(webdriver.By.linkText(linkText)); })
.id('footer')))
.then( function () {
return driver.findElement(seleniumWebdriver.By.linkText(linkText)); })
.then( function (element) { .then( function (element) {
return element.click(); }) return element.click(); })
.then(function () { .then(function () {
return driver.getCurrentUrl(); return driver.getCurrentUrl();
}); });
});
} }
// ==== ABOUT SCRATCH column ==== // ==== ABOUT SCRATCH column ====
......
/*
* Tests from:
*
* https://github.com/LLK/scratchr2/wiki/Smoke-Testing-Test-Cases
*
*/
const {
clickText,
findByXpath,
findText,
clickXpath,
clickButton,
driver
} = require('../../helpers/selenium-helpers.js');
var username = process.env.SMOKE_USERNAME;
var password = process.env.SMOKE_PASSWORD;
var tap = require('tap');
const test = tap.test;
var rootUrl = process.env.ROOT_URL || 'https://scratch.ly';
var url = rootUrl + '/discuss';
tap.plan(2);
tap.tearDown(function () {
driver.quit();
});
tap.beforeEach(function () {
return driver.get(url);
});
test('Sign in to Scratch using scratchr2 navbar', t => {
clickText('Sign in')
.then(() => findByXpath('//input[@id="login_dropdown_username"]'))
.then((element) => element.sendKeys(username))
.then(() => findByXpath('//input[@name="password"]'))
.then((element) => element.sendKeys(password))
.then(() => clickButton('Sign in'))
.then(() => findByXpath('//li[contains(@class, "logged-in-user")'
+ 'and contains(@class, "dropdown")]/span'))
.then((element) => element.getText('span'))
.then((text) => t.match(text.toLowerCase(), username.substring(0,10).toLowerCase(),
'first part of username should be displayed in navbar'))
.then(() => t.end());
});
test('Sign out of Scratch using scratchr2 navbar', t => {
clickXpath('//span[contains(@class, "user-name")'
+ ' and contains(@class, "dropdown-toggle")]/img[@class="user-icon"]')
.then(() => clickXpath('//input[@value="Sign out"]'))
.then(() => findText('Sign in'))
.then((element) => t.ok(element, 'Sign in reappeared on the page after signing out'))
.then(() => t.end());
});
/* /*
* Tests signing in according to smoke-tests at: * Tests from:
* *
* https://github.com/LLK/scratchr2/wiki/Smoke-Testing-Test-Cases * https://github.com/LLK/scratchr2/wiki/Smoke-Testing-Test-Cases
* *
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
const { const {
clickText, clickText,
findText,
findByXpath, findByXpath,
clickXpath, clickXpath,
driver driver
...@@ -20,7 +21,7 @@ const test = tap.test; ...@@ -20,7 +21,7 @@ const test = tap.test;
var rootUrl = process.env.ROOT_URL || 'https://scratch.ly'; var rootUrl = process.env.ROOT_URL || 'https://scratch.ly';
tap.plan(1); tap.plan(2);
tap.tearDown(function () { tap.tearDown(function () {
driver.quit(); driver.quit();
...@@ -44,3 +45,14 @@ test('Sign in to Scratch using scratch-www navbar', t => { ...@@ -44,3 +45,14 @@ test('Sign in to Scratch using scratch-www navbar', t => {
'first part of username should be displayed in navbar')) 'first part of username should be displayed in navbar'))
.then(() => t.end()); .then(() => t.end());
}); });
test('Sign out of Scratch using scratch-www navbar', t => {
clickXpath('//a[@class="user-info"]')
.then(() => clickText('Sign out'))
.then(() => findText('Sign in'))
.then((element) => t.ok(element, 'Sign in reappeared on the page after signing out'))
.then(() => t.end());
});
...@@ -59,44 +59,17 @@ tap.test('getAppRouteCondition', function (t) { ...@@ -59,44 +59,17 @@ tap.test('getAppRouteCondition', function (t) {
t.end(); t.end();
}); });
tap.test('testSetBackend', function (t) {
var backend = fastlyConfig.setBackend('wemust', 'goback', 'marty');
t.equal(backend, '' +
'if (marty) {\n' +
' set req.backend = wemust;\n' +
' set req.http.host = \"goback\";\n' +
'}\n'
);
t.end();
});
tap.test('testSetForward', function (t) {
var forward = fastlyConfig.setForwardHeaders('alwaysforward');
t.equal(forward, '' +
'if (alwaysforward) {\n' +
' if (!req.http.Fastly-FF) {\n' +
' if (req.http.X-Forwarded-For) {\n' +
' set req.http.Fastly-Temp-XFF = req.http.X-Forwarded-For ", " client.ip;\n' +
' } else {\n' +
' set req.http.Fastly-Temp-XFF = client.ip;\n' +
' }\n' +
' } else {\n' +
' set req.http.Fastly-Temp-XFF = req.http.X-Forwarded-For;\n' +
' }\n' +
' set req.grace = 60s;\n' +
' return(pass);\n' +
'}\n'
);
t.end();
});
tap.test('testSetTTL', function (t) { tap.test('testSetTTL', function (t) {
var ttl = fastlyConfig.setResponseTTL('itsactuallyttyl'); var ttl = fastlyConfig.setResponseTTL('itsactuallyttyl');
t.equal(ttl, '' + t.equal(ttl, '' +
'if (itsactuallyttyl) {\n' + 'if (itsactuallyttyl) {\n' +
' if (req.url ~ "^/projects/" && !req.http.Cookie:scratchsessionid) {\n' +
' set beresp.http.Vary = "Accept-Encoding, Accept-Language";\n' +
' } else {\n' +
' set beresp.ttl = 0s;\n' + ' set beresp.ttl = 0s;\n' +
' set beresp.grace = 0s;\n' + ' set beresp.grace = 0s;\n' +
' return(pass);\n' + ' return(pass);\n' +
' }\n' +
'}\n' '}\n'
); );
t.end(); t.end();
......
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