Skip to content

Commit c55df02

Browse files
committed
Update test helpers to use isolation module
1 parent 56e536d commit c55df02

2 files changed

Lines changed: 94 additions & 87 deletions

File tree

test/utils/test-helpers.d.mts

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,15 @@
11
/** @fileoverview TypeScript declaration for test-helpers.mjs module. */
22

33
interface IsolatePackageOptions {
4-
entryPoints?: string[]
4+
imports?: Record<string, string>
55
}
66

7-
interface IsolatePackageResult<T = unknown> {
8-
pkgPath: string
9-
modules?: T[]
7+
interface IsolatePackageResult {
8+
exports?: Record<string, any>
9+
tmpdir: string
1010
}
1111

12-
interface MultiEntryTestResult<T = unknown> {
13-
pkgPath: string
14-
modules: T[]
15-
}
16-
17-
interface TestResult {
18-
pkgPath: string
19-
}
20-
21-
export declare function isolatePackage<T = unknown>(
22-
packageOrPath: string,
12+
export declare function isolatePackage(
13+
packageSpec: string,
2314
options?: IsolatePackageOptions,
24-
): Promise<IsolatePackageResult<T>>
25-
26-
export declare function setupMultiEntryTest<T = unknown>(
27-
sockRegPkgName: string,
28-
entryPoints: string[],
29-
): Promise<MultiEntryTestResult<T>>
30-
31-
export declare function setupPackageTest(
32-
sockRegPkgName: string,
33-
): Promise<TestResult>
15+
): Promise<IsolatePackageResult>

test/utils/test-helpers.mjs

Lines changed: 87 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,112 +1,137 @@
11
/**
22
* @fileoverview Shared test utilities for npm package testing.
3-
* Provides helpers for setting up isolated test environments for both
4-
* socket-registry packages and local development packages.
3+
* Provides helpers for setting up isolated test environments.
54
*/
65

76
import { existsSync } from 'node:fs'
87
import path from 'node:path'
9-
import { fileURLToPath } from 'node:url'
108

119
import constants from '../../scripts/constants.mjs'
12-
import { installPackageForTesting } from '../../scripts/utils/package.mjs'
1310
import {
1411
readPackageJson,
12+
isolatePackage as registryIsolatePackage,
1513
resolveOriginalPackageName,
1614
} from '../../registry/dist/lib/packages.js'
17-
18-
const __dirname = path.dirname(fileURLToPath(import.meta.url))
15+
import { cleanTestScript } from './script-cleaning.mjs'
16+
import { testRunners } from './test-runners.mjs'
1917

2018
/**
2119
* Isolates a package in a temporary test environment.
2220
*
23-
* Supports two modes:
21+
* Supports multiple input types:
2422
* 1. Socket registry packages: Pass package name (e.g., '@socketregistry/packageurl-js')
2523
* 2. Local development packages: Pass relative or absolute path (e.g., '../../socket-packageurl-js' or '.')
24+
* 3. npm package specs: Pass any npm-package-arg compatible spec
2625
*
27-
* The helper creates an isolated test environment by:
28-
* - Installing the package in a temporary directory
29-
* - Copying package files to node_modules
30-
* - Installing dependencies
31-
* - Preserving test scripts (for registry packages)
32-
*
33-
* @param {string} packageOrPath - Package name or local path to package directory.
26+
* @param {string} packageSpec - Package name, path, or npm package spec.
3427
* @param {object} [options] - Optional configuration.
35-
* @param {string[]} [options.entryPoints] - Array of entry point filenames to load.
36-
* @returns {Promise<{pkgPath: string, modules?: any[]}>}
28+
* @param {Record<string, string>} [options.imports] - Map of import names to module specifiers (e.g., { PackageURL: './package-url.js' }).
29+
* @returns {Promise<{exports?: Record<string, any>, tmpdir: string}>}
3730
*
3831
* @example
39-
* // Test a socket-registry package
40-
* const { pkgPath } = await isolatePackage('@socketregistry/packageurl-js')
32+
* const { tmpdir } = await isolatePackage('@socketregistry/packageurl-js')
4133
*
4234
* @example
43-
* // Test a local development package
44-
* const { pkgPath } = await isolatePackage('../../socket-packageurl-js')
35+
* const { tmpdir } = await isolatePackage('../../socket-packageurl-js')
4536
*
4637
* @example
47-
* // Load multiple entry points
48-
* const { pkgPath, modules } = await isolatePackage('../../socket-packageurl-js', {
49-
* entryPoints: ['package-url.js', 'url-converter.js']
38+
* const { tmpdir, exports } = await isolatePackage('packageurl-js@1.0.0', {
39+
* imports: { PackageURL: './package-url.js', convert: './url-converter.js' }
5040
* })
41+
* // exports.PackageURL, exports.convert
5142
*/
52-
async function isolatePackage(packageOrPath, options = {}) {
53-
const { entryPoints } = options
54-
55-
// Determine if this is a path or package name
56-
const isPath = packageOrPath.startsWith('.') || path.isAbsolute(packageOrPath)
43+
async function isolatePackage(packageSpec, options = {}) {
44+
const { imports } = options
5745

46+
let resolvedSpec = packageSpec
5847
let sourcePath
59-
let packageName
60-
let versionSpec
61-
62-
if (isPath) {
63-
// Local development package
64-
sourcePath = path.resolve(__dirname, '..', '..', packageOrPath)
65-
66-
// Read package.json to get the name
67-
const pkgJson = await readPackageJson(sourcePath, { normalize: true })
68-
packageName = pkgJson.name
69-
} else {
70-
// Socket registry package
71-
const socketPkgName = packageOrPath
48+
let hasSourcePath = false
49+
50+
// Check if this is a Socket registry package.
51+
if (
52+
packageSpec.startsWith('@socketregistry/') &&
53+
!packageSpec.includes('@', 1)
54+
) {
55+
const socketPkgName = packageSpec
7256
sourcePath = path.join(constants.npmPackagesPath, socketPkgName)
7357

7458
if (!existsSync(sourcePath)) {
7559
throw new Error(`No Socket override found for ${socketPkgName}`)
7660
}
7761

78-
// Resolve to original npm package name
79-
packageName = resolveOriginalPackageName(socketPkgName)
62+
// Resolve to original npm package name.
63+
const packageName = resolveOriginalPackageName(socketPkgName)
8064

81-
// Get version spec from test/npm/package.json
65+
// Get version spec from test/npm/package.json.
8266
const testPkgJson = await readPackageJson(constants.testNpmPkgJsonPath, {
8367
normalize: true,
8468
})
85-
versionSpec = testPkgJson.devDependencies?.[packageName]
69+
const spec = testPkgJson.devDependencies?.[packageName]
8670

87-
if (!versionSpec) {
71+
if (!spec) {
8872
throw new Error(`${packageName} not in devDependencies`)
8973
}
90-
}
9174

92-
const result = await installPackageForTesting(sourcePath, packageName, {
93-
versionSpec,
94-
})
95-
96-
if (!result.installed) {
97-
throw new Error(`Failed to install package: ${result.reason}`)
75+
resolvedSpec = `${packageName}@${spec}`
76+
hasSourcePath = true
9877
}
9978

100-
const pkgPath = result.packagePath
101-
102-
if (entryPoints && entryPoints.length > 0) {
103-
const modules = entryPoints.map(entryPoint =>
104-
require(path.join(pkgPath, entryPoint)),
105-
)
106-
return { pkgPath, modules }
107-
}
79+
const result = await registryIsolatePackage(resolvedSpec, {
80+
imports,
81+
onPackageJson: async pkgJson => {
82+
// Preserve test scripts for registry packages.
83+
if (hasSourcePath) {
84+
const originalScripts = pkgJson.scripts
85+
86+
if (originalScripts) {
87+
pkgJson.scripts = pkgJson.scripts || {}
88+
89+
const additionalTestRunners = [
90+
...testRunners,
91+
'test:stock',
92+
'test:all',
93+
]
94+
let actualTestScript = additionalTestRunners.find(
95+
runner => originalScripts[runner],
96+
)
97+
98+
if (!actualTestScript && originalScripts.test) {
99+
const testMatch = originalScripts.test.match(/npm run ([-:\w]+)/)
100+
if (testMatch && originalScripts[testMatch[1]]) {
101+
actualTestScript = testMatch[1]
102+
}
103+
}
104+
105+
if (actualTestScript && originalScripts[actualTestScript]) {
106+
pkgJson.scripts.test = cleanTestScript(
107+
originalScripts[actualTestScript],
108+
)
109+
if (actualTestScript !== 'test') {
110+
pkgJson.scripts[actualTestScript] = cleanTestScript(
111+
originalScripts[actualTestScript],
112+
)
113+
}
114+
} else if (originalScripts.test) {
115+
pkgJson.scripts.test = cleanTestScript(originalScripts.test)
116+
}
117+
118+
for (const { 0: key, 1: value } of Object.entries(originalScripts)) {
119+
if (
120+
(key.startsWith('test:') || key.startsWith('tests')) &&
121+
!pkgJson.scripts[key]
122+
) {
123+
pkgJson.scripts[key] = cleanTestScript(value)
124+
}
125+
}
126+
}
127+
}
128+
129+
return pkgJson
130+
},
131+
sourcePath,
132+
})
108133

109-
return { pkgPath }
134+
return result
110135
}
111136

112137
export { isolatePackage }

0 commit comments

Comments
 (0)