Browse Source

Speed up test suite by using local npm.js stub

master
Michael Jackson 3 years ago
parent
commit
c3dc2dd014
  1. 6
      jest.config.js
  2. 4
      jest.setup.js
  3. 1
      modules/__mocks__/metadata/@babel/core.json
  4. 1
      modules/__mocks__/metadata/preact.json
  5. 1
      modules/__mocks__/metadata/react.json
  6. 1
      modules/__mocks__/metadata/sinuous.json
  7. 33
      modules/__mocks__/npmMock.js
  8. BIN
      modules/__mocks__/packages/@babel/core-7.5.4.tgz
  9. BIN
      modules/__mocks__/packages/preact-8.4.2.tgz
  10. BIN
      modules/__mocks__/packages/react-16.8.0.tgz
  11. BIN
      modules/__mocks__/packages/sinuous-0.12.9.tgz
  12. 2
      modules/__tests__/regularFile-test.js
  13. 2
      modules/__tests__/serveMetadata-test.js
  14. 12
      modules/actions/serveBrowsePage.js
  15. 22
      modules/middleware/validateVersion.js
  16. 75
      modules/utils/npm.js

6
jest.config.js

@ -3,9 +3,9 @@ module.exports = {
'\\.css$': '<rootDir>/modules/__mocks__/styleMock.js',
'\\.png$': '<rootDir>/modules/__mocks__/imageMock.js',
'entry-manifest': '<rootDir>/modules/__mocks__/entryManifest.js',
'getStats\\.js': '<rootDir>/modules/__mocks__/getStatsMock.js'
'getStats\\.js': '<rootDir>/modules/__mocks__/getStatsMock.js',
'utils\\/npm\\.js': '<rootDir>/modules/__mocks__/npmMock.js'
},
testMatch: ['**/__tests__/*-test.js'],
testURL: 'http://localhost/',
setupTestFrameworkScriptFile: './jest.setup.js'
testURL: 'http://localhost/'
};

4
jest.setup.js

@ -1,4 +0,0 @@
// TODO: Mock out the registry so tests don't actually hit
// the real registry so they don't take so long. Then we can
// remove this.
jest.setTimeout(10000);

1
modules/__mocks__/metadata/@babel/core.json

File diff suppressed because one or more lines are too long

1
modules/__mocks__/metadata/preact.json

File diff suppressed because one or more lines are too long

1
modules/__mocks__/metadata/react.json

File diff suppressed because one or more lines are too long

1
modules/__mocks__/metadata/sinuous.json

File diff suppressed because one or more lines are too long

33
modules/__mocks__/npmMock.js

@ -0,0 +1,33 @@
import fs from 'fs';
import path from 'path';
function getPackageInfo(packageName) {
const file = path.resolve(__dirname, `./metadata/${packageName}.json`);
try {
return JSON.parse(fs.readFileSync(file, 'utf-8'));
} catch (error) {
return null;
}
}
export function getVersionsAndTags(packageName) {
const info = getPackageInfo(packageName);
return info
? { versions: Object.keys(info.versions), tags: info['dist-tags'] }
: [];
}
export function getPackageConfig(packageName, version) {
const info = getPackageInfo(packageName);
return info ? info.versions[version] : null;
}
export function getPackage(packageName, version) {
const file = path.resolve(
__dirname,
`./packages/${packageName}-${version}.tgz`
);
return fs.existsSync(file) ? fs.createReadStream(file) : null;
}

BIN
modules/__mocks__/packages/@babel/core-7.5.4.tgz

Binary file not shown.

BIN
modules/__mocks__/packages/preact-8.4.2.tgz

Binary file not shown.

BIN
modules/__mocks__/packages/react-16.8.0.tgz

Binary file not shown.

BIN
modules/__mocks__/packages/sinuous-0.12.9.tgz

Binary file not shown.

2
modules/__tests__/regularFile-test.js

@ -10,7 +10,7 @@ describe('A request for a JavaScript file', () => {
it('returns 200', done => {
request(server)
.get('/[email protected]6/index.js')
.get('/[email protected]0/index.js')
.end((err, res) => {
expect(res.statusCode).toBe(200);
expect(res.headers['content-type']).toMatch(

2
modules/__tests__/serveMetadata-test.js

@ -10,7 +10,7 @@ describe('A request for metadata', () => {
it('returns 200', done => {
request(server)
.get('/[email protected]6/?meta')
.get('/[email protected]0/?meta')
.end((err, res) => {
expect(res.statusCode).toBe(200);
expect(res.headers['content-type']).toMatch(/\bapplication\/json\b/);

12
modules/actions/serveBrowsePage.js

@ -1,11 +1,12 @@
import { renderToString, renderToStaticMarkup } from 'react-dom/server';
import semver from 'semver';
import BrowseApp from '../client/browse/App.js';
import MainTemplate from '../templates/MainTemplate.js';
import asyncHandler from '../utils/asyncHandler.js';
import getScripts from '../utils/getScripts.js';
import { createElement, createHTML } from '../utils/markup.js';
import { getAvailableVersions } from '../utils/npm.js';
import { getVersionsAndTags } from '../utils/npm.js';
const doctype = '<!DOCTYPE html>';
const globalURLs =
@ -21,6 +22,15 @@ const globalURLs =
'react-dom': '/[email protected]/umd/react-dom.development.js'
};
function byVersion(a, b) {
return semver.lt(a, b) ? -1 : semver.gt(a, b) ? 1 : 0;
}
async function getAvailableVersions(packageName) {
const versionsAndTags = await getVersionsAndTags(packageName);
return versionsAndTags ? versionsAndTags.versions.sort(byVersion) : [];
}
async function serveBrowsePage(req, res) {
const availableVersions = await getAvailableVersions(req.packageName);
const data = {

22
modules/middleware/validateVersion.js

@ -1,6 +1,8 @@
import semver from 'semver';
import asyncHandler from '../utils/asyncHandler.js';
import createPackageURL from '../utils/createPackageURL.js';
import { getPackageConfig, resolveVersion } from '../utils/npm.js';
import { getPackageConfig, getVersionsAndTags } from '../utils/npm.js';
function semverRedirect(req, res, newVersion) {
res
@ -15,6 +17,24 @@ function semverRedirect(req, res, newVersion) {
);
}
async function resolveVersion(packageName, range) {
const versionsAndTags = await getVersionsAndTags(packageName);
if (versionsAndTags) {
const { versions, tags } = versionsAndTags;
if (range in tags) {
range = tags[range];
}
return versions.includes(range)
? range
: semver.maxSatisfying(versions, range);
}
return null;
}
/**
* Check the package version/tag in the URL and make sure it's good. Also
* fetch the package config and add it to req.packageConfig. Redirect to

75
modules/utils/npm.js

@ -1,7 +1,6 @@
import url from 'url';
import https from 'https';
import LRUCache from 'lru-cache';
import semver from 'semver';
import debug from './debug.js';
import bufferStream from './bufferStream.js';
@ -77,18 +76,16 @@ async function fetchPackageInfo(packageName) {
async function fetchVersionsAndTags(packageName) {
const info = await fetchPackageInfo(packageName);
if (info && info.versions) {
return {
versions: Object.keys(info.versions),
tags: info['dist-tags']
};
}
return null;
return info && info.versions
? { versions: Object.keys(info.versions), tags: info['dist-tags'] }
: null;
}
async function getVersionsAndTags(packageName) {
/**
* Returns an object of available { versions, tags }.
* Uses a cache to avoid over-fetching from the registry.
*/
export async function getVersionsAndTags(packageName) {
const cacheKey = `versions-${packageName}`;
const cacheValue = cache.get(cacheKey);
@ -107,45 +104,6 @@ async function getVersionsAndTags(packageName) {
return value;
}
function byVersion(a, b) {
return semver.lt(a, b) ? -1 : semver.gt(a, b) ? 1 : 0;
}
/**
* Returns an array of available versions, sorted by semver.
*/
export async function getAvailableVersions(packageName) {
const versionsAndTags = await getVersionsAndTags(packageName);
if (versionsAndTags) {
return versionsAndTags.versions.sort(byVersion);
}
return [];
}
/**
* Resolves the semver range or tag to a valid version.
* Output is cached to avoid over-fetching from the registry.
*/
export async function resolveVersion(packageName, range) {
const versionsAndTags = await getVersionsAndTags(packageName);
if (versionsAndTags) {
const { versions, tags } = versionsAndTags;
if (range in tags) {
range = tags[range];
}
return versions.includes(range)
? range
: semver.maxSatisfying(versions, range);
}
return null;
}
// All the keys that sometimes appear in package info
// docs that we don't need. There are probably more.
const packageConfigExcludeKeys = [
@ -160,10 +118,10 @@ const packageConfigExcludeKeys = [
'scripts'
];
function cleanPackageConfig(doc) {
return Object.keys(doc).reduce((memo, key) => {
function cleanPackageConfig(config) {
return Object.keys(config).reduce((memo, key) => {
if (!key.startsWith('_') && !packageConfigExcludeKeys.includes(key)) {
memo[key] = doc[key];
memo[key] = config[key];
}
return memo;
@ -172,17 +130,14 @@ function cleanPackageConfig(doc) {
async function fetchPackageConfig(packageName, version) {
const info = await fetchPackageInfo(packageName);
if (!info || !(version in info.versions)) {
return null;
}
return cleanPackageConfig(info.versions[version]);
return info && info.versions && version in info.versions
? cleanPackageConfig(info.versions[version])
: null;
}
/**
* Returns metadata about a package, mostly the same as package.json.
* Output is cached to avoid over-fetching from the registry.
* Uses a cache to avoid over-fetching from the registry.
*/
export async function getPackageConfig(packageName, version) {
const cacheKey = `config-${packageName}-${version}`;

Loading…
Cancel
Save