test: mock cross-fetch for download-known-assets, too

This commit is contained in:
Christopher Willis-Ford 2023-05-02 13:41:17 -07:00
parent f6a263d8c1
commit a7999828b5
11 changed files with 399 additions and 37 deletions

View file

@ -1,5 +1,6 @@
const TextEncoder = require('util').TextEncoder;
const crossFetch = jest.requireActual('cross-fetch');
const knownAssets = require('../fixtures/known-assets.js');
const Headers = crossFetch.Headers;
const successText = 'successful response';
@ -38,23 +39,31 @@ const mockFetch = (resource, options) => {
options.mockFetchTestData.headers = new Headers(options.headers);
options.mockFetchTestData.headersCount = Array.from(options.mockFetchTestData.headers).length;
}
switch (resource) {
case '200':
const assetInfo = knownAssets[resource];
if (assetInfo) {
results.ok = true;
results.status = 200;
results.text = () => Promise.resolve(successText);
results.arrayBuffer = () => Promise.resolve(new TextEncoder().encode(successText));
break;
case '404':
results.ok = false;
results.status = 404;
break;
case '500':
results.ok = false;
results.status = 500;
break;
default:
throw new Error('unimplemented');
results.arrayBuffer = () => Promise.resolve(assetInfo.content);
} else {
switch (resource) {
case '200':
results.ok = true;
results.status = 200;
results.text = () => Promise.resolve(successText);
results.arrayBuffer = () => Promise.resolve(new TextEncoder().encode(successText));
break;
case '404':
results.ok = false;
results.status = 404;
break;
case '500':
results.ok = false;
results.status = 500;
break;
default:
throw new Error(`mockFetch doesn't know how to download: ${resource}`);
}
}
return Promise.resolve(results);
};

3
test/fixtures/.gitattributes vendored Normal file
View file

@ -0,0 +1,3 @@
# Assets in this directory must be preserved exactly
# to ensure the MD5 hash matches the expected value.
* binary

196
test/fixtures/assets/117504922.json vendored Executable file
View file

@ -0,0 +1,196 @@
{
"objName": "Stage",
"costumes": [{
"costumeName": "backdrop1",
"baseLayerID": -1,
"baseLayerMD5": "ba5d7bbc200d7925664a0878421c4ed2.png",
"bitmapResolution": 2,
"rotationCenterX": 480,
"rotationCenterY": 360
}],
"currentCostumeIndex": 0,
"penLayerMD5": "5c81a336fab8be57adc039a8a2b33ca9.png",
"penLayerID": -1,
"tempoBPM": 60,
"videoAlpha": 0.5,
"children": [{
"objName": "seymour cropped",
"scripts": [[89, 102, [["whenGreenFlag"], ["setSizeTo:", 85]]]],
"sounds": [{
"soundName": "pop",
"soundID": -1,
"md5": "83a9787d4cb6f3b7632b4ddfebf74367.wav",
"sampleCount": 258,
"rate": 11025,
"format": ""
}],
"costumes": [{
"costumeName": "SeymourPapert001",
"baseLayerID": -1,
"baseLayerMD5": "2c8559602a79c7ec929636c3a5ea0291.png",
"bitmapResolution": 2,
"rotationCenterX": 204,
"rotationCenterY": 262
}],
"currentCostumeIndex": 0,
"scratchX": 115,
"scratchY": -28,
"scale": 0.85,
"direction": 90,
"rotationStyle": "normal",
"isDraggable": false,
"indexInLibrary": 2,
"visible": true,
"spriteInfo": {
}
},
{
"objName": "Sprite1",
"scripts": [[48,
24,
[["whenGreenFlag"],
["setSizeTo:", 110],
["wait:elapsed:from:", 1],
["say:", "Hi. I want to introduce you to Seymour Papert."],
["doPlaySoundAndWait", "papert1"],
["say:", "Seymour developed Logo, the first programming language for kids"],
["doPlaySoundAndWait", "papert2"],
["say:", "He believed that kids learn best"],
["doPlaySoundAndWait", "papert3"],
["say:", "when they are given the chance to create things and express themselves"],
["doPlaySoundAndWait", "papert4"],
["say:", "When we developed Scratch, we were very inspired by Seymour's ideas"],
["doPlaySoundAndWait", "papert5"],
["say:", "So you might call Seymour the grandfather of Scratch"],
["doPlaySoundAndWait", "papert6"],
["say:", "Sadly, Seymour passed away last week, on July 31, 2016"],
["doPlaySoundAndWait", "papert7"],
["say:", "But every time you create and share a Scratch project"],
["doPlaySoundAndWait", "papert8"],
["say:", "you're helping to keep Seymour's spirit alive"],
["doPlaySoundAndWait", "papert9"],
["say:", "So Scratch on!"],
["doPlaySoundAndWait", "papert10"],
["say:", "And Papert on!"],
["doPlaySoundAndWait", "papert11"],
["wait:elapsed:from:", 5],
["say:", ""]]]],
"sounds": [{
"soundName": "papert1",
"soundID": -1,
"md5": "876c45f175060737364334daed9366a1.wav",
"sampleCount": 84993,
"rate": 22050,
"format": ""
},
{
"soundName": "papert2",
"soundID": -1,
"md5": "e439ca64ef29692ed611bb769fdbe543.wav",
"sampleCount": 103425,
"rate": 22050,
"format": ""
},
{
"soundName": "papert3",
"soundID": -1,
"md5": "0bd55bc2b3ff5741bafe981decdaca5b.wav",
"sampleCount": 54273,
"rate": 22050,
"format": ""
},
{
"soundName": "papert4",
"soundID": -1,
"md5": "123abc35f1934ed471436c59f7b99e2f.wav",
"sampleCount": 93185,
"rate": 22050,
"format": ""
},
{
"soundName": "papert5",
"soundID": -1,
"md5": "cd9f672266016a0e55152b2a3398c8d9.wav",
"sampleCount": 113665,
"rate": 22050,
"format": ""
},
{
"soundName": "papert6",
"soundID": -1,
"md5": "d38287334764b7d4cd2c5db59548234d.wav",
"sampleCount": 84993,
"rate": 22050,
"format": ""
},
{
"soundName": "papert7",
"soundID": -1,
"md5": "fab6bf9923265a999bcfea1e62384a2c.wav",
"sampleCount": 144385,
"rate": 22050,
"format": ""
},
{
"soundName": "papert8",
"soundID": -1,
"md5": "6826373fa0554c582f920cc65620395e.wav",
"sampleCount": 82945,
"rate": 22050,
"format": ""
},
{
"soundName": "papert9",
"soundID": -1,
"md5": "c6b907cbfe0081748ae83cb3bb6aa6d8.wav",
"sampleCount": 74753,
"rate": 22050,
"format": ""
},
{
"soundName": "papert10",
"soundID": -1,
"md5": "f455697888f2e7b36072a2189fc78bbc.wav",
"sampleCount": 39937,
"rate": 22050,
"format": ""
},
{
"soundName": "papert11",
"soundID": -1,
"md5": "fb58460f1cd8629eae3b3c58148984ad.wav",
"sampleCount": 54273,
"rate": 22050,
"format": ""
}],
"costumes": [{
"costumeName": "-mres-cartoon",
"baseLayerID": -1,
"baseLayerMD5": "dd88c3596176f24479a8e8ec9b6c8278.png",
"bitmapResolution": 2,
"rotationCenterX": 122,
"rotationCenterY": 204
}],
"currentCostumeIndex": 0,
"scratchX": -137.95,
"scratchY": -71,
"scale": 1.1,
"direction": 90,
"rotationStyle": "normal",
"isDraggable": false,
"indexInLibrary": 1,
"visible": true,
"spriteInfo": {
}
}],
"info": {
"spriteCount": 2,
"hasCloudData": false,
"projectID": "117504922",
"flashVersion": "MAC 22,0,0,209",
"swfVersion": "v448",
"userAgent": "Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/51.0.2704.103 Safari\/537.36",
"scriptCount": 2,
"videoOn": false
}
}

Binary file not shown.

After

(image error) Size: 12 KiB

View file

@ -0,0 +1,37 @@
<svg version="1.1" id="cat" x="0px" y="0px" width="95px" height="111px" viewBox="0 0 95 111" enable-background="new 0 0 95 111" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g>
<g id="Layer_8">
<path fill="#FAA51D" stroke="#231F20" stroke-width="1.2" d="M23.754,82.506c-2.871-1.402-10.511-2.518-14.771-6.695&#xD;&#xA;&#x9;&#x9;c-9.431-9.246-3.824-21.732,0.704-21.797c3.611-0.061,3.363,5.482,4.622,11.145c0.875,3.939,1.673,6.988,6.407,9.674&#xD;&#xA;&#x9;&#x9;c4.735,2.688,6.918,2.916,6.918,2.916s3.258,0.791,1.596,2.562C27.568,82.076,23.754,82.506,23.754,82.506z"/>
<path fill="#FFFFFF" d="M14.291,66.369c-0.115-0.459-0.222-0.928-0.33-1.413c-1.172-5.271-0.941-10.432-4.303-10.377&#xD;&#xA;&#x9;&#x9;c-3.433,0.05-7.53,7.771-4.19,15.285c0.193-0.143,0.399-0.336,0.569-0.588c0.451-0.664,0.821-2.969,2.654-3.688&#xD;&#xA;&#x9;&#x9;C9.989,65.082,12.778,65.869,14.291,66.369z"/>
</g>
<path fill="#FAA51D" stroke="#231F20" stroke-width="1.2" d="M52.543,61.473c0,0,1.455,2.342,1.519,4.969&#xD;&#xA;&#x9;c0.065,2.723-0.351,6.984,1.804,8.158c3.381,1.842,9.959-0.463,10.174,5.191c0.211,5.652-10.152,3.623-13.299,2.547&#xD;&#xA;&#x9;c-2.695-0.926-4.559-1.758-5.18-5.182c0,0-3.764-13.969-0.196-15.59C50.933,59.945,52.543,61.473,52.543,61.473z"/>
<path fill="#FAA51D" stroke="#231F20" stroke-width="1.2" d="M14.61,89.584c0,0,1.504,10.391-5.922,8.387&#xD;&#xA;&#x9;c-5.586-1.51-5.705-20.68-0.406-20.691c3.237-0.006,4.425,2.859,4.425,2.859s4.385,4.812,6.95,4.312&#xD;&#xA;&#x9;c2.565-0.502,7.978-6.703,7.978-6.703s2.875-2.539,6.146,0.023c3.271,2.561,1.521,6.178,1.521,6.178s-6.07,7.072-11.014,8.072&#xD;&#xA;&#x9;c-4.945,1.002-9.375-2.568-11.58-3.451"/>
<g id="Layer_6">
<path fill="#FAA51D" stroke="#231F20" stroke-width="1.2" d="M42.435,76.238c0,0,4.866,4.062,8.166,7.52&#xD;&#xA;&#x9;&#x9;c4.384,4.592-3.339,15.039-3.339,15.039s11.234,6.467,2.998,11c-2.657,1.467-15.661-13.225-15.661-13.225s7.979-7.475,4.972-10.172&#xD;&#xA;&#x9;&#x9;c-4.964-4.449-10.771-4.959-11.6-9.824c-0.502-2.957,0.613-4.502,2.658-7.053c1.354-1.689,2.084-3.182,2.084-3.182&#xD;&#xA;&#x9;&#x9;s1.135-7.238,4.289-8.254c3.153-1.014,8.842,0.057,10.758,0.395c1.914,0.338,6.645,1.877,4.617,9.922c0,0-1.451,3.771-2.789,5.762&#xD;&#xA;&#x9;&#x9;c-1.604,2.389-2.854,3.924-4.08,4.867"/>
<path fill="#FFFFFF" d="M50.182,72.631c0,0,2.55-2.748-2.551-5.494c-5.102-2.746-7.449-0.016-9.024,1.572&#xD;&#xA;&#x9;&#x9;c-2.731,2.742,3.334,7.453,3.334,7.453l3.383,2.564c0,0,1.916-1.582,2.701-2.957C48.807,74.396,50.182,72.631,50.182,72.631"/>
</g>
<path fill="#FAA51D" stroke="#231F20" stroke-width="1.2" d="M24.428,65.395c0.939,0.057,3.625,0.213,6.064,0.391&#xD;&#xA;&#x9;c3.014,0.217,8.451-0.451,5.801-5.098c-2.649-4.65-6.74-3.674-11.961-3.754c-2.812-0.043-5.785-1.412-7.322,1.895&#xD;&#xA;&#x9;c-1.534,3.312-5.051,13.338,2.313,15.396c7.366,2.057,4.618-8.137,4.95-10.457"/>
<g id="Layer_2">
<path fill="#FAA51D" stroke="#231F20" stroke-width="1.2" d="M51.709,10.156c-1.54-0.143-4.75-0.316-6.518-0.231&#xD;&#xA;&#x9;&#x9;c-4.728,0.225-9.224,1.928-9.224,1.928L22.949,3.357l2.235,18.906c0.646-0.782-10.555,12.804-3.479,24.224&#xD;&#xA;&#x9;&#x9;c7.08,11.426,22.233,16.518,40.988,12.792c18.755-3.728,23.229-14.531,21.986-20.246c-1.242-5.714-8.322-7.823-8.322-7.823&#xD;&#xA;&#x9;&#x9;s-0.09-4.48-3.328-9.97c-1.926-3.268-8.348-8.041-8.348-8.041L61.822,1.647L54.37,8.851L51.709,10.156z"/>
<path fill="#FFFFFF" d="M75.42,31.066l-2.483-2.064l-9.115,2.661c0,0,0,3.419-4.367,4.367c-4.37,0.951-11.211-2.277-11.211-2.277&#xD;&#xA;&#x9;&#x9;l-7.784,3.417c0,0-8.437,0.928-8.739,6.081C31.048,54.704,45.1,59.478,50.425,59.783c2.905,0.167,8.235-0.337,12.277-1.14&#xD;&#xA;&#x9;&#x9;c17.752-3.235,22.551-13.92,21.309-19.635c-1.242-5.714-7.977-7.196-7.977-7.196L75.42,31.066z"/>
<path fill="none" stroke="#231F20" stroke-width="1.2" d="M9.673,42.155c0,0,4.107,0.374,5.974,0.268&#xD;&#xA;&#x9;&#x9;c1.865-0.107,5.492-0.587,5.492-0.587"/>
<path fill="none" stroke="#231F20" stroke-width="1.2" d="M80.656,36.671c0,0,4.549-0.743,6.859-1.549&#xD;&#xA;&#x9;&#x9;c2.715-0.942,4.543-2.545,4.543-2.545"/>
<path fill="none" stroke="#231F20" stroke-width="1.2" d="M21.337,37.909c0,0-2.384-1.777-6.117-3.43&#xD;&#xA;&#x9;&#x9;c-4.134-1.831-6.405-2.303-6.405-2.303"/>
<path fill="none" stroke="#231F20" stroke-width="1.2" d="M81.117,42.622c0,0,2.726,1.104,5.533,1.385&#xD;&#xA;&#x9;&#x9;c2.77,0.276,4.647,0.11,4.647,0.11"/>
<path fill="none" stroke="#000000" stroke-linecap="round" stroke-miterlimit="10" d="M51.349,10.212&#xD;&#xA;&#x9;&#x9;c2.84,0.7,3.888,1.469,3.888,1.469"/>
<line fill="none" stroke="#000000" x1="32.898" y1="9.684" x2="38.956" y2="14.042"/>
</g>
<g id="Layer_5">
<path fill="#FFFFFF" stroke="#231F20" d="M70.84,21.366c2.924,4.479,3.033,9.591,0.242,11.415&#xD;&#xA;&#x9;&#x9;c-2.793,1.825-7.426-0.332-10.354-4.813c-2.932-4.48-3.037-9.589-0.244-11.415C63.275,14.73,67.913,16.884,70.84,21.366z"/>
<path fill="#231F20" d="M70.089,28.522c0,1.08-0.802,1.956-1.8,1.956c-0.993,0-1.803-0.877-1.803-1.956&#xD;&#xA;&#x9;&#x9;c0-1.08,0.81-1.958,1.803-1.958C69.287,26.564,70.089,27.442,70.089,28.522"/>
</g>
<g id="Layer_7">
<path fill="#FFFFFF" stroke="#231F20" d="M46.867,24.619c2.926,4.48,2.619,9.862-0.681,12.015&#xD;&#xA;&#x9;&#x9;c-3.302,2.159-8.351,0.272-11.276-4.208c-2.928-4.48-2.624-9.86,0.678-12.017C38.891,18.253,43.938,20.137,46.867,24.619z"/>
<path fill="#231F20" d="M45.079,30.507c0,1.081-0.803,1.957-1.801,1.957c-0.992,0-1.803-0.878-1.803-1.957&#xD;&#xA;&#x9;&#x9;c0-1.08,0.811-1.957,1.803-1.957C44.274,28.55,45.079,29.427,45.079,30.507"/>
</g>
<path fill="#5E4A42" stroke="#000000" d="M58.766,33.926c1.854,0,4.555-0.284,4.697,0.569c0.143,0.855-1.709,4.203-2.988,4.345&#xD;&#xA;&#x9;c-1.283,0.142-6.125-2.353-6.195-3.919C54.206,33.355,57.055,33.926,58.766,33.926z"/>
<g id="Layer_4">
<path fill="none" stroke="#231F20" stroke-width="1.2" d="M45.774,41.235c0,0,10.347,3.054,14.217,3.897&#xD;&#xA;&#x9;&#x9;c3.868,0.842,10.851,1.684,10.851,1.684s-7.99,10.245-17.328,7.644C44.176,51.863,44.345,45.975,45.774,41.235z"/>
</g>
</g>
</svg>

After

(image error) Size: 6 KiB

Binary file not shown.

After

(image error) Size: 52 KiB

Binary file not shown.

View file

@ -0,0 +1,35 @@
<svg version="1.1" id="Scratch_Cat_A" x="0px" y="0px" width="94.227px" height="104.333px" viewBox="0 -2 94.227 104.333" enable-background="new 0 -2 94.227 104.333" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g>
<path id="Back_Leg" fill="#FAA51D" stroke="#000000" d="M38.988,80.967c0,0-8.832,6.201-17.07,8.412l0.086,0.215&#xD;&#xA;&#x9;c1.247,1.824,5.869,7.498-0.334,9.496c-5.333,1.718-15.12-13.104-10.821-15.901c2.626-1.713,4.892-0.252,4.892-0.252&#xD;&#xA;&#x9;s3.475-1.069,6.001-2.345c4.304-2.162,5.784-3.453,5.784-3.453s4.185-4.307,6.856-4.137C37.051,73.172,42.44,77.715,38.988,80.967z&#xD;&#xA;&#x9; M19.646,90.09l2.271-0.711"/>
<g id="Tail">
<path id="Tail_1_" fill="#FAA51D" stroke="#000000" d="M23.295,73.217c-2.415-0.451-5.305-1.311-7.742-3.504&#xD;&#xA;&#x9;&#x9;c-5.451-4.906-7.194-13-11.048-10.914c-3.856,2.088-3.782,15.166,8.353,19.195c4.182,1.391,7.998,1.395,11.091,1.312&#xD;&#xA;&#x9;&#x9;c0.812-0.026,7.718-0.655,10.079-4.075c2.361-3.419,0.719-4.271-0.09-4.744C33.128,70.016,26.711,73.855,23.295,73.217z"/>
<path id="Tail_Tip" fill="#FFFFFF" d="M4.641,59.107c-1.437,0.778-2.256,3.397-1.994,6.374c0.141,1.594,1.104,5.652,4.968,9.072&#xD;&#xA;&#x9;&#x9;c0.275-0.545-0.975-2.936,0.742-4.379c1.837-1.543,4.86-1.225,5.853-1.061c-1.663-1.812-3.004-3.812-4.176-5.64&#xD;&#xA;&#x9;&#x9;C7.751,59.922,6.475,58.116,4.641,59.107z"/>
</g>
<path id="R_Arm" fill="#FAA51D" stroke="#000000" d="M52.94,69.096c0,0,1.234,0.166,4.744,3.677c3.509,3.509,6.025,2.16,8.911,0.724&#xD;&#xA;&#x9;c2.877-1.442,10.536-6.127,6.489-9.817c-4.05-3.688-6.207,1.146-9.716,2.405c-3.511,1.26-5.06-2.487-6.856-4.287&#xD;&#xA;&#x9;c-0.59-0.594-1.188-1.098-1.729-1.506c0,0-0.972-0.758-1.905,2.791C51.943,66.624,50.933,67.627,52.94,69.096z"/>
<g id="Front_Leg_x2F_Body_1_">
<path id="Front_Leg_x2F_Body" fill="#FAA51D" stroke="#000000" d="M47.524,76.224c1.188-0.912,2.396-2.401,3.951-4.714&#xD;&#xA;&#x9;&#x9;c1.296-1.926,2.699-5.577,2.699-5.577c0.875-2.521,1.935-6.576-1.901-7.296c-1.553-0.291-4.079-0.098-7.67-0.776&#xD;&#xA;&#x9;&#x9;c-3.593-0.681-6.798-2.522-9.517,2.233c-2.719,4.757-9.591,8.271-1.057,16.562c0,0,4.901,3.842,10.765,9.639&#xD;&#xA;&#x9;&#x9;c4.832,4.774,12.044,10.603,12.044,10.603s18.973,2.188,19.534-0.694c1.923-9.789-14.775-6.91-14.775-6.91&#xD;&#xA;&#x9;&#x9;s-4.604-3.933-6.727-5.795c-3.477-3.058-11.125-10.771-11.125-10.771"/>
<path id="Belly" fill="#FFFFFF" d="M49.74,64.551C44.8,61.895,42.526,64.536,41,66.073c-2.645,2.654,3.442,6.61,3.442,6.61&#xD;&#xA;&#x9;&#x9;l3.062,3.089c2.999-3.496,4.579-5.874,5.292-7.934C52.654,66.897,51.918,65.725,49.74,64.551z"/>
</g>
<path id="L_Arm" fill="#FAA51D" stroke="#000000" d="M30.697,67.43c0.749-0.571,2.89-2.202,4.854-3.657&#xD;&#xA;&#x9;c2.428-1.799,6.117-5.849,1.077-7.646c-5.04-1.801-7.508,1.604-11.52,4.945c-2.159,1.801-5.308,2.698-4.319,6.209&#xD;&#xA;&#x9;c0.993,3.512,4.862,13.407,11.789,10.17c6.93-3.238-1.799-9.181-3.06-11.156"/>
<g id="Head">
<g id="Head_1_">
<path id="Head_Fill" fill="#FAA51D" stroke="#000000" d="M86.452,37.457c-1.472-6.447-8.891-7.579-8.891-7.579&#xD;&#xA;&#x9;&#x9;&#x9;s0.176-5.351-2.628-10.347c-1.856-3.307-8.351-8.042-8.351-8.042l-2.854-11.55l-7.43,7.32l-2.854,1.105&#xD;&#xA;&#x9;&#x9;&#x9;c-1.539-0.143-4.718-0.312-6.485-0.227c-4.729,0.225-9.227,2.036-9.227,2.036L24.854,1.649l2.233,18.91&#xD;&#xA;&#x9;&#x9;&#x9;c0.646-0.786-10.553,12.802-3.477,24.22c3.319,5.36,10.159,10.124,15.599,11.947c4.062,1.36,10.833,1.737,14,1.737&#xD;&#xA;&#x9;&#x9;&#x9;c2.646,0,7.768-0.541,10.32-0.979c3.021-0.519,6.479-1.629,10.055-3.108C83.081,50.442,87.922,43.904,86.452,37.457z&#xD;&#xA;&#x9;&#x9;&#x9; M39.771,12.212l-2.035-2.039 M53.688,8.212c2,0.333,4.333,1.366,4.333,1.366"/>
<path id="Muzzle" fill="#FFFFFF" d="M77.146,30.129l-2.881-3.107l-9.113,2.661c0,0.01-0.005,1.619-1.42,2.933&#xD;&#xA;&#x9;&#x9;&#x9;c-0.646,0.6-1.583,1.138-2.948,1.435c-4.371,0.951-11.212-2.277-11.212-2.277l-7.784,3.417c0,0-4.744,0.522-7.244,2.956&#xD;&#xA;&#x9;&#x9;&#x9;c-0.833,0.812-1.417,1.835-1.492,3.123c-0.493,8.398,8.68,13.252,15.083,15.602c1.778,0.652,2.388,0.851,3.472,1.139&#xD;&#xA;&#x9;&#x9;&#x9;c0,0,7.424-0.195,10.181-0.588c3.032-0.432,7.578-1.758,11.154-3.236c9.498-3.933,14.601-10.024,13.129-16.471&#xD;&#xA;&#x9;&#x9;&#x9;C84.597,31.265,77.146,30.129,77.146,30.129z"/>
</g>
<g id="L_Eye">
<path id="Eyeball_1_" fill="#FFFFFF" stroke="#000000" d="M48.638,22.831c2.926,4.48,2.618,9.862-0.682,12.015&#xD;&#xA;&#x9;&#x9;&#x9;c-3.303,2.159-8.352,0.272-11.275-4.208c-2.928-4.48-2.624-9.86,0.678-12.017C40.661,16.465,45.709,18.349,48.638,22.831"/>
<path id="Pupil_1_" d="M46.85,28.719c0,1.081-0.803,1.957-1.801,1.957c-0.992,0-1.804-0.878-1.804-1.957&#xD;&#xA;&#x9;&#x9;&#x9;c0-1.08,0.812-1.957,1.804-1.957C46.045,26.762,46.85,27.639,46.85,28.719"/>
</g>
<g id="R_Eye">
<path id="Eyeball" fill="#FFFFFF" stroke="#000000" d="M72.361,19.75c2.925,4.479,3.283,9.419,0.492,11.243&#xD;&#xA;&#x9;&#x9;&#x9;c-2.793,1.825-7.181-0.552-10.104-5.034c-2.933-4.48-3.079-9.134-0.286-10.959C65.25,13.177,69.434,15.268,72.361,19.75"/>
<path id="Pupil" d="M71.86,26.734c0,1.08-0.802,1.956-1.8,1.956c-0.993,0-1.804-0.877-1.804-1.956c0-1.08,0.811-1.958,1.804-1.958&#xD;&#xA;&#x9;&#x9;&#x9;C71.058,24.776,71.86,25.654,71.86,26.734"/>
</g>
<path id="Nose" fill="#5E4A42" stroke="#000000" d="M60.536,32.138c1.854,0,4.556-0.284,4.696,0.569&#xD;&#xA;&#x9;&#x9;c0.145,0.855-1.709,4.203-2.987,4.345c-1.282,0.142-6.125-2.353-6.194-3.919C55.977,31.567,58.825,32.138,60.536,32.138"/>
<path id="Whisker_3_" d="M22.75,36.602c-0.023-0.017-2.382-1.76-6.002-3.362c-4.012-1.777-6.263-2.26-6.284-2.265l0.245-1.175&#xD;&#xA;&#x9;&#x9;c0.095,0.02,2.381,0.506,6.525,2.342c3.751,1.661,6.133,3.424,6.232,3.498L22.75,36.602L22.75,36.602z"/>
<path id="Whisker_2_" d="M16.628,41.254c-2.035,0-5.092-0.276-5.238-0.289l0.107-1.195c0.041,0.004,4.088,0.367,5.887,0.266&#xD;&#xA;&#x9;&#x9;c1.82-0.104,5.412-0.578,5.447-0.583l0.157,1.189c-0.148,0.02-3.67,0.484-5.537,0.591C17.209,41.248,16.929,41.254,16.628,41.254&#xD;&#xA;&#x9;&#x9;L16.628,41.254z"/>
<path id="Whisker_1_" d="M82.524,35.475L82.33,34.29c0.046-0.007,4.524-0.744,6.761-1.523c2.573-0.894,4.326-2.415,4.345-2.43&#xD;&#xA;&#x9;&#x9;l0.791,0.902c-0.078,0.068-1.948,1.691-4.742,2.661C87.151,34.714,82.711,35.444,82.524,35.475L82.524,35.475z"/>
<path id="Whisker" d="M91.551,42.979c-0.812,0-1.905-0.035-3.189-0.163c-2.858-0.286-5.584-1.379-5.699-1.426l0.45-1.112&#xD;&#xA;&#x9;&#x9;c0.027,0.011,2.686,1.075,5.368,1.344c2.672,0.267,4.518,0.111,4.535,0.11l0.104,1.195C93.077,42.931,92.508,42.979,91.551,42.979&#xD;&#xA;&#x9;&#x9;L91.551,42.979z"/>
<path id="Mouth" fill="none" stroke="#231F20" stroke-width="1.2" d="M47.545,39.447c0,0,10.347,3.054,14.217,3.896&#xD;&#xA;&#x9;&#x9;c3.867,0.842,10.851,1.684,10.851,1.684s-7.989,10.245-17.327,7.645C45.947,50.075,46.115,44.187,47.545,39.447z"/>
</g>
</g>
</svg>

After

(image error) Size: 6.8 KiB

Binary file not shown.

After

(image error) Size: 86 KiB

60
test/fixtures/known-assets.js vendored Normal file
View file

@ -0,0 +1,60 @@
const fs = require('fs');
const path = require('path');
const md5 = require('js-md5');
const projects = [
'117504922'
];
const assets = [
'66895930177178ea01d9e610917f8acf.png',
'6e8bd9ae68fdb02b7e1e3df656a75635.svg',
'7e24c99c1b853e52f8e7f9004416fa34.png',
'83c36d806dc92327b9e7049a565c6bff.wav',
'f88bf1935daea28f8ca098462a31dbb0.svg',
'fe5e3566965f9de793beeffce377d054.jpg'
];
const loadSomething = filename => {
const fullPath = path.join(__dirname, 'assets', filename);
const content = fs.readFileSync(fullPath);
return {
content,
hash: md5(content)
};
};
const loadProject = id => {
const filename = `${id}.json`;
const result = loadSomething(filename);
// throw if not a valid JSON string
JSON.parse(result.content.toString());
return result;
};
const loadAsset = filename => {
const result = loadSomething(filename);
const expectedHash = filename.split('.', 1)[0];
if (expectedHash !== result.hash) {
throw new Error(`Asset has wrong hash: ${filename}`);
}
return result;
};
const knownAssets = Object.assign({},
projects.reduce((bag, id) => {
bag[id] = loadProject(id);
return bag;
}, {}),
assets.reduce((bag, filename) => {
bag[filename] = loadAsset(filename);
return bag;
}, {})
);
module.exports = knownAssets;

View file

@ -1,6 +1,5 @@
const md5 = require('js-md5');
jest.dontMock('cross-fetch'); // TODO: actually we should mock this...
const ScratchStorage = require('../../src/index.js');
test('constructor', () => {
@ -21,45 +20,46 @@ test('constructor', () => {
* @returns {AssetTestInfo[]} an array of asset info objects.
*/
const getTestAssets = storage => [
// TODO: mock project download, since we can no longer download projects directly
// {
// type: storage.AssetType.Project,
// id: '117504922',
// md5: null // don't check MD5 for project without revision ID
// },
// {
// type: storage.AssetType.Project,
// id: '117504922.d6ae1ffb76f2bc83421cd3f40fc4fd57',
// md5: '1225460702e149727de28bff4cfd9e23'
// },
// Project
{
type: storage.AssetType.Project,
id: '117504922',
md5: '1225460702e149727de28bff4cfd9e23'
},
// SVG without explicit extension
{
type: storage.AssetType.ImageVector,
id: 'f88bf1935daea28f8ca098462a31dbb0', // cat1-a
md5: 'f88bf1935daea28f8ca098462a31dbb0'
},
// SVG with explicit extension
{
type: storage.AssetType.ImageVector,
id: '6e8bd9ae68fdb02b7e1e3df656a75635', // cat1-b
md5: '6e8bd9ae68fdb02b7e1e3df656a75635',
ext: storage.DataFormat.SVG
},
// PNG without explicit extension
{
type: storage.AssetType.ImageBitmap,
id: '7e24c99c1b853e52f8e7f9004416fa34', // squirrel
md5: '7e24c99c1b853e52f8e7f9004416fa34'
},
// PNG with explicit extension
{
type: storage.AssetType.ImageBitmap,
id: '66895930177178ea01d9e610917f8acf', // bus
md5: '66895930177178ea01d9e610917f8acf',
ext: storage.DataFormat.PNG
},
// JPG with explicit extension
{
type: storage.AssetType.ImageBitmap,
id: 'fe5e3566965f9de793beeffce377d054', // building at MIT
md5: 'fe5e3566965f9de793beeffce377d054',
ext: storage.DataFormat.JPG
},
// WAV without explicit extension
{
type: storage.AssetType.Sound,
id: '83c36d806dc92327b9e7049a565c6bff', // meow
@ -68,18 +68,15 @@ const getTestAssets = storage => [
];
const addWebStores = storage => {
// these `asset => ...` callbacks generate values specifically for the cross-fetch mock
// in the real world they would generate proper URIs
storage.addWebStore(
[storage.AssetType.Project],
asset => {
const idParts = asset.assetId.split('.');
return idParts[1] ?
`https://cdn.projects.scratch.mit.edu/internalapi/project/${idParts[0]}/get/${idParts[1]}` :
`https://cdn.projects.scratch.mit.edu/internalapi/project/${idParts[0]}/get/`;
},
asset => asset.assetId,
null, null);
storage.addWebStore(
[storage.AssetType.ImageVector, storage.AssetType.ImageBitmap, storage.AssetType.Sound],
asset => `https://cdn.assets.scratch.mit.edu/internalapi/asset/${asset.assetId}.${asset.dataFormat}/get/`,
asset => `${asset.assetId}.${asset.dataFormat}`,
null, null
);
};
@ -97,9 +94,34 @@ test('load', () => {
const assetChecks = testAssets.map(async assetInfo => {
const asset = await storage.load(assetInfo.type, assetInfo.id, assetInfo.ext)
.catch(e => {
// test output isn't great if we just let it catch the unhandled promise rejection
// wrapping it like this makes a failure much easier to read in the test output
throw new Error(`failed to load ${assetInfo.type.name} asset with id=${assetInfo.id} (e=${e})`);
if (e instanceof Array) {
// This is storage.load reporting one or more errors from individual tools.
e = e.flat();
if (e.length === 1) {
// If we just have one, it'll display well as-is. Don't bother wrapping it.
// Note that this still might be either an Error or a status code (see below).
e = e[0];
}
}
if (e instanceof Array) {
/* global AggregateError */
// we must have >1 error, so report it as an AggregateError (supported in Node 15+)
e = new AggregateError(
e.flat(),
`failed to load ${assetInfo.type.name} asset with id=${assetInfo.id}`
);
// Jest doesn't display AggregateError very well on its own
console.error(e);
} else if (!(e instanceof Error)) {
// storage.load can throw a status like 403 or 500 which isn't an Error.
// That can look confusing in test output, so wrap it in an Error that will display well.
e = new Error(`failed to load ${assetInfo.type.name} asset with id=${assetInfo.id} (e=${e})`);
}
// else it's an Error that's already suitable for reporting
throw e;
});
expect(asset).toBeInstanceOf(storage.Asset);
expect(asset.assetId).toBe(assetInfo.id);