mirror of
https://github.com/scratchfoundation/scratch-storage.git
synced 2025-08-28 22:39:11 -04:00
test: convert all tests from node-tap to jest
This commit is contained in:
parent
d4385b2fd1
commit
f6a263d8c1
15 changed files with 7977 additions and 6390 deletions
5
jest.config.js
Normal file
5
jest.config.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
module.exports = {
|
||||
transform: {
|
||||
'\\.(png|svg|wav)$': '<rootDir>/test/transformers/arraybuffer-loader.js'
|
||||
}
|
||||
};
|
13716
package-lock.json
generated
13716
package-lock.json
generated
File diff suppressed because it is too large
Load diff
14
package.json
14
package.json
|
@ -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"
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
module.exports = {
|
||||
extends: ['scratch/node', 'scratch/es6']
|
||||
extends: ['scratch/es6', 'plugin:jest/recommended'],
|
||||
env: {
|
||||
jest: true
|
||||
},
|
||||
plugins: ['jest']
|
||||
};
|
||||
|
|
|
@ -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';
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
8
test/transformers/.eslintrc.js
Normal file
8
test/transformers/.eslintrc.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
module.exports = {
|
||||
extends: ['scratch/es6', 'plugin:jest/recommended'],
|
||||
env: {
|
||||
jest: true,
|
||||
node: true
|
||||
},
|
||||
plugins: ['jest']
|
||||
};
|
10
test/transformers/arraybuffer-loader.js
Normal file
10
test/transformers/arraybuffer-loader.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
module.exports = {
|
||||
process (_sourceText, sourcePath) {
|
||||
return {
|
||||
code: [
|
||||
'const fs = require("fs");',
|
||||
`module.exports = fs.readFileSync('${sourcePath}');`
|
||||
].join('\n')
|
||||
};
|
||||
}
|
||||
};
|
|
@ -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'
|
||||
]);
|
||||
});
|
|
@ -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);
|
||||
});
|
56
test/unit/fetch-tool.test.js
Normal file
56
test/unit/fetch-tool.test.js
Normal 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);
|
||||
});
|
|
@ -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);
|
||||
});
|
59
test/unit/load-default-assets.test.js
Normal file
59
test/unit/load-default-assets.test.js
Normal 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);
|
||||
});
|
|
@ -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
123
test/unit/metadata.test.js
Normal 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');
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue