Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

expose core test types properly #1087

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion .eslintrc.js
Expand Up @@ -108,7 +108,7 @@ module.exports = {
},
overrides: [
{
files: ['**/*/*/test/**/*.js'],
files: ['**/*/*/test/**/*.js', '**/*/*/test/**/*.ts'],
extends: ['plugin:ava/recommended'],
env: {
browser: true,
Expand Down
30 changes: 30 additions & 0 deletions packages/core/src/options.js
@@ -0,0 +1,30 @@
// @ts-check

/**
* Value of the {@link LavaMoatOpts.scuttleGlobalThis} option.
*
* @typedef LavaMoatScuttleOpts
* @property {boolean} [enabled]
* @property {string[]} [exceptions]
* @property {string} [scuttlerName]
*/

/**
* Options for LavaMoat
*
* @typedef LavaMoatOpts
* @property {LavaMoatScuttleOpts} [scuttleGlobalThis] Enable or disable
* scuttling of `globalThis`
* @property {string[]} [scuttleGlobalThisExceptions]
* @property {boolean} [writeAutoPolicy] Automatically write a policy file
* @property {boolean} [writeAutoPolicyDebug] Automatically write a debug policy
* file
* @property {boolean} [writeAutoPolicyAndRun] Automatically write a policy file
* and run the application
* @property {string} [policyPath] Path to policy file
* @property {string} [policyDebugPath] Path to policy debug file
* @property {string} [policyOverridePath] Path to policy override file
* @property {string} [projectRoot] Path to project root
* @property {boolean} [debugMode] Enable debug mode
* @property {boolean} [statsMode] Enable stats mode
*/
3 changes: 2 additions & 1 deletion packages/core/src/types.ts
@@ -1,5 +1,6 @@
export type * from './generatePolicy'
export * from './index'
export type * from './moduleRecord'
export type * from './options'
export type * from './parseForPolicy'
export type * from './schema'
export type * from './generatePolicy'
@@ -1,8 +1,7 @@
// @ts-check

import type { ExecutionContext } from 'ava'
import type { LavaMoatOpts } from '../../../node/src/defaults'
import type { LavaMoatPolicySchema } from '../../schema'
import { ModuleInitializer } from '../src/moduleRecord'
import type { LavaMoatOpts } from '../src/options'
import { LavaMoatPolicy, LavaMoatPolicyOverrides } from '../src/schema'

export type ScenarioType = 'truthy' | 'falsy' | 'deepEqual'

Expand Down Expand Up @@ -31,11 +30,7 @@ export interface NormalizedScenarioJSFile extends ScenarioFile {

export interface NormalizedBuiltin extends NormalizedScenarioJSFile {
specifier: string
moduleInitializer: (
exports: Record<string, any>,
require: (id: string) => any,
module: Record<string, any>
) => void
moduleInitializer: ModuleInitializer
}
/**
* Scenario file as provided by user
Expand All @@ -54,21 +49,17 @@ export interface ScenarioFile {
type?: ScenarioFileType
entry?: boolean
importMap?: Record<string, string>
moduleInitializer?: (
exports: Record<string, any>,
require: (id: string) => any,
module: Record<string, any>
) => void
moduleInitializer?: ModuleInitializer
}

export type ScenarioSourceFn = () => void

export interface Scenario<Result = unknown> {
name?: string
config?: LavaMoatPolicySchema
configOverride?: LavaMoatPolicySchema
config?: LavaMoatPolicy
configOverride?: LavaMoatPolicyOverrides
expectedFailure?: boolean
expectedResult?: any
expectedResult?: Result
defineEntry?: ScenarioSourceFn
defineOne?: ScenarioSourceFn
defineTwo?: ScenarioSourceFn
Expand All @@ -77,14 +68,14 @@ export interface Scenario<Result = unknown> {
expectedFailureMessageRegex?: RegExp
files?: Record<string, ScenarioFile>
defaultPolicy?: boolean
builtin?: Record<string, any>
context?: Record<string, any>
builtin?: Record<string, unknown>
context?: Record<string, unknown>
opts?: LavaMoatOpts
dir?: string
checkPostRun?: ScenarioCheckPostRunFn<Result>
checkError?: ScenarioCheckErrorFn<Result>
checkResult?: ScenarioCheckResultFn<Result>
kernelArgs?: Record<string, any>
kernelArgs?: Record<string, unknown>
beforeCreateKernel?: (scenario: NormalizedScenario<Result>) => void
}

Expand All @@ -109,8 +100,8 @@ export type NormalizedScenario<Result = unknown> = Required<
> &
Pick<Scenario<Result>, 'dir' | 'kernelArgs' | 'beforeCreateKernel'> & {
entries: string[]
globalThis?: Record<string, any>
vmContext?: Record<string, any>
globalThis?: Record<string, unknown>
vmContext?: Record<string, unknown>
}

export type ScenarioCheckPostRunFn<Result = unknown> = (
Expand Down
8 changes: 5 additions & 3 deletions packages/core/test/tsconfig.json
@@ -1,10 +1,12 @@
{
"extends": "../../../.config/tsconfig.test.json",
// not extending the test config b/c we must output some types
"extends": "../../../.config/tsconfig.src.json",
"compilerOptions": {
"checkJs": false,
"rootDir": "."
"rootDir": "..",
"outDir": "../types"
},
"include": ["**/*"],
"include": ["**/*.js", "**/*.ts"],
"references": [
{
"path": "../src/tsconfig.json"
Expand Down
57 changes: 28 additions & 29 deletions packages/core/test/util.js
Expand Up @@ -30,7 +30,7 @@ module.exports = {

/**
* @typedef {Partial<import('../src/parseForPolicy').ParseForPolicyOpts> & {
* files: import('./scenarios/scenario').NormalizedScenarioJSFile[]
* files: import('./scenario').NormalizedScenarioJSFile[]
* }} GeneratePolicyFromFilesOpts
*/

Expand All @@ -41,17 +41,17 @@ module.exports = {
async function generatePolicyFromFiles({ files, ...opts }) {
const config = await parseForPolicy({
moduleSpecifier:
/** @type {import('./scenarios/scenario').NormalizedScenarioJSFile} */ (
/** @type {import('./scenario').NormalizedScenarioJSFile} */ (
files.find((file) => file.entry)
).specifier,
resolveHook: (requestedName, parentAddress) => {
return /** @type {import('./scenarios/scenario').NormalizedScenarioJSFile} */ (
return /** @type {import('./scenario').NormalizedScenarioJSFile} */ (
files.find((file) => file.specifier === parentAddress)
).importMap[requestedName]
},
importHook: async (address) => {
return new LavamoatModuleRecord(
/** @type {import('./scenarios/scenario').NormalizedBuiltin} */ (
/** @type {import('./scenario').NormalizedBuiltin} */ (
files.find((file) => file.specifier === address)
)
)
Expand All @@ -69,8 +69,8 @@ async function generatePolicyFromFiles({ files, ...opts }) {
* running.
*
* @template [Result=unknown] Default is `unknown`
* @param {import('./scenarios/scenario').Scenario<Result>} scenario
* @returns {import('./scenarios/scenario').NormalizedScenario<Result>}
* @param {import('./scenario').Scenario} scenario
* @returns {import('./scenario').NormalizedScenario<{ value: string }>}
*/
function createScenarioFromScaffold({
name = 'template scenario',
Expand All @@ -80,12 +80,12 @@ function createScenarioFromScaffold({
testType = 'deepEqual',
checkPostRun = async (t, result, err, scenario) => {
if (err) {
await /** @type {import('./scenarios/scenario').ScenarioCheckErrorFn<Result>} */ (
await /** @type {import('./scenario').ScenarioCheckErrorFn<Result>} */ (
scenario.checkError
)(t, err, scenario)
} else {
// assumes `result` is not undefined
await /** @type {import('./scenarios/scenario').ScenarioCheckResultFn<Result>} */ (
await /** @type {import('./scenario').ScenarioCheckResultFn<Result>} */ (
scenario.checkResult
)(t, /** @type {Result} */ (result), scenario)
}
Expand Down Expand Up @@ -298,8 +298,11 @@ function createScenarioFromScaffold({
expectedFailureMessageRegex,
entries: ['entry.js'],
files: _files,
config: _config,
configOverride: _configOverride,
config: /** @type {import('../src/schema').LavaMoatPolicy} */ (_config),
configOverride:
/** @type {import('../src/schema').LavaMoatPolicyOverrides} */ (
_configOverride
),
context,
opts,
dir,
Expand Down Expand Up @@ -342,7 +345,7 @@ function createHookedConsole() {
*
* @template [Result=unknown] Default is `unknown`
* @typedef PlatformRunScenarioOpts
* @property {import('./scenarios/scenario').NormalizedScenario<Result>} scenario
* @property {import('./scenario').NormalizedScenario<Result>} scenario
* @property {boolean} [runWithPrecompiledModules]
* @property {(...args: any[]) => void} [log]
*/
Expand Down Expand Up @@ -446,9 +449,8 @@ async function runScenario({ scenario, runWithPrecompiledModules = false }) {
*/
getRelativeModuleId: (id, relative) => {
return (
/** @type {import('./scenarios/scenario').NormalizedScenarioJSFile} */ (
files[id]
).importMap[relative] || relative
/** @type {import('./scenario').NormalizedScenarioJSFile} */ (files[id])
.importMap[relative] || relative
)
},
prepareModuleInitializerArgs,
Expand Down Expand Up @@ -479,8 +481,8 @@ async function runScenario({ scenario, runWithPrecompiledModules = false }) {
* @param {Object} options - The options for preparing the scenario.
* @param {FsPromiseApi} [options.fs] - The file system module to use (default:
* `node:fs/promises`).
* @param {import('./scenarios/scenario').Scenario} options.scenario - The
* scenario object containing the files to write.
* @param {import('./scenario').Scenario} options.scenario - The scenario object
* containing the files to write.
* @param {string} [options.policyName='policies'] - The name of the policy
* directory (default: 'policies'). Default is `'policies'`
* @param {string} [options.projectDir] - The project directory path.
Expand Down Expand Up @@ -522,9 +524,7 @@ async function prepareScenarioOnDisk({
filesToWrite.map(async (file) => {
const fullPath = path.join(
/** @type {string} */ (projectDir),
/** @type {import('./scenarios/scenario').NormalizedScenarioJSFile} */ (
file
).file
/** @type {import('./scenario').NormalizedScenarioJSFile} */ (file).file
)
const dirname = path.dirname(fullPath)
await fs.mkdir(dirname, { recursive: true })
Expand All @@ -538,7 +538,7 @@ async function prepareScenarioOnDisk({
}

/**
* @param {Record<string, import('./scenarios/scenario').ScenarioFile>} files
* @param {Record<string, import('./scenario').ScenarioFile>} files
* @returns
*/
function fillInFileDetails(files) {
Expand Down Expand Up @@ -566,7 +566,7 @@ function moduleDataForBuiltin(builtinObj, name) {
file: name,
package: name,
type: 'builtin',
/** @type {import('./scenarios/scenario').ScenarioFile['moduleInitializer']} */
/** @type {import('./scenario').ScenarioFile['moduleInitializer']} */
moduleInitializer: (_, _2, module) => {
module.exports = builtinObj[name]
},
Expand Down Expand Up @@ -647,7 +647,7 @@ function evaluateWithSourceUrl(filename, content, context) {
* @returns {Promise<import('../src/schema').LavaMoatPolicy>}
*/
async function createConfigForTest(testFn, opts = {}) {
/** @type {import('./scenarios/scenario').NormalizedScenarioJSFile[]} */
/** @type {import('./scenario').NormalizedScenarioJSFile[]} */
const files = [
{
type: 'js',
Expand Down Expand Up @@ -676,21 +676,20 @@ async function createConfigForTest(testFn, opts = {}) {

/**
* @param {object} opts
* @param {import('./scenarios/scenario').Scenario} opts.scenario
* @param {import('./scenario').Scenario} opts.scenario
* @param {Partial<GeneratePolicyFromFilesOpts>} [opts.opts]
*/
async function autoConfigForScenario({ scenario, opts = {} }) {
const files =
/** @type {import('./scenarios/scenario').NormalizedScenarioJSFile[]} */ (
Object.values(scenario.files ?? {})
)
const files = /** @type {import('./scenario').NormalizedScenarioJSFile[]} */ (
Object.values(scenario.files ?? {})
)
const policy = await generatePolicyFromFiles({ files, ...opts })
scenario.config = policy
}

/**
* @param {object} opts
* @param {import('./scenarios/scenario').NormalizedScenario} opts.scenario
* @param {import('./scenario').NormalizedScenario} opts.scenario
*/
function convertOptsToArgs({ scenario }) {
const { entries } = scenario
Expand All @@ -712,7 +711,7 @@ function functionToString(func) {
/**
* @template [Result=unknown] Default is `unknown`
* @param {import('ava').ExecutionContext} t
* @param {import('./scenarios/scenario').NormalizedScenario<Result>} scenario
* @param {import('./scenario').NormalizedScenario<Result>} scenario
* @param {PlatformRunScenario<Result>} platformRunScenario
* @returns {Promise<Result | undefined>}
*/
Expand Down
27 changes: 1 addition & 26 deletions packages/node/src/defaults.js
Expand Up @@ -2,32 +2,7 @@ const { getDefaultPaths } = require('lavamoat-core')

const defaultPaths = getDefaultPaths('node')

/**
* @typedef LavaMoatScuttleOpts
* @property {boolean} [enabled]
* @property {string[]} [exceptions]
* @property {string} [scuttlerName]
*/

/**
* @typedef LavaMoatOpts
* @property {LavaMoatScuttleOpts} [scuttleGlobalThis] Enable or disable
* scuttling of `globalThis`
* @property {string[]} [scuttleGlobalThisExceptions]
* @property {boolean} [writeAutoPolicy] Automatically write a policy file
* @property {boolean} [writeAutoPolicyDebug] Automatically write a debug policy
* file
* @property {boolean} [writeAutoPolicyAndRun] Automatically write a policy file
* and run the application
* @property {string} [policyPath] Path to policy file
* @property {string} [policyDebugPath] Path to policy debug file
* @property {string} [policyOverridePath] Path to policy override file
* @property {string} [projectRoot] Path to project root
* @property {boolean} [debugMode] Enable debug mode
* @property {boolean} [statsMode] Enable stats mode
*/

/** @type {LavaMoatOpts} */
/** @type {import('lavamoat-core').LavaMoatOpts} */
const defaults = {
scuttleGlobalThis: {},
scuttleGlobalThisExceptions: [],
Expand Down