mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2025-02-17 00:21:20 -05:00
Merge pull request #1291 from LLK/release/2.2.20
[Master] Release 2.2.20
This commit is contained in:
commit
aef7b5e834
53 changed files with 975 additions and 211 deletions
3
Makefile
3
Makefile
|
@ -67,6 +67,9 @@ functional:
|
|||
integration:
|
||||
$(TAP) ./test/integration/*.js
|
||||
|
||||
smoke:
|
||||
$(TAP) ./test/integration/smoke-testing/*.js --timeout=3600
|
||||
|
||||
localization:
|
||||
$(TAP) ./test/localization/*.js
|
||||
|
||||
|
|
|
@ -1,18 +1,12 @@
|
|||
var async = require('async');
|
||||
var defaults = require('lodash.defaults');
|
||||
var glob = require('glob');
|
||||
var path = require('path');
|
||||
var fastlyConfig = require('./lib/fastly-config-methods');
|
||||
|
||||
var route_json = require('../src/routes.json');
|
||||
|
||||
const FASTLY_SERVICE_ID = process.env.FASTLY_SERVICE_ID || '';
|
||||
const S3_BUCKET_NAME = process.env.S3_BUCKET_NAME || '';
|
||||
|
||||
const PASS_REQUEST_CONDITION_NAME = 'Pass';
|
||||
const NOT_PASS_REQUEST_CONDITION_NAME = '!(Pass)';
|
||||
const PASS_CACHE_CONDITION_NAME = 'Cache Pass';
|
||||
const BUCKET_NAME_HEADER_NAME = 'Bucket name';
|
||||
|
||||
var fastly = require('./lib/fastly-extended')(process.env.FASTLY_API_KEY, FASTLY_SERVICE_ID);
|
||||
|
||||
var extraAppRoutes = [
|
||||
|
@ -23,87 +17,8 @@ var extraAppRoutes = [
|
|||
'/[^\/]*\.html$'
|
||||
];
|
||||
|
||||
/*
|
||||
* Given the relative path to the static directory, return an array of
|
||||
* patterns matching the files and directories there.
|
||||
*/
|
||||
var getStaticPaths = function (pathToStatic) {
|
||||
var staticPaths = glob.sync(path.resolve(__dirname, pathToStatic));
|
||||
return staticPaths.filter(function (pathName) {
|
||||
// Exclude view html, resolve everything else in the build
|
||||
return path.extname(pathName) !== '.html';
|
||||
}).map(function (pathName) {
|
||||
// Reduce absolute path to relative paths like '/js'
|
||||
var base = path.dirname(path.resolve(__dirname, pathToStatic));
|
||||
return pathName.replace(base, '') + (path.extname(pathName) ? '' : '/');
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
* Given a list of express routes, return a list of patterns to match
|
||||
* the express route and a static view file associated with the route
|
||||
*/
|
||||
var getViewPaths = function (routes) {
|
||||
return routes.reduce(function (paths, route) {
|
||||
var path = route.routeAlias || route.pattern;
|
||||
if (paths.indexOf(path) === -1) {
|
||||
paths.push(path);
|
||||
}
|
||||
return paths;
|
||||
}, []);
|
||||
};
|
||||
|
||||
/*
|
||||
* Translate an express-style pattern e.g. /path/:arg/ to a regex
|
||||
* all :arguments become .+?
|
||||
*/
|
||||
var expressPatternToRegex = function (pattern) {
|
||||
return pattern.replace(/(:[^/]+)/gi, '.+?');
|
||||
};
|
||||
|
||||
/*
|
||||
* Given a list of patterns for paths, OR all of them together into one
|
||||
* string suitable for a Fastly condition
|
||||
*/
|
||||
var pathsToCondition = function (paths) {
|
||||
return 'req.url~"^(' + paths.reduce(function (conditionString, pattern) {
|
||||
return conditionString + (conditionString ? '|' : '') + pattern;
|
||||
}, '') + ')"';
|
||||
};
|
||||
|
||||
/*
|
||||
* Helper method to NOT a condition statement
|
||||
*/
|
||||
var negateConditionStatement = function (statement) {
|
||||
return '!(' + statement + ')';
|
||||
};
|
||||
|
||||
/*
|
||||
* Combine static paths, routes, and any additional paths to a single
|
||||
* fastly condition to match req.url
|
||||
*/
|
||||
var getAppRouteCondition = function (pathToStatic, routes, additionalPaths) {
|
||||
var staticPaths = getStaticPaths(pathToStatic);
|
||||
var viewPaths = getViewPaths(routes);
|
||||
var allPaths = [].concat(staticPaths, viewPaths, additionalPaths);
|
||||
return pathsToCondition(allPaths);
|
||||
};
|
||||
|
||||
var getConditionNameForRoute = function (route, type) {
|
||||
return 'routes/' + route.pattern + ' (' + type + ')';
|
||||
};
|
||||
|
||||
var getHeaderNameForRoute = function (route) {
|
||||
if (route.name) return 'rewrites/' + route.name;
|
||||
if (route.redirect) return 'redirects/' + route.pattern;
|
||||
};
|
||||
|
||||
var getResponseNameForRoute = function (route) {
|
||||
return 'redirects/' + route.pattern;
|
||||
};
|
||||
|
||||
var routes = route_json.map(function (route) {
|
||||
return defaults({}, {pattern: expressPatternToRegex(route.pattern)}, route);
|
||||
return defaults({}, {pattern: fastlyConfig.expressPatternToRegex(route.pattern)}, route);
|
||||
});
|
||||
|
||||
async.auto({
|
||||
|
@ -121,76 +36,43 @@ async.auto({
|
|||
}
|
||||
});
|
||||
},
|
||||
notPassRequestCondition: ['version', function (cb, results) {
|
||||
var statement = getAppRouteCondition('../build/*', routes, extraAppRoutes);
|
||||
var condition = {
|
||||
name: NOT_PASS_REQUEST_CONDITION_NAME,
|
||||
statement: statement,
|
||||
type: 'REQUEST',
|
||||
priority: 10
|
||||
};
|
||||
fastly.setCondition(results.version, condition, cb);
|
||||
}],
|
||||
setBucketNameHeader: ['version', 'notPassRequestCondition', function (cb, results) {
|
||||
var header = {
|
||||
name: BUCKET_NAME_HEADER_NAME,
|
||||
action: 'set',
|
||||
ignore_if_set: 0,
|
||||
type: 'REQUEST',
|
||||
dst: 'http.host',
|
||||
src: '"' + S3_BUCKET_NAME + '"',
|
||||
request_condition: results.notPassRequestCondition.name,
|
||||
priority: 1
|
||||
};
|
||||
fastly.setFastlyHeader(results.version, header, cb);
|
||||
}],
|
||||
passRequestCondition: ['version', 'notPassRequestCondition', function (cb, results) {
|
||||
var condition = {
|
||||
name: PASS_REQUEST_CONDITION_NAME,
|
||||
statement: negateConditionStatement(results.notPassRequestCondition.statement),
|
||||
type: 'REQUEST',
|
||||
priority: 10
|
||||
};
|
||||
fastly.setCondition(results.version, condition, cb);
|
||||
}],
|
||||
passRequestSettingsCondition: ['version', 'passRequestCondition', function (cb, results) {
|
||||
fastly.request(
|
||||
'PUT',
|
||||
fastly.getFastlyAPIPrefix(FASTLY_SERVICE_ID, results.version) + '/request_settings/Pass',
|
||||
{request_condition: results.passRequestCondition.name},
|
||||
recvCustomVCL: ['version', function (cb, results) {
|
||||
// For all the routes in routes.json, construct a varnish-style regex that matches
|
||||
// on any of those route conditions.
|
||||
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
|
||||
var backendCondition = fastlyConfig.setBackend(
|
||||
'F_s3',
|
||||
S3_BUCKET_NAME,
|
||||
notPassStatement
|
||||
);
|
||||
// For a pass condition, set forwarding headers
|
||||
var forwardCondition = fastlyConfig.setForwardHeaders(passStatement);
|
||||
|
||||
fastly.setCustomVCL(
|
||||
results.version,
|
||||
'recv-condition',
|
||||
backendCondition + forwardCondition,
|
||||
cb
|
||||
);
|
||||
}],
|
||||
backendCondition: ['version', 'notPassRequestCondition', function (cb, results) {
|
||||
fastly.request(
|
||||
'PUT',
|
||||
fastly.getFastlyAPIPrefix(FASTLY_SERVICE_ID, results.version) + '/backend/s3',
|
||||
{request_condition: results.notPassRequestCondition.name},
|
||||
cb
|
||||
);
|
||||
}],
|
||||
passCacheCondition: ['version', 'passRequestCondition', function (cb, results) {
|
||||
var condition = {
|
||||
name: PASS_CACHE_CONDITION_NAME,
|
||||
type: 'CACHE',
|
||||
statement: results.passRequestCondition.statement,
|
||||
priority: results.passRequestCondition.priority
|
||||
};
|
||||
fastly.setCondition(results.version, condition, cb);
|
||||
}],
|
||||
passCacheSettingsCondition: ['version', 'passCacheCondition', function (cb, results) {
|
||||
fastly.request(
|
||||
'PUT',
|
||||
fastly.getFastlyAPIPrefix(FASTLY_SERVICE_ID, results.version) + '/cache_settings/Pass',
|
||||
{cache_condition: results.passCacheCondition.name},
|
||||
cb
|
||||
fetchCustomVCL: ['version', function (cb, results) {
|
||||
var passStatement = fastlyConfig.negateConditionStatement(
|
||||
fastlyConfig.getAppRouteCondition('../build/*', routes, extraAppRoutes, __dirname)
|
||||
);
|
||||
var ttlCondition = fastlyConfig.setResponseTTL(passStatement);
|
||||
fastly.setCustomVCL(results.version, 'fetch-condition', ttlCondition, cb);
|
||||
}],
|
||||
appRouteRequestConditions: ['version', function (cb, results) {
|
||||
var conditions = {};
|
||||
async.forEachOf(routes, function (route, id, cb2) {
|
||||
var condition = {
|
||||
name: getConditionNameForRoute(route, 'request'),
|
||||
name: fastlyConfig.getConditionNameForRoute(route, 'request'),
|
||||
statement: 'req.url ~ "' + route.pattern + '"',
|
||||
type: 'REQUEST',
|
||||
// Priority needs to be > 1 to not interact with http->https redirect
|
||||
|
@ -213,7 +95,7 @@ async.auto({
|
|||
async.auto({
|
||||
responseCondition: function (cb3) {
|
||||
var condition = {
|
||||
name: getConditionNameForRoute(route, 'response'),
|
||||
name: fastlyConfig.getConditionNameForRoute(route, 'response'),
|
||||
statement: 'req.url ~ "' + route.pattern + '"',
|
||||
type: 'RESPONSE',
|
||||
priority: id
|
||||
|
@ -222,16 +104,16 @@ async.auto({
|
|||
},
|
||||
responseObject: function (cb3) {
|
||||
var responseObject = {
|
||||
name: getResponseNameForRoute(route),
|
||||
name: fastlyConfig.getResponseNameForRoute(route),
|
||||
status: 301,
|
||||
response: 'Moved Permanently',
|
||||
request_condition: getConditionNameForRoute(route, 'request')
|
||||
request_condition: fastlyConfig.getConditionNameForRoute(route, 'request')
|
||||
};
|
||||
fastly.setResponseObject(results.version, responseObject, cb3);
|
||||
},
|
||||
redirectHeader: ['responseCondition', function (cb3, redirectResults) {
|
||||
var header = {
|
||||
name: getHeaderNameForRoute(route),
|
||||
name: fastlyConfig.getHeaderNameForRoute(route),
|
||||
action: 'set',
|
||||
ignore_if_set: 0,
|
||||
type: 'RESPONSE',
|
||||
|
@ -248,7 +130,7 @@ async.auto({
|
|||
});
|
||||
} else {
|
||||
var header = {
|
||||
name: getHeaderNameForRoute(route, 'request'),
|
||||
name: fastlyConfig.getHeaderNameForRoute(route, 'request'),
|
||||
action: 'set',
|
||||
ignore_if_set: 0,
|
||||
type: 'REQUEST',
|
||||
|
|
139
bin/lib/fastly-config-methods.js
Normal file
139
bin/lib/fastly-config-methods.js
Normal file
|
@ -0,0 +1,139 @@
|
|||
var glob = require('glob');
|
||||
var path = require('path');
|
||||
|
||||
var FastlyConfigMethods = {
|
||||
/*
|
||||
* Given the relative path to the static directory, return an array of
|
||||
* patterns matching the files and directories there.
|
||||
*/
|
||||
getStaticPaths: function (rootDir, pathToStatic) {
|
||||
var staticPaths = glob.sync(path.resolve(rootDir, pathToStatic));
|
||||
return staticPaths.filter(function (pathName) {
|
||||
// Exclude view html, resolve everything else in the build
|
||||
return path.extname(pathName) !== '.html';
|
||||
}).map(function (pathName) {
|
||||
// Reduce absolute path to relative paths like '/js'
|
||||
var base = path.dirname(path.resolve(rootDir, pathToStatic));
|
||||
return pathName.replace(base, '') + (path.extname(pathName) ? '' : '/');
|
||||
});
|
||||
},
|
||||
|
||||
/*
|
||||
* Given a list of express routes, return a list of patterns to match
|
||||
* the express route and a static view file associated with the route
|
||||
*/
|
||||
getViewPaths: function (routes) {
|
||||
return routes.reduce(function (paths, route) {
|
||||
var path = route.routeAlias || route.pattern;
|
||||
if (paths.indexOf(path) === -1) {
|
||||
paths.push(path);
|
||||
}
|
||||
return paths;
|
||||
}, []);
|
||||
},
|
||||
|
||||
/*
|
||||
* Translate an express-style pattern e.g. /path/:arg/ to a regex
|
||||
* all :arguments become .+?
|
||||
*/
|
||||
expressPatternToRegex: function (pattern) {
|
||||
return pattern.replace(/(:[^/]+)/gi, '.+?');
|
||||
},
|
||||
|
||||
/*
|
||||
* Given a list of patterns for paths, OR all of them together into one
|
||||
* string suitable for a Fastly condition
|
||||
*/
|
||||
pathsToCondition: function (paths) {
|
||||
return 'req.url~"^(' + paths.reduce(function (conditionString, pattern) {
|
||||
return conditionString + (conditionString ? '|' : '') + pattern;
|
||||
}, '') + ')"';
|
||||
},
|
||||
|
||||
/*
|
||||
* Helper method to NOT a condition statement
|
||||
*/
|
||||
negateConditionStatement: function (statement) {
|
||||
return '!(' + statement + ')';
|
||||
},
|
||||
|
||||
/*
|
||||
* Combine static paths, routes, and any additional paths to a single
|
||||
* fastly condition to match req.url
|
||||
*/
|
||||
getAppRouteCondition: function (pathToStatic, routes, additionalPaths, rootDir) {
|
||||
var staticPaths = FastlyConfigMethods.getStaticPaths(rootDir, pathToStatic);
|
||||
var viewPaths = FastlyConfigMethods.getViewPaths(routes);
|
||||
var allPaths = [].concat(staticPaths, viewPaths, additionalPaths);
|
||||
return FastlyConfigMethods.pathsToCondition(allPaths);
|
||||
},
|
||||
|
||||
getConditionNameForRoute: function (route, type) {
|
||||
return 'routes/' + route.pattern + ' (' + type + ')';
|
||||
},
|
||||
|
||||
getHeaderNameForRoute: function (route) {
|
||||
if (route.name) return 'rewrites/' + route.name;
|
||||
if (route.redirect) return 'redirects/' + route.pattern;
|
||||
},
|
||||
|
||||
getResponseNameForRoute: function (route) {
|
||||
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
|
||||
* Time to Live (TTL) for responses that come from s3.
|
||||
*
|
||||
* @param {string} condition condition under which the response should be set
|
||||
*/
|
||||
setResponseTTL: function (condition) {
|
||||
return '' +
|
||||
'if (' + condition + ') {\n' +
|
||||
' set beresp.ttl = 0s;\n' +
|
||||
' set beresp.grace = 0s;\n' +
|
||||
' return(pass);\n' +
|
||||
'}\n';
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = FastlyConfigMethods;
|
|
@ -166,5 +166,40 @@ module.exports = function (apiKey, serviceId) {
|
|||
this.request('PUT', url, cb);
|
||||
};
|
||||
|
||||
/**
|
||||
* Upsert a custom vcl file. Attempts a PUT, and falls back
|
||||
* to POST if not there already.
|
||||
*
|
||||
* @param {number} version current version number for fastly service
|
||||
* @param {string} name name of the custom vcl file to be upserted
|
||||
* @param {string} vcl stringified custom vcl to be uploaded
|
||||
* @param {Function} cb function that takes in two args: err, response
|
||||
*/
|
||||
fastly.setCustomVCL = function (version, name, vcl, cb) {
|
||||
if (!this.serviceId) {
|
||||
return cb('Failed to set response object. No serviceId configured');
|
||||
}
|
||||
|
||||
var url = this.getFastlyAPIPrefix(this.serviceId, version) + '/vcl/' + name;
|
||||
var postUrl = this.getFastlyAPIPrefix(this.serviceId, version) + '/vcl';
|
||||
var content = {content: vcl};
|
||||
return this.request('PUT', url, content, function (err, response) {
|
||||
if (err && err.statusCode === 404) {
|
||||
content.name = name;
|
||||
this.request('POST', postUrl, content, function (err, response) {
|
||||
if (err) {
|
||||
return cb('Failed while adding custom vcl \"' + name + '\": ' + err);
|
||||
}
|
||||
return cb(null, response);
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (err) {
|
||||
return cb('Failed to update custom vcl \"' + name + '\": ' + err);
|
||||
}
|
||||
return cb(null, response);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
return fastly;
|
||||
};
|
||||
|
|
|
@ -1,23 +1,67 @@
|
|||
{
|
||||
"en": {
|
||||
"cards.starterLink": "/pdfs/cards/Scratch2Cards.pdf",
|
||||
"cards.nameLink": "/pdfs/cards/AnimateYourNameCards.pdf",
|
||||
"cards.flyLink": "/pdfs/cards/FlyCards.pdf",
|
||||
"cards.raceLink": "/pdfs/cards/RaceGameCards.pdf",
|
||||
"cards.musicLink": "/pdfs/cards/MusicCards.pdf",
|
||||
"cards.hideLink": "/pdfs/cards/Hide-and-Seek-Cards.pdf",
|
||||
"cards.storyLink": "/pdfs/cards/StoryCards.pdf",
|
||||
"cards.dressupLink": "/pdfs/cards/DressupCards.pdf",
|
||||
"cards.pongLink": "/pdfs/cards/PongCards.pdf",
|
||||
"cards.danceLink": "/pdfs/cards/DanceCards.pdf",
|
||||
"cards.catchLink": "/pdfs/cards/CatchCards.pdf",
|
||||
"cards.petLink": "/pdfs/cards/PetCards.pdf"
|
||||
"cards.starterLink": "/pdfs/cards/Scratch2Cards.pdf",
|
||||
"cards.nameLink": "/pdfs/cards/AnimateYourNameCards.pdf",
|
||||
"cards.flyLink": "/pdfs/cards/FlyCards.pdf",
|
||||
"cards.raceLink": "/pdfs/cards/RaceGameCards.pdf",
|
||||
"cards.musicLink": "/pdfs/cards/MusicCards.pdf",
|
||||
"cards.hideLink": "/pdfs/cards/Hide-and-Seek-Cards.pdf",
|
||||
"cards.storyLink": "/pdfs/cards/StoryCards.pdf",
|
||||
"cards.dressupLink": "/pdfs/cards/DressupCards.pdf",
|
||||
"cards.pongLink": "/pdfs/cards/PongCards.pdf",
|
||||
"cards.danceLink": "/pdfs/cards/DanceCards.pdf",
|
||||
"cards.catchLink": "/pdfs/cards/CatchCards.pdf",
|
||||
"cards.petLink": "/pdfs/cards/PetCards.pdf",
|
||||
"ttt.MakeItFlyActivityLoc": "/pdfs/cards/FlyCards.pdf",
|
||||
"ttt.MakeItFlyGuideLoc": "/pdfs/guides/FlyGuide.pdf",
|
||||
"ttt.AnimateYourNameActivityLoc": "/pdfs/cards/AnimateYourNameCards.pdf",
|
||||
"ttt.AnimateYourNameGuideLoc": "/pdfs/guides/NameGuide.pdf",
|
||||
"ttt.MakeMusicActivityLoc": "/pdfs/cards/MusicCards.pdf",
|
||||
"ttt.MakeMusicGuideLoc": "/pdfs/guides/MusicGuide.pdf",
|
||||
"ttt.RaceActivityLoc": "/pdfs/cards/RaceGameCards.pdf",
|
||||
"ttt.RaceGuideLoc": "/pdfs/guides/RaceGuide.pdf",
|
||||
"ttt.DanceActivityLoc": "/pdfs/cards/DanceCards.pdf",
|
||||
"ttt.DanceGuideLoc": "/pdfs/guides/DanceGuide.pdf",
|
||||
"ttt.PongActivityLoc": "/pdfs/cards/PongCards.pdf",
|
||||
"ttt.PongGuideLoc": "/pdfs/guides/PongGuide.pdf",
|
||||
"ttt.CatchActivityLoc": "/pdfs/cards/CatchCards.pdf",
|
||||
"ttt.CatchGuideLoc": "/pdfs/guides/CatchGuide.pdf",
|
||||
"ttt.HideAndSeekActivityLoc": "/pdfs/cards/Hide-and-Seek-Cards.pdf",
|
||||
"ttt.HideAndSeekGuideLoc": "/pdfs/guides/Hide-and-Seek-Guide.pdf",
|
||||
"ttt.VirtualPetActivityLoc": "/pdfs/cards/PetCards.pdf",
|
||||
"ttt.VirtualPetGuideLoc": "/pdfs/guides/PetGuide.pdf",
|
||||
"ttt.DressupActivityLoc": "/pdfs/cards/DressupCards.pdf",
|
||||
"ttt.DressupGuideLoc": "/pdfs/guides/DressupGuide.pdf",
|
||||
"ttt.StoryActivityLoc": "/pdfs/cards/StoryCards.pdf",
|
||||
"ttt.StoryGuideLoc": "/pdfs/guides/StoryGuide.pdf"
|
||||
},
|
||||
"ar": {
|
||||
"cards.starterLink": "//cdn.scratch.mit.edu/scratchr2/static/pdfs/help/ar/Scratch2Cards.pdf"
|
||||
},
|
||||
"ca": {
|
||||
"cards.starterLink": "//cdn.scratch.mit.edu/scratchr2/static/pdfs/help/ca/Scratch2Cards.pdf"
|
||||
"cards.nameLink": "/pdfs/cards/ca/AnimateYourNameCards.pdf",
|
||||
"cards.catchLink": "/pdfs/cards/ca/CatchCards.pdf",
|
||||
"cards.danceLink": "/pdfs/cards/ca/DanceCards.pdf",
|
||||
"cards.dressupLink": "/pdfs/cards/ca/DressupCards.pdf",
|
||||
"cards.flyLink": "/pdfs/cards/ca/FlyCards.pdf",
|
||||
"cards.hideLink": "/pdfs/cards/ca/Hide-and-Seek-Cards.pdf",
|
||||
"cards.musicLink": "/pdfs/cards/ca/MusicCards.pdf",
|
||||
"cards.petLink": "/pdfs/cards/ca/PetCards.pdf",
|
||||
"cards.pongLink": "/pdfs/cards/ca/PongCards.pdf",
|
||||
"cards.raceLink": "/pdfs/cards/ca/RaceGameCards.pdf",
|
||||
"cards.starterLink": "/pdfs/cards/ca/Scratch2Cards.pdf",
|
||||
"cards.storyLink": "/pdfs/cards/ca/StoryCards.pdf",
|
||||
"ttt.MakeItFlyActivityLoc": "/pdfs/cards/ca/FlyCards.pdf",
|
||||
"ttt.AnimateYourNameActivityLoc": "/pdfs/cards/ca/AnimateYourNameCards.pdf",
|
||||
"ttt.MakeMusicActivityLoc": "/pdfs/cards/ca/MusicCards.pdf",
|
||||
"ttt.RaceActivityLoc": "/pdfs/cards/ca/RaceGameCards.pdf",
|
||||
"ttt.DanceActivityLoc": "/pdfs/cards/ca/DanceCards.pdf",
|
||||
"ttt.PongActivityLoc": "/pdfs/cards/ca/PongCards.pdf",
|
||||
"ttt.CatchActivityLoc": "/pdfs/cards/ca/CatchCards.pdf",
|
||||
"ttt.HideAndSeekActivityLoc": "/pdfs/cards/ca/Hide-and-Seek-Cards.pdf",
|
||||
"ttt.VirtualPetActivityLoc": "/pdfs/cards/ca/PetCards.pdf",
|
||||
"ttt.DressupActivityLoc": "/pdfs/cards/ca/DressupCards.pdf",
|
||||
"ttt.StoryActivityLoc": "/pdfs/cards/ca/StoryCards.pdf"
|
||||
},
|
||||
"cs": {
|
||||
"cards.starterLink": "//cdn.scratch.mit.edu/scratchr2/static/pdfs/help/cs/Scratch2Cards.pdf"
|
||||
|
@ -26,7 +70,29 @@
|
|||
"cards.starterLink": "//cdn.scratch.mit.edu/scratchr2/static/pdfs/help/de/Scratch2Cards.pdf"
|
||||
},
|
||||
"es": {
|
||||
"cards.starterLink": "//cdn.scratch.mit.edu/scratchr2/static/pdfs/help/es/Scratch2Cards.pdf"
|
||||
"cards.nameLink": "/pdfs/cards/es/AnimateYourNameCards.pdf",
|
||||
"cards.catchLink": "/pdfs/cards/es/CatchCards.pdf",
|
||||
"cards.danceLink": "/pdfs/cards/es/DanceCards.pdf",
|
||||
"cards.dressupLink": "/pdfs/cards/es/DressupCards.pdf",
|
||||
"cards.flyLink": "/pdfs/cards/es/FlyCards.pdf",
|
||||
"cards.hideLink": "/pdfs/cards/es/Hide-and-Seek-Cards.pdf",
|
||||
"cards.musicLink": "/pdfs/cards/es/MusicCards.pdf",
|
||||
"cards.petLink": "/pdfs/cards/es/PetCards.pdf",
|
||||
"cards.pongLink": "/pdfs/cards/es/PongCards.pdf",
|
||||
"cards.raceLink": "/pdfs/cards/es/RaceGameCards.pdf",
|
||||
"cards.starterLink": "/pdfs/cards/es/Scratch2Cards.pdf",
|
||||
"cards.storyLink": "/pdfs/cards/es/StoryCards.pdf",
|
||||
"ttt.MakeItFlyActivityLoc": "/pdfs/cards/es/FlyCards.pdf",
|
||||
"ttt.AnimateYourNameActivityLoc": "/pdfs/cards/es/AnimateYourNameCards.pdf",
|
||||
"ttt.MakeMusicActivityLoc": "/pdfs/cards/es/MusicCards.pdf",
|
||||
"ttt.RaceActivityLoc": "/pdfs/cards/es/RaceGameCards.pdf",
|
||||
"ttt.DanceActivityLoc": "/pdfs/cards/es/DanceCards.pdf",
|
||||
"ttt.PongActivityLoc": "/pdfs/cards/es/PongCards.pdf",
|
||||
"ttt.CatchActivityLoc": "/pdfs/cards/es/CatchCards.pdf",
|
||||
"ttt.HideAndSeekActivityLoc": "/pdfs/cards/es/Hide-and-Seek-Cards.pdf",
|
||||
"ttt.VirtualPetActivityLoc": "/pdfs/cards/es/PetCards.pdf",
|
||||
"ttt.DressupActivityLoc": "/pdfs/cards/es/DressupCards.pdf",
|
||||
"ttt.StoryActivityLoc": "/pdfs/cards/es/StoryCards.pdf"
|
||||
},
|
||||
"fr": {
|
||||
"cards.starterLink": "//cdn.scratch.mit.edu/scratchr2/static/pdfs/help/fr/Scratch2Cards.pdf"
|
||||
|
@ -54,5 +120,32 @@
|
|||
},
|
||||
"sl": {
|
||||
"cards.starterLink": "//cdn.scratch.mit.edu/scratchr2/static/pdfs/help/sl/Scratch2Cards.pdf"
|
||||
},
|
||||
"sv": {
|
||||
"cards.starterLink": "/pdfs/cards/sv/Scratch2Cards.pdf"
|
||||
},
|
||||
"zh-tw": {
|
||||
"cards.nameLink": "/pdfs/cards/zh-tw/AnimateYourNameCards.pdf",
|
||||
"cards.catchLink": "/pdfs/cards/zh-tw/CatchCards.pdf",
|
||||
"cards.danceLink": "/pdfs/cards/zh-tw/DanceCards.pdf",
|
||||
"cards.dressupLink": "/pdfs/cards/zh-tw/DressupCards.pdf",
|
||||
"cards.flyLink": "/pdfs/cards/zh-tw/FlyCards.pdf",
|
||||
"cards.hideLink": "/pdfs/cards/zh-tw/Hide-and-Seek-Cards.pdf",
|
||||
"cards.musicLink": "/pdfs/cards/zh-tw/MusicCards.pdf",
|
||||
"cards.petLink": "/pdfs/cards/zh-tw/PetCards.pdf",
|
||||
"cards.pongLink": "/pdfs/cards/zh-tw/PongCards.pdf",
|
||||
"cards.raceLink": "/pdfs/cards/zh-tw/RaceGameCards.pdf",
|
||||
"cards.storyLink": "/pdfs/cards/zh-tw/StoryCards.pdf",
|
||||
"ttt.MakeItFlyActivityLoc": "/pdfs/cards/zh-tw/FlyCards.pdf",
|
||||
"ttt.AnimateYourNameActivityLoc": "/pdfs/cards/zh-tw/AnimateYourNameCards.pdf",
|
||||
"ttt.MakeMusicActivityLoc": "/pdfs/cards/zh-tw/MusicCards.pdf",
|
||||
"ttt.RaceActivityLoc": "/pdfs/cards/zh-tw/RaceGameCards.pdf",
|
||||
"ttt.DanceActivityLoc": "/pdfs/cards/zh-tw/DanceCards.pdf",
|
||||
"ttt.PongActivityLoc": "/pdfs/cards/zh-tw/PongCards.pdf",
|
||||
"ttt.CatchActivityLoc": "/pdfs/cards/zh-tw/CatchCards.pdf",
|
||||
"ttt.HideAndSeekActivityLoc": "/pdfs/cards/zh-tw/Hide-and-Seek-Cards.pdf",
|
||||
"ttt.VirtualPetActivityLoc": "/pdfs/cards/zh-tw/PetCards.pdf",
|
||||
"ttt.DressupActivityLoc": "/pdfs/cards/zh-tw/DressupCards.pdf",
|
||||
"ttt.StoryActivityLoc": "/pdfs/cards/zh-tw/StoryCards.pdf"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
"start": "make start",
|
||||
"stop": "make stop",
|
||||
"test": "make test",
|
||||
"smoke": "make smoke",
|
||||
"watch": "make watch",
|
||||
"build": "make build",
|
||||
"dev": "make watch && make start &"
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
border-radius: 0 0 5px 5px;
|
||||
background-color: $ui-blue;
|
||||
padding: 10px;
|
||||
min-width: 160px;
|
||||
max-width: 260px;
|
||||
overflow: visible;
|
||||
color: $type-white;
|
||||
|
|
|
@ -306,7 +306,8 @@ module.exports = {
|
|||
getDefaultProps: function () {
|
||||
return {
|
||||
waiting: false,
|
||||
description: null
|
||||
description: null,
|
||||
birthOffset: 0
|
||||
};
|
||||
},
|
||||
getInitialState: function () {
|
||||
|
@ -324,13 +325,26 @@ module.exports = {
|
|||
},
|
||||
getYearOptions: function () {
|
||||
return Array.apply(null, Array(100)).map(function (v, id) {
|
||||
var year = new Date().getFullYear() - id;
|
||||
var year = new Date().getFullYear() - (id + this.props.birthOffset);
|
||||
return {value: year, label: year};
|
||||
});
|
||||
}.bind(this));
|
||||
},
|
||||
onChooseGender: function (name, gender) {
|
||||
this.setState({otherDisabled: gender !== 'other'});
|
||||
},
|
||||
onValidSubmit: function (formData, reset, invalidate) {
|
||||
var birthdate = new Date(
|
||||
formData.user.birth.year,
|
||||
formData.user.birth.month - 1,
|
||||
1
|
||||
);
|
||||
if (((Date.now() - birthdate) / (24*3600*1000*365.25)) < this.props.birthOffset) {
|
||||
return invalidate({
|
||||
'user.birth.year': this.props.intl.formatMessage({id: 'teacherRegistration.validationAge'})
|
||||
});
|
||||
}
|
||||
return this.props.onNextStep(formData);
|
||||
},
|
||||
render: function () {
|
||||
var formatMessage = this.props.intl.formatMessage;
|
||||
return (
|
||||
|
@ -348,7 +362,7 @@ module.exports = {
|
|||
tipContent={formatMessage({id: 'registration.nameStepTooltip'})} />
|
||||
</p>
|
||||
<Card>
|
||||
<Form onValidSubmit={this.props.onNextStep}>
|
||||
<Form onValidSubmit={this.onValidSubmit}>
|
||||
<Select label={formatMessage({id: 'general.birthMonth'})}
|
||||
name="user.birth.month"
|
||||
options={this.getMonthOptions()}
|
||||
|
|
|
@ -176,14 +176,14 @@
|
|||
{
|
||||
"name": "jobs",
|
||||
"pattern": "^/jobs/?$",
|
||||
"routeAlias": "/jobs/?$",
|
||||
"routeAlias": "/jobs(/moderator)?/?$",
|
||||
"view": "jobs/jobs",
|
||||
"title": "Jobs"
|
||||
},
|
||||
{
|
||||
"name": "jobs-moderator",
|
||||
"pattern": "^/jobs/moderator/?$",
|
||||
"routeAlias": "/jobs/?$",
|
||||
"routeAlias": "/jobs(/moderator)?/?$",
|
||||
"view": "jobs/moderator/moderator",
|
||||
"title": "Community Moderator"
|
||||
},
|
||||
|
|
|
@ -12,7 +12,7 @@ var TitleBanner = require('../../components/title-banner/title-banner.jsx');
|
|||
require('./developers.scss');
|
||||
|
||||
var Developers = React.createClass({
|
||||
type: 'About',
|
||||
type: 'Developers',
|
||||
render: function () {
|
||||
return (
|
||||
<div className="developers">
|
||||
|
|
|
@ -36,5 +36,6 @@
|
|||
"teacherRegistration.howUseScratch": "How do you plan to use Scratch at your organization?",
|
||||
"teacherRegistration.emailStepTitle": "Email Address",
|
||||
"teacherRegistration.emailStepDescription": "We will send you a confirmation email that will allow you to access your Scratch Teacher Account.",
|
||||
"teacherRegistration.validationEmailMatch": "The emails do not match"
|
||||
"teacherRegistration.validationEmailMatch": "The emails do not match",
|
||||
"teacherRegistration.validationAge": "Sorry, teachers must be at least 13 years old"
|
||||
}
|
||||
|
|
|
@ -95,7 +95,8 @@ var TeacherRegistration = intl.injectIntl(React.createClass({
|
|||
<Steps.UsernameStep onNextStep={this.advanceStep}
|
||||
waiting={this.state.waiting} />
|
||||
<Steps.DemographicsStep onNextStep={this.advanceStep}
|
||||
waiting={this.state.waiting} />
|
||||
waiting={this.state.waiting}
|
||||
birthOffset={13} />
|
||||
<Steps.NameStep onNextStep={this.advanceStep}
|
||||
waiting={this.state.waiting} />
|
||||
<Steps.PhoneNumberStep onNextStep={this.advanceStep}
|
||||
|
|
BIN
static/pdfs/cards/ca/AnimateYourNameCards.pdf
Executable file
BIN
static/pdfs/cards/ca/AnimateYourNameCards.pdf
Executable file
Binary file not shown.
BIN
static/pdfs/cards/ca/CatchCards.pdf
Executable file
BIN
static/pdfs/cards/ca/CatchCards.pdf
Executable file
Binary file not shown.
BIN
static/pdfs/cards/ca/DanceCards.pdf
Executable file
BIN
static/pdfs/cards/ca/DanceCards.pdf
Executable file
Binary file not shown.
BIN
static/pdfs/cards/ca/DressupCards.pdf
Executable file
BIN
static/pdfs/cards/ca/DressupCards.pdf
Executable file
Binary file not shown.
BIN
static/pdfs/cards/ca/FlyCards.pdf
Executable file
BIN
static/pdfs/cards/ca/FlyCards.pdf
Executable file
Binary file not shown.
BIN
static/pdfs/cards/ca/Hide-and-Seek-Cards.pdf
Executable file
BIN
static/pdfs/cards/ca/Hide-and-Seek-Cards.pdf
Executable file
Binary file not shown.
BIN
static/pdfs/cards/ca/MusicCards.pdf
Executable file
BIN
static/pdfs/cards/ca/MusicCards.pdf
Executable file
Binary file not shown.
BIN
static/pdfs/cards/ca/PetCards.pdf
Executable file
BIN
static/pdfs/cards/ca/PetCards.pdf
Executable file
Binary file not shown.
BIN
static/pdfs/cards/ca/PongCards.pdf
Executable file
BIN
static/pdfs/cards/ca/PongCards.pdf
Executable file
Binary file not shown.
BIN
static/pdfs/cards/ca/RaceGameCards.pdf
Executable file
BIN
static/pdfs/cards/ca/RaceGameCards.pdf
Executable file
Binary file not shown.
BIN
static/pdfs/cards/ca/Scratch2Cards.pdf
Normal file
BIN
static/pdfs/cards/ca/Scratch2Cards.pdf
Normal file
Binary file not shown.
BIN
static/pdfs/cards/ca/StoryCards.pdf
Executable file
BIN
static/pdfs/cards/ca/StoryCards.pdf
Executable file
Binary file not shown.
BIN
static/pdfs/cards/es/AnimateYourNameCards.pdf
Executable file
BIN
static/pdfs/cards/es/AnimateYourNameCards.pdf
Executable file
Binary file not shown.
BIN
static/pdfs/cards/es/CatchCards.pdf
Normal file
BIN
static/pdfs/cards/es/CatchCards.pdf
Normal file
Binary file not shown.
BIN
static/pdfs/cards/es/DanceCards.pdf
Executable file
BIN
static/pdfs/cards/es/DanceCards.pdf
Executable file
Binary file not shown.
BIN
static/pdfs/cards/es/DressupCards.pdf
Executable file
BIN
static/pdfs/cards/es/DressupCards.pdf
Executable file
Binary file not shown.
BIN
static/pdfs/cards/es/FlyCards.pdf
Normal file
BIN
static/pdfs/cards/es/FlyCards.pdf
Normal file
Binary file not shown.
BIN
static/pdfs/cards/es/Hide-and-Seek-Cards.pdf
Executable file
BIN
static/pdfs/cards/es/Hide-and-Seek-Cards.pdf
Executable file
Binary file not shown.
BIN
static/pdfs/cards/es/MusicCards.pdf
Normal file
BIN
static/pdfs/cards/es/MusicCards.pdf
Normal file
Binary file not shown.
BIN
static/pdfs/cards/es/PetCards.pdf
Executable file
BIN
static/pdfs/cards/es/PetCards.pdf
Executable file
Binary file not shown.
BIN
static/pdfs/cards/es/PongCards.pdf
Executable file
BIN
static/pdfs/cards/es/PongCards.pdf
Executable file
Binary file not shown.
BIN
static/pdfs/cards/es/RaceGameCards.pdf
Executable file
BIN
static/pdfs/cards/es/RaceGameCards.pdf
Executable file
Binary file not shown.
BIN
static/pdfs/cards/es/Scratch2Cards.pdf
Normal file
BIN
static/pdfs/cards/es/Scratch2Cards.pdf
Normal file
Binary file not shown.
BIN
static/pdfs/cards/es/StoryCards.pdf
Executable file
BIN
static/pdfs/cards/es/StoryCards.pdf
Executable file
Binary file not shown.
BIN
static/pdfs/cards/sv/Scratch2Cards.pdf
Normal file
BIN
static/pdfs/cards/sv/Scratch2Cards.pdf
Normal file
Binary file not shown.
BIN
static/pdfs/cards/zh-tw/AnimateYourNameCards.pdf
Executable file
BIN
static/pdfs/cards/zh-tw/AnimateYourNameCards.pdf
Executable file
Binary file not shown.
BIN
static/pdfs/cards/zh-tw/CatchCards.pdf
Executable file
BIN
static/pdfs/cards/zh-tw/CatchCards.pdf
Executable file
Binary file not shown.
BIN
static/pdfs/cards/zh-tw/DanceCards.pdf
Executable file
BIN
static/pdfs/cards/zh-tw/DanceCards.pdf
Executable file
Binary file not shown.
BIN
static/pdfs/cards/zh-tw/DressupCards.pdf
Executable file
BIN
static/pdfs/cards/zh-tw/DressupCards.pdf
Executable file
Binary file not shown.
BIN
static/pdfs/cards/zh-tw/FlyCards.pdf
Executable file
BIN
static/pdfs/cards/zh-tw/FlyCards.pdf
Executable file
Binary file not shown.
BIN
static/pdfs/cards/zh-tw/Hide-and-Seek-Cards.pdf
Executable file
BIN
static/pdfs/cards/zh-tw/Hide-and-Seek-Cards.pdf
Executable file
Binary file not shown.
BIN
static/pdfs/cards/zh-tw/MusicCards.pdf
Executable file
BIN
static/pdfs/cards/zh-tw/MusicCards.pdf
Executable file
Binary file not shown.
BIN
static/pdfs/cards/zh-tw/PetCards.pdf
Normal file
BIN
static/pdfs/cards/zh-tw/PetCards.pdf
Normal file
Binary file not shown.
BIN
static/pdfs/cards/zh-tw/PongCards.pdf
Normal file
BIN
static/pdfs/cards/zh-tw/PongCards.pdf
Normal file
Binary file not shown.
BIN
static/pdfs/cards/zh-tw/RaceGameCards.pdf
Normal file
BIN
static/pdfs/cards/zh-tw/RaceGameCards.pdf
Normal file
Binary file not shown.
BIN
static/pdfs/cards/zh-tw/StoryCards.pdf
Executable file
BIN
static/pdfs/cards/zh-tw/StoryCards.pdf
Executable file
Binary file not shown.
|
@ -1,33 +0,0 @@
|
|||
/*
|
||||
* Checks that the links in the navbar on the homepage have the right URLs to redirect to
|
||||
*
|
||||
* Test cases: https://github.com/LLK/scratch-www/wiki/Most-Important-Workflows#Create_should_take_you_to_the_editor
|
||||
*/
|
||||
|
||||
var tap=require('tap');
|
||||
var seleniumWebdriver = require('selenium-webdriver');
|
||||
|
||||
//chrome driver
|
||||
var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build();
|
||||
//open scratch.ly in a new instance of the browser
|
||||
driver.get('https://scratch.ly');
|
||||
|
||||
//find the create link within the navbar
|
||||
//the create link depends on whether the user is signed in or not (tips window opens)
|
||||
tap.test('checkCreateLinkWhenSignedOut', function (t) {
|
||||
var xPathLink = '//li[contains(@class, "link") and contains(@class, "create")]/a';
|
||||
var createLinkSignedOut = driver.findElement(seleniumWebdriver.By.xpath(xPathLink));
|
||||
createLinkSignedOut.getAttribute('href').then( function (url) {
|
||||
//expected value of the href
|
||||
var expectedHref = '/projects/editor/?tip_bar=home';
|
||||
//the create href should match `/projects/editor/?tip_bar=home`
|
||||
//the create href should be at the end of the URL
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// close the instance of the browser
|
||||
driver.quit();
|
||||
|
301
test/integration/smoke-testing/test_footer_links.js
Normal file
301
test/integration/smoke-testing/test_footer_links.js
Normal file
|
@ -0,0 +1,301 @@
|
|||
/*
|
||||
* Checks that the links in the footer on the homepage have the right URLs to redirect to
|
||||
*
|
||||
* Test cases: https://github.com/LLK/scratch-www/wiki/Most-Important-Workflows
|
||||
*/
|
||||
|
||||
require('chromedriver');
|
||||
var tap = require('tap');
|
||||
var seleniumWebdriver = require('selenium-webdriver');
|
||||
|
||||
// Selenium's promise driver will be deprecated, so we should not rely on it
|
||||
seleniumWebdriver.SELENIUM_PROMISE_MANAGER=0;
|
||||
|
||||
//chrome driver
|
||||
var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build();
|
||||
|
||||
var rootUrl = process.env.ROOT_URL || 'https://scratch.ly';
|
||||
|
||||
//timeout for each test; timeout for suite set at command line level
|
||||
var options = { timeout: 20000 };
|
||||
|
||||
//number of tests in the plan
|
||||
tap.plan(24);
|
||||
|
||||
tap.tearDown(function () {
|
||||
//quit the instance of the browser
|
||||
driver.quit();
|
||||
});
|
||||
|
||||
tap.beforeEach(function () {
|
||||
//load the page with the driver
|
||||
return driver.get(rootUrl);
|
||||
});
|
||||
|
||||
// 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?
|
||||
return driver.wait(seleniumWebdriver.until.elementLocated(seleniumWebdriver.By.id('view')))
|
||||
.then( function () {
|
||||
return driver.wait(seleniumWebdriver.until.elementLocated(seleniumWebdriver.By
|
||||
.id('footer')))
|
||||
.then( function () {
|
||||
return driver.findElement(seleniumWebdriver.By.linkText(linkText)); })
|
||||
.then( function (element) {
|
||||
return element.click(); })
|
||||
.then(function () {
|
||||
return driver.getCurrentUrl();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ==== ABOUT SCRATCH column ====
|
||||
|
||||
// ABOUT SCRATCH
|
||||
tap.test('clickAboutScratchLink', options, function (t) {
|
||||
var linkText = 'About Scratch';
|
||||
var expectedHref = '/about';
|
||||
clickFooterLinks(linkText).then( function (url) {
|
||||
//the href should be at the end of the URL
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// FOR PARENTS
|
||||
tap.test('clickForParentsLink', options, function (t) {
|
||||
var linkText = 'For Parents';
|
||||
var expectedHref = '/parents/';
|
||||
clickFooterLinks(linkText).then( function (url) {
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// FOR EDUCATORS
|
||||
tap.test('clickForEducatorsLink', options, function (t) {
|
||||
var linkText = 'For Educators';
|
||||
var expectedHref = '/educators';
|
||||
clickFooterLinks(linkText).then( function (url) {
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// FOR DEVELOPERS
|
||||
tap.test('clickForDevelopersScratchLink', options, function (t) {
|
||||
var linkText = 'For Developers';
|
||||
var expectedHref = '/developers';
|
||||
clickFooterLinks(linkText).then( function (url) {
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// CREDITS
|
||||
tap.test('clickCreditsLink', options, function (t) {
|
||||
var linkText = 'Credits';
|
||||
var expectedHref = '/info/credits';
|
||||
clickFooterLinks(linkText).then( function (url) {
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// JOBS
|
||||
tap.test('clickJobsLink', options, function (t) {
|
||||
var linkText = 'Jobs';
|
||||
var expectedHref = '/jobs';
|
||||
clickFooterLinks(linkText).then( function (url) {
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// PRESS
|
||||
tap.test('clickPressLink', options, function (t) {
|
||||
var linkText = 'Press';
|
||||
var expectedHref = 'https://wiki.scratch.mit.edu/wiki/Scratch_Press';
|
||||
clickFooterLinks(linkText).then( function (url) {
|
||||
t.equal(url, expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// ==== COMMUNITY column ====
|
||||
|
||||
// COMMUNITY GUIDELINES
|
||||
tap.test('clickCommunityGuidelinesLink', options, function (t) {
|
||||
var linkText = 'Community Guidelines';
|
||||
var expectedHref = '/community_guidelines';
|
||||
clickFooterLinks(linkText).then( function (url) {
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// DISCUSSION FORUMS
|
||||
tap.test('clickDiscussionForumsLink', options, function (t) {
|
||||
var linkText = 'Discussion Forums';
|
||||
var expectedHref = '/discuss/';
|
||||
clickFooterLinks(linkText).then( function (url) {
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// SCRATCH WIKI
|
||||
tap.test('clickScratchWikiLink', options, function (t) {
|
||||
var linkText = 'Scratch Wiki';
|
||||
var expectedHref = 'https://wiki.scratch.mit.edu/wiki/Scratch_Wiki_Home';
|
||||
clickFooterLinks(linkText).then( function (url) {
|
||||
t.equal(url, expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// STATISTICS
|
||||
tap.test('clickStatisticsLink', options, function (t) {
|
||||
var linkText = 'Statistics';
|
||||
var expectedHref = '/statistics/';
|
||||
clickFooterLinks(linkText).then( function (url) {
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// ==== SUPPORT column ====
|
||||
|
||||
// HELP PAGE
|
||||
tap.test('clickHelpPageLink', options, function (t) {
|
||||
var linkText = 'Help Page';
|
||||
var expectedHref = '/help/';
|
||||
clickFooterLinks(linkText).then( function (url) {
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// FAQ
|
||||
tap.test('clickFAQLink', options, function (t) {
|
||||
var linkText = 'FAQ';
|
||||
var expectedHref = '/info/faq';
|
||||
clickFooterLinks(linkText).then( function (url) {
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// OFFLINE EDITOR
|
||||
tap.test('clickOfflineEditorLink', options, function (t) {
|
||||
var linkText = 'Offline Editor';
|
||||
var expectedHref = '/scratch2download/';
|
||||
clickFooterLinks(linkText).then( function (url) {
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// CONTACT US
|
||||
tap.test('clickContactUsLink', options, function (t) {
|
||||
var linkText = 'Contact Us';
|
||||
var expectedHref = '/contact-us/';
|
||||
clickFooterLinks(linkText).then( function (url) {
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// DONATE
|
||||
tap.test('clickDonateLink', options, function (t) {
|
||||
var linkText = 'Donate';
|
||||
var expectedHref = 'https://secure.donationpay.org/scratchfoundation/';
|
||||
clickFooterLinks(linkText).then( function (url) {
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// ==== LEGAL column ====
|
||||
|
||||
// TERMS OF USE
|
||||
tap.test('clickTermsOfUseLink', options, function (t) {
|
||||
var linkText = 'Terms of Use';
|
||||
var expectedHref = '/terms_of_use';
|
||||
clickFooterLinks(linkText).then( function (url) {
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// PRIVACY POLICY
|
||||
tap.test('clickPrivacyPolicyLink', options, function (t) {
|
||||
var linkText = 'Privacy Policy';
|
||||
var expectedHref = '/privacy_policy';
|
||||
clickFooterLinks(linkText).then( function (url) {
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// DMCA
|
||||
tap.test('clickDMCALink', options, function (t) {
|
||||
var linkText = 'DMCA';
|
||||
var expectedHref = '/DMCA';
|
||||
clickFooterLinks(linkText).then( function (url) {
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// ==== SCRATCH FAMILY column ====
|
||||
|
||||
// SCRATCH ED (SCRATCHED)
|
||||
tap.test('clickScratchEdLink', options, function (t) {
|
||||
var linkText = 'ScratchEd';
|
||||
var expectedHref = 'http://scratched.gse.harvard.edu/';
|
||||
clickFooterLinks(linkText).then( function (url) {
|
||||
t.equal(url, expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// SCRATCH JR (SCRATCHJR)
|
||||
tap.test('clickScratchJrLink', options, function (t) {
|
||||
var linkText = 'ScratchJr';
|
||||
var expectedHref = 'http://www.scratchjr.org/';
|
||||
clickFooterLinks(linkText).then( function (url) {
|
||||
t.equal(url, expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// SCRATCH DAY
|
||||
tap.test('clickScratchDayLink', options, function (t) {
|
||||
var linkText = 'Scratch Day';
|
||||
var expectedHref = 'https://day.scratch.mit.edu/';
|
||||
clickFooterLinks(linkText).then( function (url) {
|
||||
t.equal(url, expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// SCRATCH CONFERENCE
|
||||
tap.test('clickScratchConferenceLink', options, function (t) {
|
||||
var linkText = 'Scratch Conference';
|
||||
var expectedHref = '/conference';
|
||||
clickFooterLinks(linkText).then( function (url) {
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// SCRATCH FOUNDATION
|
||||
tap.test('clickScratchFoundationLink', options, function (t) {
|
||||
var linkText = 'Scratch Foundation';
|
||||
var expectedHref = 'https://www.scratchfoundation.org/';
|
||||
clickFooterLinks(linkText).then( function (url) {
|
||||
t.equal(url, expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
131
test/integration/smoke-testing/test_navbar_links.js
Normal file
131
test/integration/smoke-testing/test_navbar_links.js
Normal file
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* Checks that the links in the navbar on the homepage have the right URLs to redirect to
|
||||
*
|
||||
* Test cases: https://github.com/LLK/scratch-www/wiki/Most-Important-Workflows
|
||||
*/
|
||||
|
||||
require('chromedriver');
|
||||
var seleniumWebdriver = require('selenium-webdriver');
|
||||
var tap = require('tap');
|
||||
|
||||
// Selenium's promise driver will be deprecated, so we should not rely on it
|
||||
seleniumWebdriver.SELENIUM_PROMISE_MANAGER=0;
|
||||
|
||||
//Set test url through environment variable
|
||||
var rootUrl = process.env.ROOT_URL || 'https://scratch.ly';
|
||||
|
||||
//chrome driver
|
||||
var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build();
|
||||
|
||||
//number of tests in the plan
|
||||
tap.plan(8);
|
||||
|
||||
tap.tearDown(function () {
|
||||
//quit the instance of the browser
|
||||
driver.quit();
|
||||
});
|
||||
|
||||
tap.beforeEach(function () {
|
||||
//load the page with the driver
|
||||
return driver.get(rootUrl);
|
||||
});
|
||||
|
||||
// ==== Links in navbar ====
|
||||
|
||||
//the create link changes depending on whether the user is signed in or not (tips window opens)
|
||||
tap.test('checkCreateLinkWhenSignedOut', function (t) {
|
||||
var xPathLink = '//li[contains(@class, "link") and contains(@class, "create")]/a';
|
||||
var expectedHref = '/projects/editor/?tip_bar=home';
|
||||
driver.findElement(seleniumWebdriver.By.xpath(xPathLink))
|
||||
.then( function (element) {
|
||||
return element.getAttribute('href');})
|
||||
.then( function (url) {
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
tap.test('checkExploreLinkWhenSignedOut', function (t) {
|
||||
var xPathLink = '//li[contains(@class, "link") and contains(@class, "explore")]/a';
|
||||
var expectedHref = '/explore/projects/all';
|
||||
driver.findElement(seleniumWebdriver.By.xpath(xPathLink))
|
||||
.then( function (element) {
|
||||
return element.getAttribute('href');})
|
||||
.then( function (url) {
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
tap.test('checkDiscussLinkWhenSignedOut', function (t) {
|
||||
var xPathLink = '//li[contains(@class, "link") and contains(@class, "discuss")]/a';
|
||||
var expectedHref = '/discuss';
|
||||
driver.findElement(seleniumWebdriver.By.xpath(xPathLink))
|
||||
.then( function (element) {
|
||||
return element.getAttribute('href');})
|
||||
.then( function (url) {
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
tap.test('checkAboutLinkWhenSignedOut', function (t) {
|
||||
var xPathLink = '//li[contains(@class, "link") and contains(@class, "about")]/a';
|
||||
var expectedHref = '/about';
|
||||
driver.findElement(seleniumWebdriver.By.xpath(xPathLink))
|
||||
.then( function (element) {
|
||||
return element.getAttribute('href');})
|
||||
.then( function (url) {
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
tap.test('checkHelpLinkWhenSignedOut', function (t) {
|
||||
var xPathLink = '//li[contains(@class, "link") and contains(@class, "help")]/a';
|
||||
var expectedHref = '/help';
|
||||
driver.findElement(seleniumWebdriver.By.xpath(xPathLink))
|
||||
.then( function (element) {
|
||||
return element.getAttribute('href');})
|
||||
.then( function (url) {
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// ==== Search bar ====
|
||||
|
||||
tap.test('checkSearchBar', function (t) {
|
||||
var xPathLink = '//input[@id="frc-q-1088"]';
|
||||
// search bar should exist
|
||||
driver.findElement(seleniumWebdriver.By.xpath(xPathLink)).then( function (element) {
|
||||
t.ok(element);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// ==== Join Scratch & Sign In ====
|
||||
|
||||
tap.test('checkJoinScratchLinkWhenSignedOut', function (t) {
|
||||
var xPathLink = '//li[contains(@class, "link") and contains(@class, "right") and contains(@class, "join")]/a';
|
||||
var expectedText = 'Join Scratch';
|
||||
driver.findElement(seleniumWebdriver.By.xpath(xPathLink))
|
||||
.then( function (element) {
|
||||
return element.getText('a');})
|
||||
.then( function (text) {
|
||||
t.equal(text, expectedText);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
tap.test('checkSignInLinkWhenSignedOut', function (t) {
|
||||
var xPathLink = '//li[contains(@class, "link") and contains(@class, "right") and contains(@class, "login-item")]/a';
|
||||
var expectedText = 'Sign in';
|
||||
driver.findElement(seleniumWebdriver.By.xpath(xPathLink))
|
||||
.then( function (element) {
|
||||
return element.getText('a');})
|
||||
.then( function (text) {
|
||||
t.equal(text, expectedText);
|
||||
t.end();
|
||||
});
|
||||
});
|
94
test/integration/smoke-testing/test_project_rows.js
Normal file
94
test/integration/smoke-testing/test_project_rows.js
Normal file
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* Checks that the some of the homepage rows on the homepage are displayed and
|
||||
* contents have the right URLs to redirect to
|
||||
*
|
||||
* Test cases: https://github.com/LLK/scratch-www/wiki/Most-Important-Workflows
|
||||
*/
|
||||
|
||||
require('chromedriver');
|
||||
var tap = require('tap');
|
||||
var seleniumWebdriver = require('selenium-webdriver');
|
||||
|
||||
// Selenium's promise driver will be deprecated, so we should not rely on it
|
||||
seleniumWebdriver.SELENIUM_PROMISE_MANAGER=0;
|
||||
|
||||
//chrome driver
|
||||
var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build();
|
||||
|
||||
var rootUrl = process.env.ROOT_URL || 'https://scratch.ly';
|
||||
|
||||
//number of tests in the plan
|
||||
tap.plan(4);
|
||||
|
||||
tap.tearDown(function () {
|
||||
//quit the instance of the browser
|
||||
driver.quit();
|
||||
});
|
||||
|
||||
tap.beforeEach(function () {
|
||||
//load the page with the driver
|
||||
return driver.get(rootUrl);
|
||||
});
|
||||
|
||||
//checks that the title of the first row is Featured Projects
|
||||
tap.test('checkFeaturedProjectsRowTitleWhenSignedOut', function (t) {
|
||||
var xPathLink = '//div[@class="box"]/div[@class="box-header"]/h4';
|
||||
driver.findElement(seleniumWebdriver.By.xpath(xPathLink))
|
||||
.then( function (element) {
|
||||
element.getText('h4')
|
||||
.then( function (text) {
|
||||
//expected value of the title text
|
||||
var expectedText = 'Featured Projects';
|
||||
t.equal(text, expectedText);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
//checks that the link for a project makes sense
|
||||
tap.test('checkFeaturedProjectsRowLinkWhenSignedOut', function (t) {
|
||||
var xPathLink = '//div[contains(@class, "thumbnail") ' +
|
||||
'and contains(@class, "project") and contains(@class, "slick-slide") ' +
|
||||
'and contains(@class, "slick-active")]/a[@class="thumbnail-image"]';
|
||||
driver.wait(seleniumWebdriver.until
|
||||
.elementLocated(seleniumWebdriver.By.xpath(xPathLink)))
|
||||
.then( function (element) {
|
||||
element.getAttribute('href')
|
||||
.then( function (url) {
|
||||
//expected pattern for the project URL
|
||||
//since I don't know the length of the project ID number
|
||||
var expectedUrlRegExp = new RegExp('/projects/.*[0-9].*/?');
|
||||
t.match(url, expectedUrlRegExp);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
//checks that the title of the 2nd row is Featured Studios
|
||||
tap.test('checkFeaturedStudiosRowWhenSignedOut', function (t) {
|
||||
var xPathLink = '//div[@class="box"][2]/div[@class="box-header"]/h4';
|
||||
driver.findElement(seleniumWebdriver.By.xpath(xPathLink))
|
||||
.then(function (element) {
|
||||
element.getText('h4')
|
||||
.then( function (text) {
|
||||
var expectedText = 'Featured Studios';
|
||||
t.equal(text, expectedText);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
//checks that the link for a studio makes sense
|
||||
tap.test('checkFeaturedStudiosRowLinkWhenSignedOut', function (t) {
|
||||
var xPathLink = '//div[contains(@class, "thumbnail") and contains(@class, "gallery") ' +
|
||||
'and contains(@class, "slick-slide") and contains(@class, "slick-active")]/a[@class="thumbnail-image"]';
|
||||
driver.findElement(seleniumWebdriver.By.xpath(xPathLink))
|
||||
.then(function (element) {
|
||||
element.getAttribute('href')
|
||||
.then( function (url) {
|
||||
var expectedUrlRegExp = new RegExp('/studios/.*[0-9].*/?');
|
||||
t.match(url, expectedUrlRegExp);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
});
|
103
test/unit/test_fastly_config_methods.js
Normal file
103
test/unit/test_fastly_config_methods.js
Normal file
|
@ -0,0 +1,103 @@
|
|||
var defaults = require('lodash.defaults');
|
||||
var fastlyConfig = require('../../bin/lib/fastly-config-methods');
|
||||
var route_json = require('../../src/routes.json');
|
||||
var tap = require('tap');
|
||||
|
||||
var testRoutes = [
|
||||
{
|
||||
name: 'less-traveled',
|
||||
pattern: '^/?$',
|
||||
routeAlias: '/?$',
|
||||
view: 'less-traveled/less-traveled',
|
||||
title: 'Robert Frost Goes Here'
|
||||
},
|
||||
{
|
||||
name: 'more-traveled',
|
||||
pattern: '^/more?$',
|
||||
routeAlias: '/more?$',
|
||||
view: 'more-traveled/more-traveled',
|
||||
title: 'Robert Frost Does Not Go Here'
|
||||
}
|
||||
];
|
||||
|
||||
var routes = route_json.map(function (route) {
|
||||
return defaults({}, {pattern: fastlyConfig.expressPatternToRegex(route.pattern)}, route);
|
||||
});
|
||||
var extraAppRoutes = [
|
||||
// Homepage with querystring.
|
||||
// TODO: Should this be added for every route?
|
||||
'/\\?',
|
||||
// View html
|
||||
'/[^\/]*\.html$'
|
||||
];
|
||||
|
||||
|
||||
tap.test('getStaticPaths', function (t) {
|
||||
var staticPaths = fastlyConfig.getStaticPaths(__dirname, '../../build/*');
|
||||
t.type(staticPaths, 'object');
|
||||
t.end();
|
||||
});
|
||||
|
||||
tap.test('getViewPaths', function (t) {
|
||||
var viewPaths = fastlyConfig.getViewPaths(testRoutes);
|
||||
t.type(viewPaths, 'object');
|
||||
t.equal(viewPaths[0], '/?$');
|
||||
t.equal(viewPaths[1], '/more?$');
|
||||
t.end();
|
||||
});
|
||||
|
||||
tap.test('pathsToCondition', function (t) {
|
||||
var condition = fastlyConfig.pathsToCondition(['/?$', '/more?$']);
|
||||
t.type(condition, 'string');
|
||||
t.equal(condition, 'req.url~"^(/?$|/more?$)"');
|
||||
t.end();
|
||||
});
|
||||
|
||||
tap.test('getAppRouteCondition', function (t) {
|
||||
var condition = fastlyConfig.getAppRouteCondition('../../build/*', routes, extraAppRoutes, __dirname);
|
||||
t.type(condition, 'string');
|
||||
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) {
|
||||
var ttl = fastlyConfig.setResponseTTL('itsactuallyttyl');
|
||||
t.equal(ttl, '' +
|
||||
'if (itsactuallyttyl) {\n' +
|
||||
' set beresp.ttl = 0s;\n' +
|
||||
' set beresp.grace = 0s;\n' +
|
||||
' return(pass);\n' +
|
||||
'}\n'
|
||||
);
|
||||
t.end();
|
||||
});
|
Loading…
Reference in a new issue