scratch-storage 2.0.2 → 2.2.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.
Files changed (32) hide show
  1. package/.circleci/config.yml +70 -0
  2. package/.nyc_output/c826ae6b-4ae2-4d8e-bdb2-162df5761fa7.json +1 -0
  3. package/.nyc_output/ec2d52a1-5131-4dd0-9c23-59106f297082.json +1 -0
  4. package/.nyc_output/processinfo/c826ae6b-4ae2-4d8e-bdb2-162df5761fa7.json +1 -0
  5. package/.nyc_output/processinfo/ec2d52a1-5131-4dd0-9c23-59106f297082.json +1 -0
  6. package/.nyc_output/processinfo/index.json +1 -0
  7. package/README.md +2 -1
  8. package/dist/node/8c63ecb0448dfbb8e804.worker.js +4187 -0
  9. package/dist/node/8c63ecb0448dfbb8e804.worker.js.map +1 -0
  10. package/dist/node/scratch-storage.js +2933 -142
  11. package/dist/node/scratch-storage.js.map +1 -1
  12. package/dist/web/90c5db0dff5ca2a36ff6.worker.js +743 -0
  13. package/dist/web/90c5db0dff5ca2a36ff6.worker.js.map +1 -0
  14. package/dist/web/scratch-storage.js +1149 -85
  15. package/dist/web/scratch-storage.js.map +1 -1
  16. package/dist/web/scratch-storage.min.js +1149 -85
  17. package/dist/web/scratch-storage.min.js.map +1 -1
  18. package/package.json +9 -4
  19. package/src/FetchTool.js +17 -13
  20. package/src/FetchWorkerTool.js +14 -7
  21. package/src/FetchWorkerTool.worker.js +6 -10
  22. package/src/scratchFetch.js +86 -0
  23. package/test/integration/download-known-assets.js +27 -30
  24. package/test/mocks/mockFetch.js +64 -0
  25. package/test/unit/fetch-tool.js +27 -44
  26. package/test/unit/metadata.js +136 -0
  27. package/webpack.config.js +9 -8
  28. package/.travis.yml +0 -24
  29. package/dist/node/f98711f696b187f5e007.worker.js +0 -1906
  30. package/dist/node/f98711f696b187f5e007.worker.js.map +0 -1
  31. package/dist/web/01b4fbd4d14a4f22cab8.worker.js +0 -190
  32. package/dist/web/01b4fbd4d14a4f22cab8.worker.js.map +0 -1
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "scratch-storage",
3
- "version": "2.0.2",
3
+ "version": "2.2.0",
4
4
  "description": "Load and store project and asset files for Scratch 3.0",
5
5
  "license": "BSD-3-Clause",
6
6
  "homepage": "https://github.com/LLK/scratch-storage#readme",
7
7
  "repository": {
8
8
  "type": "git",
9
9
  "url": "https://github.com/LLK/scratch-storage.git",
10
- "sha": "1592e27d02d61c89172517cfb0832cdbb4f95dea"
10
+ "sha": "5c6588073bb3c9ba4500403054d6409db2379286"
11
11
  },
12
12
  "main": "./dist/node/scratch-storage.js",
13
13
  "browser": "./src/index.js",
@@ -24,9 +24,14 @@
24
24
  "watch": "webpack --progress --colors --watch",
25
25
  "semantic-release": "semantic-release"
26
26
  },
27
+ "tap": {
28
+ "check-coverage": false
29
+ },
27
30
  "dependencies": {
31
+ "@babel/runtime": "7.21.0",
28
32
  "arraybuffer-loader": "^1.0.3",
29
33
  "base64-js": "1.3.0",
34
+ "cross-fetch": "3.1.5",
30
35
  "fastestsmallesttextencoderdecoder": "^1.0.7",
31
36
  "js-md5": "0.7.3",
32
37
  "minilog": "3.1.0",
@@ -34,6 +39,7 @@
34
39
  },
35
40
  "devDependencies": {
36
41
  "@babel/core": "7.14.8",
42
+ "@babel/plugin-transform-runtime": "7.21.0",
37
43
  "@babel/polyfill": "7.12.1",
38
44
  "@babel/preset-env": "7.14.8",
39
45
  "@commitlint/cli": "8.2.0",
@@ -48,9 +54,8 @@
48
54
  "file-loader": "4.1.0",
49
55
  "husky": "1.3.1",
50
56
  "json": "^9.0.4",
51
- "node-fetch": "2.6.1",
52
57
  "semantic-release": "^15.10.5",
53
- "tap": "12.1.1",
58
+ "tap": "16.3.4",
54
59
  "uglifyjs-webpack-plugin": "2.2.0",
55
60
  "webpack": "4.46.0",
56
61
  "webpack-cli": "3.1.2"
package/src/FetchTool.js CHANGED
@@ -1,48 +1,52 @@
1
- /* eslint-env browser */
1
+ const {scratchFetch} = require('./scratchFetch');
2
+
3
+ /**
4
+ * @typedef {Request & {withCredentials: boolean}} ScratchSendRequest
5
+ */
2
6
 
3
7
  /**
4
8
  * Get and send assets with the fetch standard web api.
5
9
  */
6
10
  class FetchTool {
7
11
  /**
8
- * Is get supported? false if the environment does not support fetch.
12
+ * Is get supported?
13
+ * Always true for `FetchTool` because `scratchFetch` ponyfills `fetch` if necessary.
9
14
  * @returns {boolean} Is get supported?
10
15
  */
11
16
  get isGetSupported () {
12
- return typeof fetch !== 'undefined';
17
+ return true;
13
18
  }
14
19
 
15
20
  /**
16
21
  * Request data from a server with fetch.
17
- * @param {{url:string}} reqConfig - Request configuration for data to get.
18
- * @param {{method:string}} options - Additional options to configure fetch.
19
- * @returns {Promise.<Uint8Array>} Resolve to Buffer of data from server.
22
+ * @param {Request} reqConfig - Request configuration for data to get.
23
+ * @returns {Promise.<Uint8Array?>} Resolve to Buffer of data from server.
20
24
  */
21
25
  get ({url, ...options}) {
22
- return fetch(url, Object.assign({method: 'GET'}, options))
26
+ return scratchFetch(url, Object.assign({method: 'GET'}, options))
23
27
  .then(result => {
24
28
  if (result.ok) return result.arrayBuffer().then(b => new Uint8Array(b));
25
29
  if (result.status === 404) return null;
26
- return Promise.reject(result.status);
30
+ return Promise.reject(result.status); // TODO: we should throw a proper error
27
31
  });
28
32
  }
29
33
 
30
34
  /**
31
- * Is sending supported? false if the environment does not support sending
32
- * with fetch.
35
+ * Is sending supported?
36
+ * Always true for `FetchTool` because `scratchFetch` ponyfills `fetch` if necessary.
33
37
  * @returns {boolean} Is sending supported?
34
38
  */
35
39
  get isSendSupported () {
36
- return typeof fetch !== 'undefined';
40
+ return true;
37
41
  }
38
42
 
39
43
  /**
40
44
  * Send data to a server with fetch.
41
- * @param {Request} reqConfig - Request configuration for data to send.
45
+ * @param {ScratchSendRequest} reqConfig - Request configuration for data to send.
42
46
  * @returns {Promise.<string>} Server returned metadata.
43
47
  */
44
48
  send ({url, withCredentials = false, ...options}) {
45
- return fetch(url, Object.assign({
49
+ return scratchFetch(url, Object.assign({
46
50
  credentials: withCredentials ? 'include' : 'omit'
47
51
  }, options))
48
52
  .then(response => {
@@ -1,3 +1,5 @@
1
+ const {applyMetadata} = require('./scratchFetch');
2
+
1
3
  /**
2
4
  * Get and send assets with a worker that uses fetch.
3
5
  */
@@ -13,13 +15,13 @@ class PrivateFetchWorkerTool {
13
15
 
14
16
  /**
15
17
  * A possible error occurred standing up the worker.
16
- * @type {!Error}
18
+ * @type {Error?}
17
19
  */
18
20
  this._supportError = null;
19
21
 
20
22
  /**
21
23
  * The worker that runs fetch and returns data for us.
22
- * @type {!Worker}
24
+ * @type {Worker?}
23
25
  */
24
26
  this.worker = null;
25
27
 
@@ -34,9 +36,9 @@ class PrivateFetchWorkerTool {
34
36
  // eslint-disable-next-line global-require
35
37
  const FetchWorker = require('worker-loader?{"inline":true,"fallback":true}!./FetchWorkerTool.worker');
36
38
 
37
- this.worker = new FetchWorker();
39
+ const worker = new FetchWorker();
38
40
 
39
- this.worker.addEventListener('message', ({data}) => {
41
+ worker.addEventListener('message', ({data}) => {
40
42
  if (data.support) {
41
43
  this._workerSupport = data.support;
42
44
  return;
@@ -52,6 +54,8 @@ class PrivateFetchWorkerTool {
52
54
  }
53
55
  }
54
56
  });
57
+
58
+ this.worker = worker;
55
59
  }
56
60
  } catch (error) {
57
61
  this._supportError = error;
@@ -78,17 +82,20 @@ class PrivateFetchWorkerTool {
78
82
  * Request data from a server with a worker using fetch.
79
83
  * @param {{url:string}} reqConfig - Request configuration for data to get.
80
84
  * @param {{method:string}} options - Additional options to configure fetch.
81
- * @returns {Promise.<Buffer>} Resolve to Buffer of data from server.
85
+ * @returns {Promise.<Buffer|Uint8Array|null>} Resolve to Buffer of data from server.
82
86
  */
83
87
  get ({url, ...options}) {
84
88
  return new Promise((resolve, reject) => {
85
89
  // TODO: Use a Scratch standard ID generator ...
86
90
  const id = Math.random().toString(16)
87
91
  .substring(2);
92
+ const augmentedOptions = applyMetadata(
93
+ Object.assign({method: 'GET'}, options)
94
+ );
88
95
  this.worker.postMessage({
89
96
  id,
90
97
  url,
91
- options: Object.assign({method: 'GET'}, options)
98
+ options: augmentedOptions
92
99
  });
93
100
  this.jobs[id] = {
94
101
  id,
@@ -153,7 +160,7 @@ class PublicFetchWorkerTool {
153
160
  /**
154
161
  * Request data from a server with a worker that uses fetch.
155
162
  * @param {{url:string}} reqConfig - Request configuration for data to get.
156
- * @returns {Promise.<Buffer>} Resolve to Buffer of data from server.
163
+ * @returns {Promise.<Buffer|Uint8Array|null>} Resolve to Buffer of data from server.
157
164
  */
158
165
  get (reqConfig) {
159
166
  return this.inner.get(reqConfig);
@@ -1,5 +1,7 @@
1
1
  /* eslint-env worker */
2
2
 
3
+ const crossFetch = require('cross-fetch').default;
4
+
3
5
  let jobsActive = 0;
4
6
  const complete = [];
5
7
 
@@ -48,7 +50,7 @@ const onMessage = ({data: job}) => {
48
50
 
49
51
  jobsActive++;
50
52
 
51
- fetch(job.url, job.options)
53
+ crossFetch(job.url, job.options)
52
54
  .then(result => {
53
55
  if (result.ok) return result.arrayBuffer();
54
56
  if (result.status === 404) return null;
@@ -59,12 +61,6 @@ const onMessage = ({data: job}) => {
59
61
  .then(() => jobsActive--);
60
62
  };
61
63
 
62
- if (self.fetch) {
63
- postMessage({support: {fetch: true}});
64
- self.addEventListener('message', onMessage);
65
- } else {
66
- postMessage({support: {fetch: false}});
67
- self.addEventListener('message', ({data: job}) => {
68
- postMessage([{id: job.id, error: 'fetch is unavailable'}]);
69
- });
70
- }
64
+ // crossFetch means "fetch" is now always supported
65
+ postMessage({support: {fetch: true}});
66
+ self.addEventListener('message', onMessage);
@@ -0,0 +1,86 @@
1
+ const {fetch, Headers} = require('cross-fetch');
2
+
3
+ /**
4
+ * Metadata header names
5
+ * @enum {string} The enum value is the name of the associated header.
6
+ * @readonly
7
+ */
8
+ const RequestMetadata = {
9
+ /** The ID of the project associated with this request */
10
+ ProjectId: 'X-ProjectId',
11
+ /** The ID of the project run associated with this request */
12
+ RunId: 'X-RunId'
13
+ };
14
+
15
+ /**
16
+ * Metadata for requests
17
+ * @type {Map<string, string>}
18
+ */
19
+ const metadata = new Map();
20
+
21
+ /**
22
+ * Non-destructively merge any metadata state (if any) with the provided options object (if any).
23
+ * If there is metadata state but no options object is provided, make a new object.
24
+ * If there is no metadata state, return the provided options parameter without modification.
25
+ * If there is metadata and an options object is provided, modify a copy and return it.
26
+ * Headers in the provided options object may override headers generated from metadata state.
27
+ * @param {RequestInit} [options] The initial request options. May be null or undefined.
28
+ * @returns {RequestInit|undefined} the provided options parameter without modification, or a new options object.
29
+ */
30
+ const applyMetadata = options => {
31
+ if (metadata.size > 0) {
32
+ const augmentedOptions = Object.assign({}, options);
33
+ augmentedOptions.headers = new Headers(Array.from(metadata));
34
+ if (options && options.headers) {
35
+ const overrideHeaders =
36
+ options.headers instanceof Headers ? options.headers : new Headers(options.headers);
37
+ for (const [name, value] of overrideHeaders.entries()) {
38
+ augmentedOptions.headers.set(name, value);
39
+ }
40
+ }
41
+ return augmentedOptions;
42
+ }
43
+ return options;
44
+ };
45
+
46
+ /**
47
+ * Make a network request.
48
+ * This is a wrapper for the global fetch method, adding some Scratch-specific functionality.
49
+ * @param {RequestInfo|URL} resource The resource to fetch.
50
+ * @param {RequestInit} options Optional object containing custom settings for this request.
51
+ * @see {@link https://developer.mozilla.org/docs/Web/API/fetch} for more about the fetch API.
52
+ * @returns {Promise<Response>} A promise for the response to the request.
53
+ */
54
+ const scratchFetch = (resource, options) => {
55
+ const augmentedOptions = applyMetadata(options);
56
+ return fetch(resource, augmentedOptions);
57
+ };
58
+
59
+ /**
60
+ * Set the value of a named request metadata item.
61
+ * Setting the value to `null` or `undefined` will NOT remove the item.
62
+ * Use `unsetMetadata` for that.
63
+ * @param {RequestMetadata} name The name of the metadata item to set.
64
+ * @param {any} value The value to set (will be converted to a string).
65
+ */
66
+ const setMetadata = (name, value) => {
67
+ metadata.set(name, value);
68
+ };
69
+
70
+ /**
71
+ * Remove a named request metadata item.
72
+ * @param {RequestMetadata} name The name of the metadata item to remove.
73
+ */
74
+ const unsetMetadata = name => {
75
+ metadata.delete(name);
76
+ };
77
+
78
+ module.exports = {
79
+ default: scratchFetch,
80
+
81
+ RequestMetadata,
82
+ applyMetadata,
83
+ scratchFetch,
84
+ setMetadata,
85
+ unsetMetadata
86
+ };
@@ -20,16 +20,17 @@ test('constructor', t => {
20
20
  * @property {DataFormat} [ext] - Optional: the asset's data format / file extension.
21
21
  */
22
22
  const testAssets = [
23
- {
24
- type: storage.AssetType.Project,
25
- id: '117504922',
26
- md5: null // don't check MD5 for project without revision ID
27
- },
28
- {
29
- type: storage.AssetType.Project,
30
- id: '117504922.d6ae1ffb76f2bc83421cd3f40fc4fd57',
31
- md5: '1225460702e149727de28bff4cfd9e23'
32
- },
23
+ // TODO: mock project download, since we can no longer download projects directly
24
+ // {
25
+ // type: storage.AssetType.Project,
26
+ // id: '117504922',
27
+ // md5: null // don't check MD5 for project without revision ID
28
+ // },
29
+ // {
30
+ // type: storage.AssetType.Project,
31
+ // id: '117504922.d6ae1ffb76f2bc83421cd3f40fc4fd57',
32
+ // md5: '1225460702e149727de28bff4cfd9e23'
33
+ // },
33
34
  {
34
35
  type: storage.AssetType.ImageVector,
35
36
  id: 'f88bf1935daea28f8ca098462a31dbb0', // cat1-a
@@ -65,9 +66,9 @@ const testAssets = [
65
66
  }
66
67
  ];
67
68
 
68
- test('addWebSource', t => {
69
+ test('addWebStore', t => {
69
70
  t.doesNotThrow(() => {
70
- storage.addWebSource(
71
+ storage.addWebStore(
71
72
  [storage.AssetType.Project],
72
73
  asset => {
73
74
  const idParts = asset.assetId.split('.');
@@ -77,7 +78,7 @@ test('addWebSource', t => {
77
78
  });
78
79
  });
79
80
  t.doesNotThrow(() => {
80
- storage.addWebSource(
81
+ storage.addWebStore(
81
82
  [storage.AssetType.ImageVector, storage.AssetType.ImageBitmap, storage.AssetType.Sound],
82
83
  asset => `https://cdn.assets.scratch.mit.edu/internalapi/asset/${asset.assetId}.${asset.dataFormat}/get/`
83
84
  );
@@ -86,29 +87,25 @@ test('addWebSource', t => {
86
87
  });
87
88
 
88
89
  test('load', t => {
89
- const promises = [];
90
- const checkAsset = (assetInfo, asset) => {
90
+ const assetChecks = testAssets.map(async assetInfo => {
91
+ const asset = await storage.load(assetInfo.type, assetInfo.id, assetInfo.ext)
92
+ .catch(e => {
93
+ // tap's output isn't great if we just let it catch the unhandled promise rejection
94
+ // wrapping it like this makes a failure much easier to read in the test output
95
+ throw new Error(`failed to load ${assetInfo.type.name} asset with id=${assetInfo.id} (e=${e})`);
96
+ });
91
97
  t.type(asset, storage.Asset);
92
- t.strictEqual(asset.assetId, assetInfo.id);
93
- t.strictEqual(asset.assetType, assetInfo.type);
98
+ t.equal(asset.assetId, assetInfo.id);
99
+ t.equal(asset.assetType, assetInfo.type);
94
100
  t.ok(asset.data.length);
95
101
 
96
102
  // Web assets should come back as clean
97
- t.true(asset.clean);
103
+ t.ok(asset.clean);
98
104
 
99
105
  if (assetInfo.md5) {
100
- t.strictEqual(md5(asset.data), assetInfo.md5);
106
+ t.equal(md5(asset.data), assetInfo.md5);
101
107
  }
102
- };
103
- for (let i = 0; i < testAssets.length; ++i) {
104
- const assetInfo = testAssets[i];
105
-
106
- let promise = storage.load(assetInfo.type, assetInfo.id, assetInfo.ext);
107
- t.type(promise, 'Promise');
108
-
109
- promise = promise.then(asset => checkAsset(assetInfo, asset));
110
- promises.push(promise);
111
- }
108
+ });
112
109
 
113
- return Promise.all(promises);
110
+ return Promise.all(assetChecks);
114
111
  });
@@ -0,0 +1,64 @@
1
+ const TextEncoder = require('util').TextEncoder;
2
+ const {Headers} = require('cross-fetch');
3
+
4
+ const successText = 'successful response';
5
+
6
+ /**
7
+ * @typedef MockFetchResponse The Response-like object returned by mockFetch.
8
+ * @property {boolean} ok True if the simulated request was successful, false otherwise.
9
+ * @property {number} status The HTTP status code of the simulated request.
10
+ * @property {() => Promise<string>} [text] A success string if the simulated request succeeded, undefined otherwise.
11
+ * @property {() => Promise<Uint8Array>} [arrayBuffer] Same as `text`, but encoded with UTF-8 if present.
12
+ */
13
+
14
+ /**
15
+ * @typedef {RequestInit & {mockFetchTestData: MockFetchTestData}} MockFetchRequestInit
16
+ */
17
+
18
+ /**
19
+ * @typedef MockFetchTestData
20
+ * @property {Headers} [headers] A Headers object initialized with the header info received by mockFetch.
21
+ * @property {Number} [headersCount] The number of headers in the 'headers' property.
22
+ */
23
+
24
+ /**
25
+ * Mock the 'fetch' method from browsers.
26
+ * @param {RequestInfo|URL} resource The (mock) resource to fetch, which will determine the response.
27
+ * @param {MockFetchRequestInit} [options] Optional object containing custom settings for this request.
28
+ * @returns {Promise<MockFetchResponse>} A promise for a Response-like object. Does not fully implement Response.
29
+ */
30
+ const mockFetch = (resource, options) => {
31
+ /** @type MockFetchResponse */
32
+ const results = {
33
+ ok: false,
34
+ status: 0
35
+ };
36
+ if (options?.mockFetchTestData) {
37
+ options.mockFetchTestData.headers = new Headers(options.headers);
38
+ options.mockFetchTestData.headersCount = Array.from(options.mockFetchTestData.headers).length;
39
+ }
40
+ switch (resource) {
41
+ case '200':
42
+ results.ok = true;
43
+ results.status = 200;
44
+ results.text = () => Promise.resolve(successText);
45
+ results.arrayBuffer = () => Promise.resolve(new TextEncoder().encode(successText));
46
+ break;
47
+ case '404':
48
+ results.ok = false;
49
+ results.status = 404;
50
+ break;
51
+ case '500':
52
+ results.ok = false;
53
+ results.status = 500;
54
+ break;
55
+ default:
56
+ throw new Error('unimplemented');
57
+ }
58
+ return Promise.resolve(results);
59
+ };
60
+
61
+ module.exports = {
62
+ mockFetch,
63
+ successText
64
+ };
@@ -1,77 +1,60 @@
1
- const test = require('tap').test;
2
- const TextEncoder = require('util').TextEncoder;
1
+ const tap = require('tap');
3
2
  const TextDecoder = require('util').TextDecoder;
4
3
 
5
- const FetchTool = require('../../src/FetchTool');
6
-
7
- test('send success returns response.text()', t => {
8
- global.fetch = () => Promise.resolve({
9
- ok: true,
10
- text: () => Promise.resolve('successful response')
11
- });
4
+ const {mockFetch, successText} = require('../mocks/mockFetch.js');
5
+
6
+ /**
7
+ * This is the real FetchTool, but the 'cross-fetch' module has been replaced with the mockFetch function.
8
+ * @type {typeof import('../../src/FetchTool')}
9
+ */
10
+ const FetchTool = tap.mock('../../src/FetchTool', {
11
+ 'cross-fetch': {
12
+ default: mockFetch,
13
+ fetch: mockFetch
14
+ }
15
+ });
12
16
 
17
+ tap.test('send success returns response.text()', t => {
13
18
  const tool = new FetchTool();
14
-
19
+
15
20
  return t.resolves(
16
- tool.send('url').then(result => {
17
- t.equal(result, 'successful response');
21
+ tool.send({url: '200'}).then(result => {
22
+ t.equal(result, successText);
18
23
  })
19
24
  );
20
25
  });
21
26
 
22
- test('send failure returns response.status', t => {
23
- global.fetch = () => Promise.resolve({
24
- ok: false,
25
- status: 500
26
- });
27
-
27
+ tap.test('send failure returns response.status', t => {
28
28
  const tool = new FetchTool();
29
29
 
30
- return t.rejects(tool.send('url'), 500);
30
+ return t.rejects(tool.send({url: '500'}), 500);
31
31
  });
32
32
 
33
- test('get success returns Uint8Array.body(response.arrayBuffer())', t => {
34
- const text = 'successful response';
33
+ tap.test('get success returns Uint8Array.body(response.arrayBuffer())', t => {
35
34
  const encoding = 'utf-8';
36
- const encoded = new TextEncoder().encode(text);
37
35
  const decoder = new TextDecoder(encoding);
38
36
 
39
- global.fetch = () => Promise.resolve({
40
- ok: true,
41
- arrayBuffer: () => Promise.resolve(encoded.buffer)
42
- });
43
-
44
37
  const tool = new FetchTool();
45
-
38
+
46
39
  return t.resolves(
47
- tool.get({url: 'url'}).then(result => {
48
- t.equal(decoder.decode(result), text);
40
+ tool.get({url: '200'}).then(result => {
41
+ t.equal(decoder.decode(result), successText);
49
42
  })
50
43
  );
51
44
  });
52
45
 
53
- test('get with 404 response returns null data', t => {
54
- global.fetch = () => Promise.resolve({
55
- ok: false,
56
- status: 404
57
- });
58
-
46
+ tap.test('get with 404 response returns null data', t => {
59
47
  const tool = new FetchTool();
60
48
 
61
49
  return t.resolves(
62
- tool.get('url').then(result => {
50
+ tool.get({url: '404'}).then(result => {
63
51
  t.equal(result, null);
64
52
  })
65
53
  );
66
54
  });
67
55
 
68
- test('get failure returns response.status', t => {
69
- global.fetch = () => Promise.resolve({
70
- ok: false,
71
- status: 500
72
- });
73
-
56
+ tap.test('get failure returns response.status', t => {
74
57
  const tool = new FetchTool();
75
58
 
76
- return t.rejects(tool.get({url: 'url'}), 500);
59
+ return t.rejects(tool.get({url: '500'}), 500);
77
60
  });