test: convert all tests from node-tap to jest

This commit is contained in:
Christopher Willis-Ford 2023-05-02 09:09:23 -07:00
parent d4385b2fd1
commit f6a263d8c1
15 changed files with 7977 additions and 6390 deletions

5
jest.config.js Normal file
View file

@ -0,0 +1,5 @@
module.exports = {
transform: {
'\\.(png|svg|wav)$': '<rootDir>/test/transformers/arraybuffer-loader.js'
}
};

13716
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -14,11 +14,11 @@
"build": "webpack --progress --colors --bail",
"coverage": "tap ./test/{unit,integration}/*.js --coverage --coverage-report=lcov",
"commitmsg": "commitlint -e $GIT_PARAMS",
"lint": "eslint .",
"tap-integration": "tap ./test/integration/*.js",
"tap-unit": "tap ./test/unit/*.js",
"tap": "npm run tap-unit && npm run tap-integration",
"test": "npm run lint && npm run tap",
"test": "npm run test:lint && jest \"test[\\\\/](unit|integration)\"",
"test:clearCache": "jest --clearCache",
"test:lint": "eslint .",
"test:integration": "jest \"test[\\\\/]integration\"",
"test:unit": "jest \"test[\\\\/]unit\"",
"version": "json -f package.json -I -e \"this.repository.sha = '$(git log -n1 --pretty=format:%H)'\"",
"watch": "webpack --progress --colors --watch",
"semantic-release": "semantic-release"
@ -44,17 +44,19 @@
"@commitlint/cli": "8.2.0",
"@commitlint/config-conventional": "8.2.0",
"@commitlint/travis-cli": "8.2.0",
"@types/jest": "29.5.1",
"babel-eslint": "10.1.0",
"babel-loader": "8.0.6",
"cz-conventional-changelog": "3.3.0",
"eslint": "7.27.0",
"eslint-config-scratch": "6.0.0",
"eslint-plugin-jest": "27.2.1",
"eslint-plugin-react": "7.24.0",
"file-loader": "4.1.0",
"husky": "1.3.1",
"jest": "29.5.0",
"json": "^9.0.4",
"semantic-release": "^15.10.5",
"tap": "16.3.4",
"uglifyjs-webpack-plugin": "2.2.0",
"webpack": "4.46.0",
"webpack-cli": "3.1.2"

View file

@ -1,3 +1,7 @@
module.exports = {
extends: ['scratch/node', 'scratch/es6']
extends: ['scratch/es6', 'plugin:jest/recommended'],
env: {
jest: true
},
plugins: ['jest']
};

View file

@ -1,5 +1,5 @@
const TextEncoder = require('util').TextEncoder;
const crossFetch = require('cross-fetch');
const crossFetch = jest.requireActual('cross-fetch');
const Headers = crossFetch.Headers;
const successText = 'successful response';

View file

@ -1,25 +1,26 @@
const md5 = require('js-md5');
const test = require('tap').test;
const ScratchStorage = require('../../dist/node/scratch-storage');
jest.dontMock('cross-fetch'); // TODO: actually we should mock this...
const ScratchStorage = require('../../src/index.js');
let storage;
test('constructor', t => {
storage = new ScratchStorage();
t.type(storage, ScratchStorage);
t.end();
test('constructor', () => {
const storage = new ScratchStorage();
expect(storage).toBeInstanceOf(ScratchStorage);
});
/**
*
* @type {AssetTestInfo[]}
* @typedef {object} AssetTestInfo
* @property {AssetType} type - The type of the asset.
* @property {string} id - The asset's unique ID.
* @property {string} md5 - The asset's MD5 hash.
* @property {DataFormat} [ext] - Optional: the asset's data format / file extension.
*/
const testAssets = [
/**
* @param {ScratchStorage} storage The storage module.
* @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,
@ -66,44 +67,51 @@ const testAssets = [
}
];
test('addWebStore', t => {
t.doesNotThrow(() => {
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/`;
});
});
t.doesNotThrow(() => {
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/`
);
});
t.end();
const addWebStores = storage => {
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/`;
},
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/`,
null, null
);
};
test('addWebStore', () => {
const storage = new ScratchStorage();
addWebStores(storage);
expect(storage.webHelper.stores.length).toBe(2);
});
test('load', t => {
test('load', () => {
const storage = new ScratchStorage();
addWebStores(storage);
const testAssets = getTestAssets(storage);
const assetChecks = testAssets.map(async assetInfo => {
const asset = await storage.load(assetInfo.type, assetInfo.id, assetInfo.ext)
.catch(e => {
// tap's output isn't great if we just let it catch the unhandled promise rejection
// 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})`);
});
t.type(asset, storage.Asset);
t.equal(asset.assetId, assetInfo.id);
t.equal(asset.assetType, assetInfo.type);
t.ok(asset.data.length);
expect(asset).toBeInstanceOf(storage.Asset);
expect(asset.assetId).toBe(assetInfo.id);
expect(asset.assetType).toBe(assetInfo.type);
expect(asset.data.length).toBeGreaterThan(0);
// Web assets should come back as clean
t.ok(asset.clean);
expect(asset.clean).toBeTruthy();
if (assetInfo.md5) {
t.equal(md5(asset.data), assetInfo.md5);
// eslint-disable-next-line jest/no-conditional-expect
expect(md5(asset.data)).toBe(assetInfo.md5);
}
});

View file

@ -0,0 +1,8 @@
module.exports = {
extends: ['scratch/es6', 'plugin:jest/recommended'],
env: {
jest: true,
node: true
},
plugins: ['jest']
};

View file

@ -0,0 +1,10 @@
module.exports = {
process (_sourceText, sourcePath) {
return {
code: [
'const fs = require("fs");',
`module.exports = fs.readFileSync('${sourcePath}');`
].join('\n')
};
}
};

View file

@ -1,6 +1,4 @@
const test = require('tap').test;
const ScratchStorage = require('../../dist/node/scratch-storage');
const ScratchStorage = require('../../src');
/**
* Simulate a storage helper, adding log messages when "load" is called rather than actually loading anything.
@ -36,20 +34,18 @@ class LoggingHelper {
}
}
test('ScratchStorage constructor', t => {
test('ScratchStorage constructor', () => {
const storage = new ScratchStorage();
t.type(storage, ScratchStorage);
t.end();
expect(storage).toBeInstanceOf(ScratchStorage);
});
test('LoggingHelper constructor', t => {
test('LoggingHelper constructor', () => {
const storage = new ScratchStorage();
const loggingHelper = new LoggingHelper(storage, 'constructor test', true, []);
t.type(loggingHelper, LoggingHelper);
t.end();
expect(loggingHelper).toBeInstanceOf(LoggingHelper);
});
test('addHelper', t => {
test('addHelper', async () => {
const logContainer = [];
const storage = new ScratchStorage();
@ -69,17 +65,17 @@ test('addHelper', t => {
storage.addHelper(loggingHelpers[1], 0);
// Did they all get added?
t.equal(storage._helpers.length, initialHelperCount + loggingHelpers.length);
expect(storage._helpers.length).toBe(initialHelperCount + loggingHelpers.length);
// We shouldn't have any log entries yet
t.deepEqual(logContainer, []);
expect(logContainer).toStrictEqual([]);
return storage.load(storage.AssetType.Project, '0').then(() => {
// Verify that all helpers were consulted, and in the correct order
t.deepEqual(logContainer, [
'first',
'second',
'third'
]);
});
await storage.load(storage.AssetType.Project, '0');
// Verify that all helpers were consulted, and in the correct order
expect(logContainer).toStrictEqual([
'first',
'second',
'third'
]);
});

View file

@ -1,57 +0,0 @@
const tap = require('tap');
const TextDecoder = require('util').TextDecoder;
const mockFetch = require('../mocks/mock-fetch.js');
/**
* This is the real FetchTool, but the 'cross-fetch' module has been replaced with the mockFetch function.
* @type {typeof import('../../src/FetchTool')}
*/
const FetchTool = tap.mock('../../src/FetchTool', {
'cross-fetch': mockFetch
});
tap.test('send success returns response.text()', t => {
const tool = new FetchTool();
return t.resolves(
tool.send({url: '200'}).then(result => {
t.equal(result, mockFetch.successText);
})
);
});
tap.test('send failure returns response.status', t => {
const tool = new FetchTool();
return t.rejects(tool.send({url: '500'}), 500);
});
tap.test('get success returns Uint8Array.body(response.arrayBuffer())', t => {
const encoding = 'utf-8';
const decoder = new TextDecoder(encoding);
const tool = new FetchTool();
return t.resolves(
tool.get({url: '200'}).then(result => {
t.equal(decoder.decode(result), mockFetch.successText);
})
);
});
tap.test('get with 404 response returns null data', t => {
const tool = new FetchTool();
return t.resolves(
tool.get({url: '404'}).then(result => {
t.equal(result, null);
})
);
});
tap.test('get failure returns response.status', t => {
const tool = new FetchTool();
return t.rejects(tool.get({url: '500'}), 500);
});

View file

@ -0,0 +1,56 @@
const TextDecoder = require('util').TextDecoder;
jest.mock('cross-fetch');
const mockFetch = require('cross-fetch');
const FetchTool = require('../../src/FetchTool.js');
test('send success returns response.text()', async () => {
const tool = new FetchTool();
const result = await tool.send({url: '200'});
expect(result).toBe(mockFetch.successText);
});
test('send failure returns response.status', async () => {
const tool = new FetchTool();
const catcher = jest.fn();
try {
await tool.send({url: '500'});
} catch (e) {
catcher(e);
}
expect(catcher).toHaveBeenCalledWith(500);
});
test('get success returns Uint8Array.body(response.arrayBuffer())', async () => {
const encoding = 'utf-8';
const decoder = new TextDecoder(encoding);
const tool = new FetchTool();
const result = await tool.get({url: '200'});
expect(decoder.decode(result)).toBe(mockFetch.successText);
});
test('get with 404 response returns null data', async () => {
const tool = new FetchTool();
const result = await tool.get({url: '404'});
expect(result).toBeNull();
});
test('get failure returns response.status', async () => {
const tool = new FetchTool();
const catcher = jest.fn();
try {
await tool.get({url: '500'});
} catch (e) {
catcher(e);
}
expect(catcher).toHaveBeenCalledWith(500);
});

View file

@ -1,48 +0,0 @@
const md5 = require('js-md5');
const test = require('tap').test;
const ScratchStorage = require('../../dist/node/scratch-storage');
let storage;
test('constructor', t => {
storage = new ScratchStorage();
t.type(storage, ScratchStorage);
t.end();
});
const defaultAssetTypes = [storage.AssetType.ImageBitmap, storage.AssetType.ImageVector, storage.AssetType.Sound];
const defaultIds = {};
test('getDefaultAssetId', t => {
for (let i = 0; i < defaultAssetTypes.length; ++i) {
const assetType = defaultAssetTypes[i];
const id = storage.getDefaultAssetId(assetType);
t.type(id, 'string');
defaultIds[assetType.name] = id;
}
t.end();
});
test('load', t => {
const promises = [];
const checkAsset = (assetType, id, asset) => {
t.type(asset, storage.Asset);
t.strictEqual(asset.assetId, id);
t.strictEqual(asset.assetType, assetType);
t.ok(asset.data.length);
t.strictEqual(md5(asset.data), id);
};
for (let i = 0; i < defaultAssetTypes.length; ++i) {
const assetType = defaultAssetTypes[i];
const id = defaultIds[assetType.name];
const promise = storage.load(assetType, id);
t.type(promise, 'Promise');
const checkedPromise = promise.then(asset => checkAsset(assetType, id, asset));
promises.push(checkedPromise);
}
return Promise.all(promises);
});

View file

@ -0,0 +1,59 @@
const md5 = require('js-md5');
const ScratchStorage = require('../../src/index.js');
const getDefaultAssetTypes = storage => {
const defaultAssetTypes = [storage.AssetType.ImageBitmap, storage.AssetType.ImageVector, storage.AssetType.Sound];
return defaultAssetTypes;
};
const getDefaultAssetIds = (storage, defaultAssetTypes) => {
const defaultIds = {};
for (const assetType of defaultAssetTypes) {
const id = storage.getDefaultAssetId(assetType);
defaultIds[assetType.name] = id;
}
return defaultIds;
};
test('constructor', () => {
const storage = new ScratchStorage();
expect(storage).toBeInstanceOf(ScratchStorage);
});
test('getDefaultAssetId', () => {
const storage = new ScratchStorage();
const defaultAssetTypes = getDefaultAssetTypes(storage);
const defaultIds = getDefaultAssetIds(storage, defaultAssetTypes);
for (const assetType of defaultAssetTypes) {
const id = defaultIds[assetType.name];
expect(typeof id).toBe('string');
}
});
test('load', () => {
const storage = new ScratchStorage();
const defaultAssetTypes = getDefaultAssetTypes(storage);
const defaultIds = getDefaultAssetIds(storage, defaultAssetTypes);
const promises = [];
const checkAsset = (assetType, id, asset) => {
expect(asset).toBeInstanceOf(storage.Asset);
expect(asset.assetId).toStrictEqual(id);
expect(asset.assetType).toStrictEqual(assetType);
expect(asset.data.length).toBeTruthy();
expect(md5(asset.data)).toBe(id);
};
for (const assetType of defaultAssetTypes) {
const id = defaultIds[assetType.name];
const promise = storage.load(assetType, id);
expect(promise).toBeInstanceOf(Promise);
const checkedPromise = promise.then(asset => checkAsset(assetType, id, asset));
promises.push(checkedPromise);
}
return Promise.all(promises);
});

View file

@ -1,147 +0,0 @@
const tap = require('tap');
const mockFetchModule = require('../mocks/mock-fetch.js');
// Call this separately from each test to ensure that metadata gets reset.
// This is especially important when parallelizing tests!
const setupModules = () => {
/**
* This instance of scratchFetch will be shared between this file and FetchTool.
* By sharing the same instance, the test can affect the metadata that FetchTool will use.
*/
const scratchFetchModule = tap.mock('../../src/scratchFetch', {
'cross-fetch': mockFetchModule
});
/**
* This is the real FetchTool, but the 'cross-fetch' module has been replaced with the mockFetch function.
* @type {typeof import('../../src/FetchTool')}
*/
const FetchTool = tap.mock('../../src/FetchTool', {
'cross-fetch': mockFetchModule,
// Make sure FetchTool uses the same scratchFetch instance
'../../src/scratchFetch': scratchFetchModule
});
return {scratchFetchModule, FetchTool};
};
tap.test('get without metadata', async t => {
const {FetchTool} = setupModules();
const tool = new FetchTool();
/** @type import('../mocks/mock-fetch.js').MockFetchTestData */
const mockFetchTestData = {};
const result = await tool.get({url: '200', mockFetchTestData});
t.type(result, Uint8Array);
t.ok(mockFetchTestData.headers, 'mockFetch did not report headers');
t.equal(mockFetchTestData.headersCount, 0);
});
tap.test('get with metadata', async t => {
const {scratchFetchModule, FetchTool} = setupModules();
const {RequestMetadata, setMetadata} = scratchFetchModule;
const tool = new FetchTool();
setMetadata(RequestMetadata.ProjectId, 1234);
setMetadata(RequestMetadata.RunId, 5678);
/** @type import('../mocks/mock-fetch.js').MockFetchTestData */
const mockFetchTestData = {};
const result = await tool.get({url: '200', mockFetchTestData});
t.type(result, Uint8Array);
t.ok(mockFetchTestData.headers, 'mockFetch did not report headers');
t.equal(mockFetchTestData.headersCount, 2);
t.equal(mockFetchTestData.headers?.get(RequestMetadata.ProjectId), '1234');
t.equal(mockFetchTestData.headers?.get(RequestMetadata.RunId), '5678');
});
tap.test('send without metadata', async t => {
const {FetchTool} = setupModules();
const tool = new FetchTool();
/** @type import('../mocks/mock-fetch.js').MockFetchTestData */
const mockFetchTestData = {};
const result = await tool.send({url: '200', mockFetchTestData});
t.type(result, 'string');
t.ok(mockFetchTestData.headers, 'mockFetch did not report headers');
t.equal(mockFetchTestData.headersCount, 0);
});
tap.test('send with metadata', async t => {
const {scratchFetchModule, FetchTool} = setupModules();
const {RequestMetadata, setMetadata} = scratchFetchModule;
const tool = new FetchTool();
setMetadata(RequestMetadata.ProjectId, 4321);
setMetadata(RequestMetadata.RunId, 8765);
/** @type import('../mocks/mock-fetch.js').MockFetchTestData */
const mockFetchTestData = {};
const result = await tool.send({url: '200', mockFetchTestData});
t.type(result, 'string');
t.ok(mockFetchTestData.headers, 'mockFetch did not report headers');
t.equal(mockFetchTestData.headersCount, 2);
t.equal(mockFetchTestData.headers?.get(RequestMetadata.ProjectId), '4321');
t.equal(mockFetchTestData.headers?.get(RequestMetadata.RunId), '8765');
});
tap.test('selectively delete metadata', async t => {
const {scratchFetchModule, FetchTool} = setupModules();
const {RequestMetadata, setMetadata, unsetMetadata} = scratchFetchModule;
// verify that these special values are preserved and not interpreted as "delete"
setMetadata(RequestMetadata.ProjectId, null);
setMetadata(RequestMetadata.RunId, void 0); // void 0 = undefined
const tool = new FetchTool();
/** @type import('../mocks/mock-fetch.js').MockFetchTestData */
const mockFetchTestData = {};
const result1 = await tool.send({url: '200', mockFetchTestData});
t.type(result1, 'string');
t.ok(mockFetchTestData.headers, 'mockFetch did not report headers');
t.equal(mockFetchTestData.headersCount, 2);
t.equal(mockFetchTestData.headers?.get(RequestMetadata.ProjectId), 'null'); // string "null" means it's present
t.equal(mockFetchTestData.headers?.get(RequestMetadata.RunId), 'undefined');
// remove the Project ID from metadata
unsetMetadata(RequestMetadata.ProjectId);
const result2 = await tool.send({url: '200', mockFetchTestData});
t.type(result2, 'string');
t.ok(mockFetchTestData.headers, 'mockFetch did not report headers');
t.equal(mockFetchTestData.headersCount, 1);
t.equal(mockFetchTestData.headers?.get(RequestMetadata.ProjectId), null); // value `null` means it's missing
t.equal(mockFetchTestData.headers?.get(RequestMetadata.RunId), 'undefined');
});
tap.test('metadata has case-insensitive keys', async t => {
const {scratchFetchModule, FetchTool} = setupModules();
const {setMetadata} = scratchFetchModule;
setMetadata('foo', 1);
setMetadata('FOO', 2);
const tool = new FetchTool();
/** @type import('../mocks/mock-fetch.js').MockFetchTestData */
const mockFetchTestData = {};
await tool.get({url: '200', mockFetchTestData});
t.ok(mockFetchTestData.headers, 'mockFetch did not report headers');
t.equal(mockFetchTestData.headersCount, 1);
t.equal(mockFetchTestData.headers?.get('foo'), '2');
});

123
test/unit/metadata.test.js Normal file
View file

@ -0,0 +1,123 @@
jest.mock('cross-fetch');
beforeEach(() => {
// reset the metadata container to ensure the tests don't interfere with each other
// but this also means we need to `require` inside the tests
/* eslint-disable global-require */
jest.resetModules();
});
test('get without metadata', async () => {
const FetchTool = require('../../src/FetchTool.js');
const tool = new FetchTool();
const mockFetchTestData = {};
const result = await tool.get({url: '200', mockFetchTestData});
expect(result).toBeInstanceOf(Uint8Array);
expect(mockFetchTestData.headers).toBeTruthy();
expect(mockFetchTestData.headersCount).toBe(0);
});
test('get with metadata', async () => {
const FetchTool = require('../../src/FetchTool.js');
const ScratchFetch = require('../../src/scratchFetch');
const {RequestMetadata, setMetadata} = ScratchFetch;
const tool = new FetchTool();
setMetadata(RequestMetadata.ProjectId, 1234);
setMetadata(RequestMetadata.RunId, 5678);
const mockFetchTestData = {};
const result = await tool.get({url: '200', mockFetchTestData});
expect(result).toBeInstanceOf(Uint8Array);
expect(mockFetchTestData.headers).toBeTruthy();
expect(mockFetchTestData.headersCount).toBe(2);
expect(mockFetchTestData.headers?.get(RequestMetadata.ProjectId)).toBe('1234');
expect(mockFetchTestData.headers?.get(RequestMetadata.RunId)).toBe('5678');
});
test('send without metadata', async () => {
const FetchTool = require('../../src/FetchTool.js');
const tool = new FetchTool();
const mockFetchTestData = {};
const result = await tool.send({url: '200', mockFetchTestData});
expect(typeof result).toBe('string');
expect(mockFetchTestData.headers).toBeTruthy();
expect(mockFetchTestData.headersCount).toBe(0);
});
test('send with metadata', async () => {
const FetchTool = require('../../src/FetchTool.js');
const ScratchFetch = require('../../src/scratchFetch');
const {RequestMetadata, setMetadata} = ScratchFetch;
const tool = new FetchTool();
setMetadata(RequestMetadata.ProjectId, 4321);
setMetadata(RequestMetadata.RunId, 8765);
const mockFetchTestData = {};
const result = await tool.send({url: '200', mockFetchTestData});
expect(typeof result).toBe('string');
expect(mockFetchTestData.headers).toBeTruthy();
expect(mockFetchTestData.headersCount).toBe(2);
expect(mockFetchTestData.headers?.get(RequestMetadata.ProjectId)).toBe('4321');
expect(mockFetchTestData.headers?.get(RequestMetadata.RunId)).toBe('8765');
});
test('selectively delete metadata', async () => {
const FetchTool = require('../../src/FetchTool.js');
const ScratchFetch = require('../../src/scratchFetch');
const {RequestMetadata, setMetadata, unsetMetadata} = ScratchFetch;
// verify that these special values are preserved and not interpreted as "delete"
setMetadata(RequestMetadata.ProjectId, null);
setMetadata(RequestMetadata.RunId, void 0); // void 0 = undefined
const tool = new FetchTool();
const mockFetchTestData = {};
const result1 = await tool.send({url: '200', mockFetchTestData});
expect(typeof result1).toBe('string');
expect(mockFetchTestData.headers).toBeTruthy();
expect(mockFetchTestData.headersCount).toBe(2);
expect(mockFetchTestData.headers?.get(RequestMetadata.ProjectId)).toBe('null'); // string "null" means it's present
expect(mockFetchTestData.headers?.get(RequestMetadata.RunId)).toBe('undefined');
// remove the Project ID from metadata
unsetMetadata(RequestMetadata.ProjectId);
const result2 = await tool.send({url: '200', mockFetchTestData});
expect(typeof result2).toBe('string');
expect(mockFetchTestData.headers).toBeTruthy();
expect(mockFetchTestData.headersCount).toBe(1);
expect(mockFetchTestData.headers?.get(RequestMetadata.ProjectId)).toBeNull(); // value `null` means it's present
expect(mockFetchTestData.headers?.get(RequestMetadata.RunId)).toBe('undefined');
});
test('metadata has case-insensitive keys', async () => {
const FetchTool = require('../../src/FetchTool.js');
const ScratchFetch = require('../../src/scratchFetch');
const {setMetadata} = ScratchFetch;
setMetadata('foo', 1);
setMetadata('FOO', 2);
const tool = new FetchTool();
const mockFetchTestData = {};
await tool.get({url: '200', mockFetchTestData});
expect(mockFetchTestData.headers).toBeTruthy();
expect(mockFetchTestData.headersCount).toBe(1);
expect(mockFetchTestData.headers?.get('foo')).toBe('2');
});