Skip to content

Commit 3b25c63

Browse files
authored
test: Test 250+ popular npm packages (#93)
1 parent 49d69ba commit 3b25c63

File tree

4 files changed

+332
-1
lines changed

4 files changed

+332
-1
lines changed

.github/workflows/ci.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,15 @@ jobs:
103103
- name: Verify Minimum Coverage Is Met
104104
run: >
105105
lcov --summary all_lcov.info | grep lines | cut -d' ' -f 4 | cut -d% -f 1 | xargs node -e "x=process.argv[1];console.log(x);assert(+x >= 90)"
106+
107+
integration:
108+
runs-on: ubuntu-latest
109+
steps:
110+
- uses: actions/checkout@v4
111+
# We only test that exports match on a single node version because many of
112+
# the libraries we test do not support older node versions
113+
- uses: actions/setup-node@v4
114+
with:
115+
node-version: 'lts/*'
116+
- run: npm install
117+
- run: npm run test:e2e

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ package-lock.json
22
yarn.lock
33
node_modules
44
coverage
5-
.DS_Store
5+
.DS_Store
6+
test/check-exports/package.json

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"main": "index.js",
66
"scripts": {
77
"test": "c8 --reporter lcov --check-coverage --lines 50 imhotap --files test/{hook,low-level,other,get-esm-exports}/*",
8+
"test:e2e": "node test/check-exports/test.mjs",
89
"test:ts": "c8 --reporter lcov imhotap --files test/typescript/*.test.mts",
910
"coverage": "c8 --reporter html imhotap --files test/{hook,low-level,other,get-esm-exports}/* && echo '\nNow open coverage/index.html\n'",
1011
"lint": "eslint .",

test/check-exports/test.mjs

Lines changed: 317 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,317 @@
1+
import { spawnSync } from 'child_process'
2+
import { deepStrictEqual } from 'assert'
3+
import { dirname, resolve } from 'path'
4+
import { fileURLToPath } from 'url'
5+
6+
const cwd = dirname(fileURLToPath(import.meta.url))
7+
const hook = resolve(cwd, '..', '..', 'hook.mjs')
8+
9+
const mostPopular240NpmModules = [
10+
'ansi-styles',
11+
'semver',
12+
'supports-color',
13+
'chalk', 'has-flag',
14+
'debug',
15+
'tslib',
16+
'color-convert',
17+
'ms',
18+
'color-name',
19+
'lru-cache',
20+
'minimatch',
21+
'strip-ansi',
22+
'source-map',
23+
'ansi-regex',
24+
'glob',
25+
'readable-stream',
26+
'commander',
27+
'yallist',
28+
'string-width',
29+
'escape-string-regexp',
30+
'brace-expansion',
31+
'find-up',
32+
'p-locate',
33+
'locate-path',
34+
'wrap-ansi',
35+
'p-limit',
36+
'safe-buffer',
37+
'kind-of',
38+
'minipass',
39+
'uuid',
40+
'string_decoder',
41+
'ajv',
42+
'emoji-regex',
43+
'isarray',
44+
'react-is',
45+
'fs-extra',
46+
'is-fullwidth-code-point',
47+
'get-stream',
48+
'json-schema-traverse',
49+
'yargs-parser',
50+
'glob-parent',
51+
'yargs',
52+
'rimraf',
53+
'acorn',
54+
'which',
55+
'estraverse',
56+
'js-yaml',
57+
'path-exists',
58+
'argparse',
59+
'pretty-format',
60+
'resolve-from',
61+
'cliui',
62+
'schema-utils',
63+
'globals',
64+
'camelcase',
65+
'execa',
66+
'punycode',
67+
'path-key',
68+
'signal-exit',
69+
'inherits',
70+
'resolve',
71+
'mkdirp',
72+
'is-stream',
73+
'ws',
74+
'universalify',
75+
'qs',
76+
'slash',
77+
'json5',
78+
'iconv-lite',
79+
'form-data',
80+
'is-number',
81+
'eslint-visitor-keys',
82+
'@jest/types',
83+
'postcss',
84+
'make-dir',
85+
'pify',
86+
'cross-spawn',
87+
'braces',
88+
'whatwg-url',
89+
'fill-range',
90+
'eslint-scope',
91+
'tr46',
92+
'micromatch',
93+
'convert-source-map',
94+
'define-property',
95+
'agent-base',
96+
'shebang-regex',
97+
'shebang-command',
98+
'mimic-fn',
99+
'globby',
100+
'npm-run-path',
101+
'mime',
102+
'@babel/code-frame',
103+
'extend-shallow',
104+
'to-regex-range',
105+
'onetime',
106+
'https-proxy-agent',
107+
'y18n',
108+
'buffer',
109+
'strip-bom',
110+
'is-glob',
111+
'doctrine',
112+
'picocolors',
113+
'pkg-dir',
114+
'@babel/types',
115+
'regenerator-runtime',
116+
'human-signals',
117+
'@jridgewell/trace-mapping',
118+
'ignore',
119+
'jsesc',
120+
'parse-json',
121+
'jest-worker',
122+
'graceful-fs',
123+
'jest-util',
124+
'jsonfile',
125+
'normalize-path',
126+
'strip-json-comments',
127+
'cosmiconfig',
128+
'minimist',
129+
'path-type',
130+
'@babel/parser',
131+
'balanced-match',
132+
'picomatch',
133+
'typescript',
134+
'isexe',
135+
'statuses',
136+
'entities',
137+
'bytes',
138+
'node-fetch',
139+
'http-errors',
140+
'@babel/highlight',
141+
'@babel/helper-validator-identifier',
142+
'function-bind',
143+
'async',
144+
'sprintf-js',
145+
'@babel/generator',
146+
'is-extendable',
147+
'get-intrinsic',
148+
'lodash',
149+
'mime-db',
150+
'source-map-support',
151+
'mime-types',
152+
'is-arrayish',
153+
'@babel/core',
154+
'once',
155+
'anymatch',
156+
'depd',
157+
'hosted-git-info',
158+
'path-to-regexp',
159+
'axios',
160+
'is-core-module',
161+
'@babel/template',
162+
'cookie',
163+
'write-file-atomic',
164+
'js-tokens',
165+
'@typescript-eslint/typescript-estree',
166+
'@typescript-eslint/types',
167+
'object-inspect',
168+
'wrappy',
169+
'is-extglob',
170+
'chokidar',
171+
'@typescript-eslint/visitor-keys',
172+
'call-bind',
173+
'loader-utils',
174+
'browserslist',
175+
'http-proxy-agent',
176+
'fast-glob',
177+
'concat-map',
178+
'inflight',
179+
'ajv-keywords',
180+
'ansi-escapes',
181+
'ci-info',
182+
'fast-deep-equal',
183+
'caniuse-lite',
184+
'fs.realpath',
185+
'@jridgewell/gen-mapping',
186+
'setprototypeof',
187+
'strip-final-newline',
188+
'optionator',
189+
'path-is-absolute',
190+
'@babel/traverse',
191+
'core-util-is',
192+
'has-symbols',
193+
'yocto-queue',
194+
'p-try',
195+
'electron-to-chromium',
196+
'@smithy/smithy-client',
197+
'yaml',
198+
'ini',
199+
'@babel/helper-plugin-utils',
200+
'jest-get-type',
201+
'type-check',
202+
'levn',
203+
'is-descriptor',
204+
'prelude-ls',
205+
'slice-ansi',
206+
'@typescript-eslint/scope-manager',
207+
'isobject',
208+
'esprima',
209+
'@babel/helper-split-export-declaration',
210+
'callsites',
211+
'readdirp',
212+
'escalade',
213+
'import-fresh',
214+
'get-caller-file',
215+
'@jridgewell/sourcemap-codec',
216+
'acorn-walk',
217+
'rxjs',
218+
'ieee754',
219+
'is-plain-obj',
220+
'istanbul-lib-instrument',
221+
'@babel/helper-module-imports',
222+
'side-channel',
223+
'normalize-package-data',
224+
'is-plain-object',
225+
'@jridgewell/resolve-uri',
226+
'follow-redirects',
227+
'array-union',
228+
'json-parse-even-better-errors',
229+
'path-parse',
230+
'has-property-descriptors',
231+
'uri-js',
232+
'safer-buffer',
233+
'@babel/helpers',
234+
'on-finished',
235+
'@babel/helper-function-name',
236+
'p-map',
237+
'postcss-value-parser',
238+
'indent-string',
239+
'@babel/helper-module-transforms',
240+
'object-assign',
241+
'delayed-stream',
242+
'@nodelib/fs.stat',
243+
'require-directory',
244+
'diff',
245+
'parse5',
246+
'asynckit',
247+
'tmp',
248+
'combined-stream'
249+
]
250+
251+
const otherCommonModulesUsedWithInstrumentation = [
252+
'express',
253+
'fastify',
254+
'@hapi/hapi',
255+
'connect',
256+
'svelte',
257+
'@sveltejs/kit',
258+
'next',
259+
'gatsby',
260+
'@remix-run/node',
261+
'@remix-run/react'
262+
]
263+
264+
const modules = [...mostPopular240NpmModules, ...otherCommonModulesUsedWithInstrumentation]
265+
266+
function installLibs (names) {
267+
spawnSync('npm', ['init', '-y'], { cwd })
268+
spawnSync('npm', ['install', ...names], { cwd })
269+
}
270+
271+
function getExports (name, loader) {
272+
const args = ['--input-type=module', '--no-warnings', '-e', `import * as lib from '${name}'; console.log(JSON.stringify(Object.keys(lib)))`]
273+
if (loader) args.push(loader)
274+
const out = spawnSync(process.execPath, args, { cwd })
275+
if (out.status !== 0) {
276+
console.error(out.stderr.toString())
277+
throw new Error(`Getting exports returned non-zero exit code '${name}'`)
278+
}
279+
const stdout = out.stdout.toString()
280+
return JSON.parse(stdout).sort()
281+
}
282+
283+
const NPM_LIST_SEMVER_PARSE = /.*@((0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?)/
284+
285+
function getVersion (name) {
286+
const result = spawnSync('npm', ['list', name, '--depth', '0'], { cwd })
287+
const stdout = result.output.toString()
288+
const [, version] = stdout.match(NPM_LIST_SEMVER_PARSE)
289+
return version
290+
}
291+
292+
function testLib (name) {
293+
const version = getVersion(name)
294+
try {
295+
const expected = getExports(name)
296+
const actual = getExports(name, `--experimental-loader=${hook}`)
297+
deepStrictEqual(actual, expected, `Exports for ${name} are different`)
298+
console.log(`✅ Exports for ${name}@${version} match`)
299+
return false
300+
} catch (err) {
301+
console.error(`❌ Error getting exports for ${name}@${version}:`, err)
302+
return true
303+
}
304+
}
305+
306+
console.log(`📦 Installing ${modules.length} libraries...`)
307+
installLibs(modules)
308+
309+
let errored = false
310+
for (const mod of modules) {
311+
errored += testLib(mod)
312+
}
313+
314+
if (errored) {
315+
console.error('❌ Some tests failed')
316+
process.exit(1)
317+
}

0 commit comments

Comments
 (0)