diff --git a/test/__mocks__/cross-fetch.js b/test/__mocks__/cross-fetch.js index d076dfd..9689f29 100644 --- a/test/__mocks__/cross-fetch.js +++ b/test/__mocks__/cross-fetch.js @@ -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); }; diff --git a/test/fixtures/.gitattributes b/test/fixtures/.gitattributes new file mode 100644 index 0000000..c1ce0d4 --- /dev/null +++ b/test/fixtures/.gitattributes @@ -0,0 +1,3 @@ +# Assets in this directory must be preserved exactly +# to ensure the MD5 hash matches the expected value. +* binary diff --git a/test/fixtures/assets/117504922.json b/test/fixtures/assets/117504922.json new file mode 100755 index 0000000..ca5b96a --- /dev/null +++ b/test/fixtures/assets/117504922.json @@ -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 + } +} \ No newline at end of file diff --git a/test/fixtures/assets/66895930177178ea01d9e610917f8acf.png b/test/fixtures/assets/66895930177178ea01d9e610917f8acf.png new file mode 100644 index 0000000..3edf816 Binary files /dev/null and b/test/fixtures/assets/66895930177178ea01d9e610917f8acf.png differ diff --git a/test/fixtures/assets/6e8bd9ae68fdb02b7e1e3df656a75635.svg b/test/fixtures/assets/6e8bd9ae68fdb02b7e1e3df656a75635.svg new file mode 100644 index 0000000..0ecb2de --- /dev/null +++ b/test/fixtures/assets/6e8bd9ae68fdb02b7e1e3df656a75635.svg @@ -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
		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
		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
		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
		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
	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
	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
	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
	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
	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
		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
		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
		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
		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
		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
	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
	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
		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
		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
		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
		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
		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
		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
		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
		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
		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
		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
		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
		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
		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
		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
	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
		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> \ No newline at end of file diff --git a/test/fixtures/assets/7e24c99c1b853e52f8e7f9004416fa34.png b/test/fixtures/assets/7e24c99c1b853e52f8e7f9004416fa34.png new file mode 100644 index 0000000..a8f80f1 Binary files /dev/null and b/test/fixtures/assets/7e24c99c1b853e52f8e7f9004416fa34.png differ diff --git a/test/fixtures/assets/83c36d806dc92327b9e7049a565c6bff.wav b/test/fixtures/assets/83c36d806dc92327b9e7049a565c6bff.wav new file mode 100644 index 0000000..45742d5 Binary files /dev/null and b/test/fixtures/assets/83c36d806dc92327b9e7049a565c6bff.wav differ diff --git a/test/fixtures/assets/f88bf1935daea28f8ca098462a31dbb0.svg b/test/fixtures/assets/f88bf1935daea28f8ca098462a31dbb0.svg new file mode 100644 index 0000000..9c509b3 --- /dev/null +++ b/test/fixtures/assets/f88bf1935daea28f8ca098462a31dbb0.svg @@ -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
	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
	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
	 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
		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
		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
		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
		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
	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
	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
		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
		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
		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
		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
		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
	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
	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
			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
			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
			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
			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
			 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
			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
			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
			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
			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
			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
			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
			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
			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
		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
		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
		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
		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
		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
		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
		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
		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> \ No newline at end of file diff --git a/test/fixtures/assets/fe5e3566965f9de793beeffce377d054.jpg b/test/fixtures/assets/fe5e3566965f9de793beeffce377d054.jpg new file mode 100644 index 0000000..bdaf847 Binary files /dev/null and b/test/fixtures/assets/fe5e3566965f9de793beeffce377d054.jpg differ diff --git a/test/fixtures/known-assets.js b/test/fixtures/known-assets.js new file mode 100644 index 0000000..9a5794a --- /dev/null +++ b/test/fixtures/known-assets.js @@ -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; diff --git a/test/integration/download-known-assets.test.js b/test/integration/download-known-assets.test.js index 998c77a..52b9add 100644 --- a/test/integration/download-known-assets.test.js +++ b/test/integration/download-known-assets.test.js @@ -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);