scratch-storage 5.0.11 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci-cd.yml +10 -3
- package/dist/node/chunks/fetch-worker.7afe4ade866d6f6a3cb3.js +383 -0
- package/dist/node/chunks/fetch-worker.7afe4ade866d6f6a3cb3.js.map +1 -0
- package/dist/node/scratch-storage.js +316 -821
- package/dist/node/scratch-storage.js.map +1 -1
- package/dist/types/HostQueues.d.ts +20 -0
- package/dist/types/ScratchStorage.d.ts +2 -15
- package/dist/types/scratchFetch.d.ts +37 -15
- package/dist/web/chunks/fetch-worker.9fbe108071a9c5d6e70b.js +2 -0
- package/dist/web/chunks/{fetch-worker.4277f33a97fe995aa7dd.js.map → fetch-worker.9fbe108071a9c5d6e70b.js.map} +1 -1
- package/dist/web/chunks/fetch-worker.efebf27d479ab0f0d98c.js +394 -0
- package/dist/web/chunks/fetch-worker.efebf27d479ab0f0d98c.js.map +1 -0
- package/dist/web/scratch-storage.js +316 -821
- package/dist/web/scratch-storage.js.map +1 -1
- package/dist/web/scratch-storage.min.js +1 -1
- package/package.json +7 -5
- package/src/FetchTool.ts +3 -2
- package/src/FetchWorkerTool.worker.ts +94 -0
- package/src/HostQueues.ts +33 -0
- package/src/ScratchStorage.ts +1 -1
- package/src/{scratchFetch.js → scratchFetch.ts} +53 -34
- package/src/types.d.ts +4 -3
- package/test/build/api.test.js +60 -0
- package/test/build/scratchFetch.test.js +40 -0
- package/test/fixtures/known-assets.js +30 -2
- package/test/{__mocks__/cross-fetch.js → fixtures/mockFetch.js} +18 -17
- package/test/integration/download-known-assets.test.js +6 -3
- package/test/unit/fetch-tool.test.js +8 -7
- package/test/unit/metadata.test.js +9 -8
- package/tsconfig.json +0 -1
- package/tsconfig.test.json +1 -0
- package/dist/node/chunks/fetch-worker.56da39b0f4bad6747138.js +0 -809
- package/dist/node/chunks/fetch-worker.56da39b0f4bad6747138.js.map +0 -1
- package/dist/web/chunks/fetch-worker.4277f33a97fe995aa7dd.js +0 -2
- package/dist/web/chunks/fetch-worker.c813c32be403f91001ba.js +0 -820
- package/dist/web/chunks/fetch-worker.c813c32be403f91001ba.js.map +0 -1
- package/src/FetchWorkerTool.worker.js +0 -67
|
@@ -1,29 +1,44 @@
|
|
|
1
|
-
|
|
1
|
+
import {type QueueOptions} from '@scratch/task-herder';
|
|
2
|
+
import {hostQueueManager} from './HostQueues';
|
|
3
|
+
|
|
4
|
+
export const Headers = globalThis.Headers;
|
|
2
5
|
|
|
3
6
|
/**
|
|
4
|
-
* Metadata header names
|
|
5
|
-
*
|
|
6
|
-
* @readonly
|
|
7
|
+
* Metadata header names.
|
|
8
|
+
* The enum value is the name of the associated header.
|
|
7
9
|
*/
|
|
8
|
-
|
|
10
|
+
export enum RequestMetadata {
|
|
9
11
|
/** The ID of the project associated with this request */
|
|
10
|
-
ProjectId
|
|
12
|
+
ProjectId = 'X-Project-ID',
|
|
11
13
|
/** The ID of the project run associated with this request */
|
|
12
|
-
RunId
|
|
14
|
+
RunId = 'X-Run-ID'
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type ScratchFetchOptions = {
|
|
18
|
+
/**
|
|
19
|
+
* The name of the queue to use for this request.
|
|
20
|
+
* If absent, the hostname of the requested URL will be used as the queue name.
|
|
21
|
+
* This is a Scratch-specific extension to the standard RequestInit type.
|
|
22
|
+
*/
|
|
23
|
+
queueName?: string;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The options to use when creating the queue for this request.
|
|
27
|
+
* Ignored if a queue with the specified name already exists.
|
|
28
|
+
*/
|
|
29
|
+
queueOptions?: QueueOptions;
|
|
13
30
|
};
|
|
14
31
|
|
|
15
32
|
/**
|
|
16
|
-
* Metadata headers for requests
|
|
17
|
-
* @type {Headers}
|
|
33
|
+
* Metadata headers for requests.
|
|
18
34
|
*/
|
|
19
|
-
const metadata = new
|
|
35
|
+
const metadata = new Headers();
|
|
20
36
|
|
|
21
37
|
/**
|
|
22
38
|
* Check if there is any metadata to apply.
|
|
23
39
|
* @returns {boolean} true if `metadata` has contents, or false if it is empty.
|
|
24
40
|
*/
|
|
25
|
-
const hasMetadata = () => {
|
|
26
|
-
/* global self */
|
|
41
|
+
export const hasMetadata = (): boolean => {
|
|
27
42
|
const searchParams = (
|
|
28
43
|
typeof self !== 'undefined' &&
|
|
29
44
|
self &&
|
|
@@ -51,16 +66,16 @@ const hasMetadata = () => {
|
|
|
51
66
|
* @param {RequestInit} [options] The initial request options. May be null or undefined.
|
|
52
67
|
* @returns {RequestInit|undefined} the provided options parameter without modification, or a new options object.
|
|
53
68
|
*/
|
|
54
|
-
const applyMetadata = options => {
|
|
69
|
+
export const applyMetadata = (options?: globalThis.RequestInit): globalThis.RequestInit | undefined => {
|
|
55
70
|
if (hasMetadata()) {
|
|
56
71
|
const augmentedOptions = Object.assign({}, options);
|
|
57
|
-
augmentedOptions.headers = new
|
|
72
|
+
augmentedOptions.headers = new Headers(metadata);
|
|
58
73
|
if (options && options.headers) {
|
|
59
74
|
// the Fetch spec says options.headers could be:
|
|
60
75
|
// "A Headers object, an object literal, or an array of two-item arrays to set request's headers."
|
|
61
76
|
// turn it into a Headers object to be sure of how to interact with it
|
|
62
|
-
const overrideHeaders = options.headers instanceof
|
|
63
|
-
options.headers : new
|
|
77
|
+
const overrideHeaders = options.headers instanceof Headers ?
|
|
78
|
+
options.headers : new Headers(options.headers);
|
|
64
79
|
for (const [name, value] of overrideHeaders.entries()) {
|
|
65
80
|
augmentedOptions.headers.set(name, value);
|
|
66
81
|
}
|
|
@@ -74,13 +89,27 @@ const applyMetadata = options => {
|
|
|
74
89
|
* Make a network request.
|
|
75
90
|
* This is a wrapper for the global fetch method, adding some Scratch-specific functionality.
|
|
76
91
|
* @param {RequestInfo|URL} resource The resource to fetch.
|
|
77
|
-
* @param {RequestInit}
|
|
92
|
+
* @param {RequestInit} [requestOptions] Optional object containing custom settings for this request.
|
|
93
|
+
* @param {ScratchFetchOptions} [scratchOptions] Optional Scratch-specific settings for this request.
|
|
78
94
|
* @see {@link https://developer.mozilla.org/docs/Web/API/fetch} for more about the fetch API.
|
|
79
95
|
* @returns {Promise<Response>} A promise for the response to the request.
|
|
80
96
|
*/
|
|
81
|
-
const scratchFetch = (
|
|
82
|
-
|
|
83
|
-
|
|
97
|
+
export const scratchFetch = (
|
|
98
|
+
resource: RequestInfo | URL,
|
|
99
|
+
requestOptions?: globalThis.RequestInit,
|
|
100
|
+
scratchOptions?: ScratchFetchOptions
|
|
101
|
+
): Promise<Response> => {
|
|
102
|
+
requestOptions = applyMetadata(requestOptions);
|
|
103
|
+
|
|
104
|
+
let queueName = scratchOptions?.queueName;
|
|
105
|
+
if (!queueName) {
|
|
106
|
+
// Normalize resource to a Request object. The `fetch` call will do this anyway, so it's not much extra work,
|
|
107
|
+
// but it guarantees availability of the URL for queue naming.
|
|
108
|
+
resource = new Request(resource, requestOptions);
|
|
109
|
+
queueName = new URL(resource.url).hostname;
|
|
110
|
+
}
|
|
111
|
+
const queue = hostQueueManager.getOrCreate(queueName, scratchOptions?.queueOptions);
|
|
112
|
+
return queue.do(() => fetch(resource, requestOptions));
|
|
84
113
|
};
|
|
85
114
|
|
|
86
115
|
/**
|
|
@@ -90,7 +119,7 @@ const scratchFetch = (resource, options) => {
|
|
|
90
119
|
* @param {RequestMetadata} name The name of the metadata item to set.
|
|
91
120
|
* @param {any} value The value to set (will be converted to a string).
|
|
92
121
|
*/
|
|
93
|
-
const setMetadata = (name, value) => {
|
|
122
|
+
export const setMetadata = (name: RequestMetadata, value: any): void => {
|
|
94
123
|
metadata.set(name, value);
|
|
95
124
|
};
|
|
96
125
|
|
|
@@ -98,7 +127,7 @@ const setMetadata = (name, value) => {
|
|
|
98
127
|
* Remove a named request metadata item.
|
|
99
128
|
* @param {RequestMetadata} name The name of the metadata item to remove.
|
|
100
129
|
*/
|
|
101
|
-
const unsetMetadata = name => {
|
|
130
|
+
export const unsetMetadata = (name: RequestMetadata): void => {
|
|
102
131
|
metadata.delete(name);
|
|
103
132
|
};
|
|
104
133
|
|
|
@@ -106,16 +135,6 @@ const unsetMetadata = name => {
|
|
|
106
135
|
* Retrieve a named request metadata item.
|
|
107
136
|
* Only for use in tests. At the time of writing, used in scratch-vm tests.
|
|
108
137
|
* @param {RequestMetadata} name The name of the metadata item to retrieve.
|
|
109
|
-
* @returns {
|
|
138
|
+
* @returns {string|null} The value of the metadata item, or `null` if it was not found.
|
|
110
139
|
*/
|
|
111
|
-
const getMetadata = name => metadata.get(name);
|
|
112
|
-
|
|
113
|
-
module.exports = {
|
|
114
|
-
Headers: crossFetch.Headers,
|
|
115
|
-
RequestMetadata,
|
|
116
|
-
applyMetadata,
|
|
117
|
-
scratchFetch,
|
|
118
|
-
setMetadata,
|
|
119
|
-
unsetMetadata,
|
|
120
|
-
getMetadata
|
|
121
|
-
};
|
|
140
|
+
export const getMetadata = (name: RequestMetadata): string | null => metadata.get(name);
|
package/src/types.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
declare module '
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
declare module '*?arrayBuffer' {
|
|
2
|
+
const value: ArrayBuffer;
|
|
3
|
+
export default value;
|
|
4
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Tests the build output to verify the general parts of the public API.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const ScratchStorageModule = require('../../dist/node/scratch-storage.js');
|
|
6
|
+
|
|
7
|
+
/** @type {ScratchStorageModule.ScratchStorage} */
|
|
8
|
+
let storage;
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
const {ScratchStorage} = ScratchStorageModule;
|
|
12
|
+
storage = new ScratchStorage();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test('constructor', () => {
|
|
16
|
+
const {ScratchStorage} = ScratchStorageModule;
|
|
17
|
+
expect(storage).toBeInstanceOf(ScratchStorage);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('DataFormat', () => {
|
|
21
|
+
const {DataFormat} = ScratchStorageModule;
|
|
22
|
+
expect(DataFormat).toBeDefined();
|
|
23
|
+
expect(DataFormat.JPG).toBe('jpg');
|
|
24
|
+
expect(DataFormat.JSON).toBe('json');
|
|
25
|
+
expect(DataFormat.MP3).toBe('mp3');
|
|
26
|
+
expect(DataFormat.PNG).toBe('png');
|
|
27
|
+
expect(DataFormat.SB2).toBe('sb2');
|
|
28
|
+
expect(DataFormat.SB3).toBe('sb3');
|
|
29
|
+
expect(DataFormat.SVG).toBe('svg');
|
|
30
|
+
expect(DataFormat.WAV).toBe('wav');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('AssetType', () => {
|
|
34
|
+
const {AssetType, DataFormat} = ScratchStorageModule;
|
|
35
|
+
expect(AssetType).toBeDefined();
|
|
36
|
+
expect(AssetType.ImageBitmap.contentType).toBe('image/png');
|
|
37
|
+
expect(AssetType.ImageVector.contentType).toBe('image/svg+xml');
|
|
38
|
+
expect(AssetType.Project.runtimeFormat).toBe(DataFormat.JSON);
|
|
39
|
+
expect(AssetType.Sound.immutable).toBe(true);
|
|
40
|
+
expect(AssetType.Sprite.name).toBe('Sprite');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('Asset', () => {
|
|
44
|
+
const {Asset, AssetType} = ScratchStorageModule;
|
|
45
|
+
expect(Asset).toBeDefined();
|
|
46
|
+
const asset = new Asset(
|
|
47
|
+
AssetType.ImageVector,
|
|
48
|
+
'some-hash'
|
|
49
|
+
);
|
|
50
|
+
expect(asset).toBeInstanceOf(Asset);
|
|
51
|
+
expect(asset.dataFormat).toBe('svg');
|
|
52
|
+
expect(asset.assetId).toBe('some-hash');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('Helper', () => {
|
|
56
|
+
const {Helper} = ScratchStorageModule;
|
|
57
|
+
expect(Helper).toBeDefined();
|
|
58
|
+
const helper = new Helper();
|
|
59
|
+
expect(helper).toBeInstanceOf(Helper);
|
|
60
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Tests the build output to verify the scratchFetch portion of the public API.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const ScratchStorageModule = require('../../dist/node/scratch-storage.js');
|
|
6
|
+
|
|
7
|
+
/** @type {ScratchStorageModule.ScratchStorage} */
|
|
8
|
+
let storage;
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
const {ScratchStorage} = ScratchStorageModule;
|
|
12
|
+
storage = new ScratchStorage();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test('scratchFetch accessor', () => {
|
|
16
|
+
expect(storage.scratchFetch).toBeDefined();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('Headers', () => {
|
|
20
|
+
expect(storage.scratchFetch).toBeDefined();
|
|
21
|
+
const headers = new storage.scratchFetch.Headers();
|
|
22
|
+
expect(headers).toBeInstanceOf(storage.scratchFetch.Headers);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('RequestMetadata enum', () => {
|
|
26
|
+
expect(storage.scratchFetch.RequestMetadata).toBeDefined();
|
|
27
|
+
expect(typeof storage.scratchFetch.RequestMetadata.ProjectId).toBe('string');
|
|
28
|
+
expect(typeof storage.scratchFetch.RequestMetadata.RunId).toBe('string');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('scratchFetch function', () => {
|
|
32
|
+
expect(typeof storage.scratchFetch.scratchFetch).toBe('function');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('metadata functions', () => {
|
|
36
|
+
expect(typeof storage.scratchFetch.applyMetadata).toBe('function');
|
|
37
|
+
expect(typeof storage.scratchFetch.setMetadata).toBe('function');
|
|
38
|
+
expect(typeof storage.scratchFetch.unsetMetadata).toBe('function');
|
|
39
|
+
expect(typeof storage.scratchFetch.getMetadata).toBe('function');
|
|
40
|
+
});
|
|
@@ -3,6 +3,16 @@ const path = require('path');
|
|
|
3
3
|
|
|
4
4
|
const md5 = require('js-md5');
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {object} KnownAsset
|
|
8
|
+
* @property {Buffer} content - The content of the asset.
|
|
9
|
+
* @property {string} hash - The MD5 hash of the asset content.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @typedef {{[id: string]: KnownAsset}} KnownAssetCollection
|
|
14
|
+
*/
|
|
15
|
+
|
|
6
16
|
const projects = [
|
|
7
17
|
'117504922'
|
|
8
18
|
];
|
|
@@ -15,6 +25,11 @@ const assets = [
|
|
|
15
25
|
'fe5e3566965f9de793beeffce377d054.jpg'
|
|
16
26
|
];
|
|
17
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Load a file from disk, then return its content and hash.
|
|
30
|
+
* @param {string} filename - The file to load
|
|
31
|
+
* @returns {KnownAsset} The loaded asset
|
|
32
|
+
*/
|
|
18
33
|
const loadSomething = filename => {
|
|
19
34
|
const fullPath = path.join(__dirname, 'assets', filename);
|
|
20
35
|
const content = fs.readFileSync(fullPath);
|
|
@@ -25,6 +40,11 @@ const loadSomething = filename => {
|
|
|
25
40
|
};
|
|
26
41
|
};
|
|
27
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Load a project from disk, ensure it's valid JSON, then return its content and hash.
|
|
45
|
+
* @param {string} id - The project ID
|
|
46
|
+
* @returns {KnownAsset} The loaded project asset
|
|
47
|
+
*/
|
|
28
48
|
const loadProject = id => {
|
|
29
49
|
const filename = `${id}.json`;
|
|
30
50
|
const result = loadSomething(filename);
|
|
@@ -35,6 +55,11 @@ const loadProject = id => {
|
|
|
35
55
|
return result;
|
|
36
56
|
};
|
|
37
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Load an asset from disk, ensuring its hash matches its filename.
|
|
60
|
+
* @param {string} filename - The file to load
|
|
61
|
+
* @returns {KnownAsset} The loaded asset
|
|
62
|
+
*/
|
|
38
63
|
const loadAsset = filename => {
|
|
39
64
|
const result = loadSomething(filename);
|
|
40
65
|
|
|
@@ -46,15 +71,18 @@ const loadAsset = filename => {
|
|
|
46
71
|
return result;
|
|
47
72
|
};
|
|
48
73
|
|
|
74
|
+
/**
|
|
75
|
+
* @type {KnownAssetCollection}
|
|
76
|
+
*/
|
|
49
77
|
const knownAssets = Object.assign({},
|
|
50
78
|
projects.reduce((bag, id) => {
|
|
51
79
|
bag[id] = loadProject(id);
|
|
52
80
|
return bag;
|
|
53
|
-
}, {}),
|
|
81
|
+
}, /** @type {KnownAssetCollection} */ ({})),
|
|
54
82
|
assets.reduce((bag, filename) => {
|
|
55
83
|
bag[filename] = loadAsset(filename);
|
|
56
84
|
return bag;
|
|
57
|
-
}, {})
|
|
85
|
+
}, /** @type {KnownAssetCollection} */ ({}))
|
|
58
86
|
);
|
|
59
87
|
|
|
60
88
|
module.exports = knownAssets;
|
|
@@ -1,8 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mock implementation of the fetch function for testing.
|
|
3
|
+
* Since `fetch` is a global, Jest will not automatically mock it from `__mocks__`.
|
|
4
|
+
*
|
|
5
|
+
* // In your test setup file or at the top of your test files:
|
|
6
|
+
* global.fetch = require('../mocks/fetch').default;
|
|
7
|
+
*/
|
|
8
|
+
|
|
1
9
|
const TextEncoder = require('util').TextEncoder;
|
|
2
|
-
const crossFetch = jest.requireActual('cross-fetch');
|
|
3
10
|
const knownAssets = require('../fixtures/known-assets.js');
|
|
4
11
|
|
|
5
|
-
const Headers =
|
|
12
|
+
const Headers = global.Headers;
|
|
6
13
|
const successText = 'successful response';
|
|
7
14
|
|
|
8
15
|
/**
|
|
@@ -30,7 +37,7 @@ const successText = 'successful response';
|
|
|
30
37
|
* @returns {Promise<MockFetchResponse>} A promise for a Response-like object. Does not fully implement Response.
|
|
31
38
|
*/
|
|
32
39
|
const mockFetch = (resource, options) => {
|
|
33
|
-
/** @type MockFetchResponse */
|
|
40
|
+
/** @type {MockFetchResponse} */
|
|
34
41
|
const results = {
|
|
35
42
|
ok: false,
|
|
36
43
|
status: 0
|
|
@@ -39,14 +46,15 @@ const mockFetch = (resource, options) => {
|
|
|
39
46
|
options.mockFetchTestData.headers = new Headers(options.headers);
|
|
40
47
|
options.mockFetchTestData.headersCount = Array.from(options.mockFetchTestData.headers).length;
|
|
41
48
|
}
|
|
42
|
-
|
|
43
|
-
const
|
|
49
|
+
const request = new Request(resource, options);
|
|
50
|
+
const path = new URL(request.url).pathname.slice(1); // remove leading '/'
|
|
51
|
+
const assetInfo = knownAssets[path];
|
|
44
52
|
if (assetInfo) {
|
|
45
53
|
results.ok = true;
|
|
46
54
|
results.status = 200;
|
|
47
55
|
results.arrayBuffer = () => Promise.resolve(assetInfo.content);
|
|
48
56
|
} else {
|
|
49
|
-
switch (
|
|
57
|
+
switch (path) {
|
|
50
58
|
case '200':
|
|
51
59
|
results.ok = true;
|
|
52
60
|
results.status = 200;
|
|
@@ -68,14 +76,7 @@ const mockFetch = (resource, options) => {
|
|
|
68
76
|
return Promise.resolve(results);
|
|
69
77
|
};
|
|
70
78
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
exports.Headers = crossFetch.Headers;
|
|
76
|
-
exports.Request = crossFetch.Request;
|
|
77
|
-
exports.Response = crossFetch.Response;
|
|
78
|
-
exports.successText = successText;
|
|
79
|
-
|
|
80
|
-
// Needed for TypeScript consumers without esModuleInterop.
|
|
81
|
-
exports.default = mockFetch;
|
|
79
|
+
module.exports = {
|
|
80
|
+
fetch: mockFetch,
|
|
81
|
+
successText
|
|
82
|
+
};
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
const md5 = require('js-md5');
|
|
2
2
|
|
|
3
|
+
const mockFetch = require('../fixtures/mockFetch.js');
|
|
3
4
|
const ScratchStorage = require('../../src/index').ScratchStorage;
|
|
4
5
|
|
|
6
|
+
jest.spyOn(global, 'fetch').mockImplementation(mockFetch.fetch);
|
|
7
|
+
|
|
5
8
|
test('constructor', () => {
|
|
6
9
|
const storage = new ScratchStorage();
|
|
7
10
|
expect(storage).toBeInstanceOf(ScratchStorage);
|
|
@@ -68,15 +71,15 @@ const getTestAssets = storage => [
|
|
|
68
71
|
];
|
|
69
72
|
|
|
70
73
|
const addWebStores = storage => {
|
|
71
|
-
// these `asset => ...` callbacks generate values specifically for the
|
|
74
|
+
// these `asset => ...` callbacks generate values specifically for the fetch mock
|
|
72
75
|
// in the real world they would generate proper URIs
|
|
73
76
|
storage.addWebStore(
|
|
74
77
|
[storage.AssetType.Project],
|
|
75
|
-
asset => asset.assetId
|
|
78
|
+
asset => `http://example.com/${asset.assetId}`,
|
|
76
79
|
null, null);
|
|
77
80
|
storage.addWebStore(
|
|
78
81
|
[storage.AssetType.ImageVector, storage.AssetType.ImageBitmap, storage.AssetType.Sound],
|
|
79
|
-
asset =>
|
|
82
|
+
asset => `http://example.com/${asset.assetId}.${asset.dataFormat}`,
|
|
80
83
|
null, null
|
|
81
84
|
);
|
|
82
85
|
};
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
const TextDecoder = require('util').TextDecoder;
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
const mockFetch = require('../fixtures/mockFetch.js');
|
|
4
|
+
jest.spyOn(global, 'fetch').mockImplementation(mockFetch.fetch);
|
|
5
|
+
|
|
5
6
|
const {FetchTool} = require('../../src/FetchTool');
|
|
6
7
|
|
|
7
8
|
test('send success returns response.text()', async () => {
|
|
8
9
|
const tool = new FetchTool();
|
|
9
10
|
|
|
10
|
-
const result = await tool.send({url: '200'});
|
|
11
|
+
const result = await tool.send({url: 'http://example.com/200'});
|
|
11
12
|
expect(result).toBe(mockFetch.successText);
|
|
12
13
|
});
|
|
13
14
|
|
|
@@ -17,7 +18,7 @@ test('send failure returns response.status', async () => {
|
|
|
17
18
|
const catcher = jest.fn();
|
|
18
19
|
|
|
19
20
|
try {
|
|
20
|
-
await tool.send({url: '500'});
|
|
21
|
+
await tool.send({url: 'http://example.com/500'});
|
|
21
22
|
} catch (e) {
|
|
22
23
|
catcher(e);
|
|
23
24
|
}
|
|
@@ -31,14 +32,14 @@ test('get success returns Uint8Array.body(response.arrayBuffer())', async () =>
|
|
|
31
32
|
|
|
32
33
|
const tool = new FetchTool();
|
|
33
34
|
|
|
34
|
-
const result = await tool.get({url: '200'});
|
|
35
|
+
const result = await tool.get({url: 'http://example.com/200'});
|
|
35
36
|
expect(decoder.decode(result)).toBe(mockFetch.successText);
|
|
36
37
|
});
|
|
37
38
|
|
|
38
39
|
test('get with 404 response returns null data', async () => {
|
|
39
40
|
const tool = new FetchTool();
|
|
40
41
|
|
|
41
|
-
const result = await tool.get({url: '404'});
|
|
42
|
+
const result = await tool.get({url: 'http://example.com/404'});
|
|
42
43
|
expect(result).toBeNull();
|
|
43
44
|
});
|
|
44
45
|
|
|
@@ -47,7 +48,7 @@ test('get failure returns response.status', async () => {
|
|
|
47
48
|
const catcher = jest.fn();
|
|
48
49
|
|
|
49
50
|
try {
|
|
50
|
-
await tool.get({url: '500'});
|
|
51
|
+
await tool.get({url: 'http://example.com/500'});
|
|
51
52
|
} catch (e) {
|
|
52
53
|
catcher(e);
|
|
53
54
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
const mockFetch = require('../fixtures/mockFetch.js');
|
|
2
|
+
jest.spyOn(global, 'fetch').mockImplementation(mockFetch.fetch);
|
|
2
3
|
|
|
3
4
|
beforeEach(() => {
|
|
4
5
|
// reset the metadata container to ensure the tests don't interfere with each other
|
|
@@ -17,7 +18,7 @@ test('get without metadata', async () => {
|
|
|
17
18
|
const tool = new FetchTool();
|
|
18
19
|
|
|
19
20
|
const mockFetchTestData = {};
|
|
20
|
-
const result = await tool.get({url: '200', mockFetchTestData});
|
|
21
|
+
const result = await tool.get({url: 'http://example.com/200', mockFetchTestData});
|
|
21
22
|
|
|
22
23
|
expect(result).toBeInstanceOf(Uint8Array);
|
|
23
24
|
expect(mockFetchTestData.headers).toBeTruthy();
|
|
@@ -35,7 +36,7 @@ test('get with metadata', async () => {
|
|
|
35
36
|
setMetadata(RequestMetadata.RunId, 5678);
|
|
36
37
|
|
|
37
38
|
const mockFetchTestData = {};
|
|
38
|
-
const result = await tool.get({url: '200', mockFetchTestData});
|
|
39
|
+
const result = await tool.get({url: 'http://example.com/200', mockFetchTestData});
|
|
39
40
|
|
|
40
41
|
expect(result).toBeInstanceOf(Uint8Array);
|
|
41
42
|
expect(mockFetchTestData.headers).toBeTruthy();
|
|
@@ -49,7 +50,7 @@ test('send without metadata', async () => {
|
|
|
49
50
|
const tool = new FetchTool();
|
|
50
51
|
|
|
51
52
|
const mockFetchTestData = {};
|
|
52
|
-
const result = await tool.send({url: '200', mockFetchTestData});
|
|
53
|
+
const result = await tool.send({url: 'http://example.com/200', mockFetchTestData});
|
|
53
54
|
|
|
54
55
|
expect(typeof result).toBe('string');
|
|
55
56
|
expect(mockFetchTestData.headers).toBeTruthy();
|
|
@@ -67,7 +68,7 @@ test('send with metadata', async () => {
|
|
|
67
68
|
setMetadata(RequestMetadata.RunId, 8765);
|
|
68
69
|
|
|
69
70
|
const mockFetchTestData = {};
|
|
70
|
-
const result = await tool.send({url: '200', mockFetchTestData});
|
|
71
|
+
const result = await tool.send({url: 'http://example.com/200', mockFetchTestData});
|
|
71
72
|
|
|
72
73
|
expect(typeof result).toBe('string');
|
|
73
74
|
expect(mockFetchTestData.headers).toBeTruthy();
|
|
@@ -89,7 +90,7 @@ test('selectively delete metadata', async () => {
|
|
|
89
90
|
|
|
90
91
|
const mockFetchTestData = {};
|
|
91
92
|
|
|
92
|
-
const result1 = await tool.send({url: '200', mockFetchTestData});
|
|
93
|
+
const result1 = await tool.send({url: 'http://example.com/200', mockFetchTestData});
|
|
93
94
|
expect(typeof result1).toBe('string');
|
|
94
95
|
expect(mockFetchTestData.headers).toBeTruthy();
|
|
95
96
|
|
|
@@ -100,7 +101,7 @@ test('selectively delete metadata', async () => {
|
|
|
100
101
|
// remove the Project ID from metadata
|
|
101
102
|
unsetMetadata(RequestMetadata.ProjectId);
|
|
102
103
|
|
|
103
|
-
const result2 = await tool.send({url: '200', mockFetchTestData});
|
|
104
|
+
const result2 = await tool.send({url: 'http://example.com/200', mockFetchTestData});
|
|
104
105
|
expect(typeof result2).toBe('string');
|
|
105
106
|
expect(mockFetchTestData.headers).toBeTruthy();
|
|
106
107
|
|
|
@@ -120,7 +121,7 @@ test('metadata has case-insensitive keys', async () => {
|
|
|
120
121
|
const tool = new FetchTool();
|
|
121
122
|
|
|
122
123
|
const mockFetchTestData = {};
|
|
123
|
-
await tool.get({url: '200', mockFetchTestData});
|
|
124
|
+
await tool.get({url: 'http://example.com/200', mockFetchTestData});
|
|
124
125
|
|
|
125
126
|
expect(mockFetchTestData.headers).toBeTruthy();
|
|
126
127
|
expect(mockFetchTestData.headersCount).toBe(1);
|
package/tsconfig.json
CHANGED