From c42d0800a5b0fb539c42f8cab1a3753af7dd7216 Mon Sep 17 00:00:00 2001 From: Saleh Abdel Motaal Date: Wed, 21 Aug 2024 17:22:02 -0400 Subject: [PATCH 1/6] feat(test-utils): Add `endoified` helper using `jsdom` in `vitest` --- packages/extension/src/endoify.test.ts | 51 ++++++++++++++++++++++++ packages/extension/vite.config.ts | 3 -- packages/shims/scripts/bundle.js | 54 ++++++++++++++++---------- packages/shims/src/apply-lockdown.mjs | 11 ------ packages/shims/src/endoify.mjs | 4 +- packages/test-utils/package.json | 2 + packages/test-utils/src/endoified.ts | 51 ++++++++++++++++++++++++ yarn.lock | 1 + 8 files changed, 141 insertions(+), 36 deletions(-) create mode 100644 packages/extension/src/endoify.test.ts delete mode 100644 packages/shims/src/apply-lockdown.mjs create mode 100644 packages/test-utils/src/endoified.ts diff --git a/packages/extension/src/endoify.test.ts b/packages/extension/src/endoify.test.ts new file mode 100644 index 000000000..757481ff0 --- /dev/null +++ b/packages/extension/src/endoify.test.ts @@ -0,0 +1,51 @@ +/* eslint-disable @typescript-eslint/explicit-function-return-type */ +import endoified from '@ocap/test-utils/endoified'; +import { describe } from 'vitest'; +import * as vitest from 'vitest'; + +// eslint-disable-next-line spaced-comment +/// + +/* eslint-disable vitest/valid-describe-callback */ +describe( + `endoify`, + endoified( + // eslint-disable-next-line @typescript-eslint/no-shadow + ({ it, expect }) => { + for (const assertion of [ + () => typeof globalThis === 'object', + () => typeof lockdown === 'function', + () => typeof repairIntrinsics === 'function', + () => typeof Compartment === 'function', + () => typeof assert === 'function', + () => typeof HandledPromise === 'function', + () => typeof harden === 'function', + () => typeof getStackString === 'function', + () => { + try { + return !Object.assign(harden({ a: 1 }), { b: 2 }); + } catch { + return true; + } + }, + ]) { + it(`asserts ${String(assertion)}`, () => { + console.log(`${String(assertion)} => ${assertion()}`); + expect(assertion(), `${String(assertion)}`).toBe(true); + }); + } + }, + { ...vitest }, + new URL( + '../dist/endoify.mjs', + import.meta.url.replace(/^https?:\/\/.*?\/@fs\//u, 'file:///'), + ), + ), +); + +declare global { + // eslint-disable-next-line no-var + var getStackString: (error: Error) => string; + // eslint-disable-next-line no-var, @typescript-eslint/consistent-type-imports + var HandledPromise: import('@endo/eventual-send').HandledPromiseConstructor; +} diff --git a/packages/extension/vite.config.ts b/packages/extension/vite.config.ts index 7d5e5d364..eccec9946 100644 --- a/packages/extension/vite.config.ts +++ b/packages/extension/vite.config.ts @@ -29,9 +29,6 @@ const staticCopyTargets: readonly string[] = [ // External modules 'dev-console.js', '../../shims/dist/endoify.mjs', - // Dependencies of external modules - '../../shims/dist/eventual-send.mjs', - '../../../node_modules/ses/dist/ses.mjs', ]; // https://vitejs.dev/config/ diff --git a/packages/shims/scripts/bundle.js b/packages/shims/scripts/bundle.js index 45157d98d..76e1d4cd4 100644 --- a/packages/shims/scripts/bundle.js +++ b/packages/shims/scripts/bundle.js @@ -1,42 +1,56 @@ +// eslint-disable-next-line import-x/no-unassigned-import import 'ses'; +// eslint-disable-next-line import-x/no-unassigned-import, import-x/extensions import '@endo/lockdown/commit.js'; import bundleSource from '@endo/bundle-source'; -import { copyFile, mkdir, writeFile } from 'fs/promises'; +import { mkdir, writeFile } from 'fs/promises'; import path from 'path'; import { rimraf } from 'rimraf'; import { fileURLToPath } from 'url'; console.log('Bundling shims...'); -const rootDir = path.resolve(import.meta.dirname, '..'); +const rootDir = fileURLToPath(new URL('..', import.meta.url)); const src = path.resolve(rootDir, 'src'); const dist = path.resolve(rootDir, 'dist'); + +await mkdir(dist, { recursive: true }); +await rimraf(`${dist}/*`, { glob: true }); + const fileNames = { endoify: 'endoify.mjs', - eventualSend: 'eventual-send.mjs', - applyLockdown: 'apply-lockdown.mjs', }; -await mkdir(dist, { recursive: true }); -await rimraf(`${dist}/*`); +const { source: endoifyBundleSource } = await bundleSource( + path.resolve(src, fileNames.endoify), + { format: 'endoScript' }, +); -for (const fileName of [fileNames.endoify, fileNames.applyLockdown]) { - await copyFile(path.resolve(src, fileName), path.resolve(dist, fileName)); -} +await writeFile(path.resolve(dist, fileNames.endoify), endoifyBundleSource); -const eventualSendSourcePath = fileURLToPath( - import.meta.resolve('@endo/eventual-send/shim.js'), -); +// const fileNames = { +// endoify: 'endoify.mjs', +// eventualSend: 'eventual-send.mjs', +// applyLockdown: 'apply-lockdown.mjs', +// }; -const { source: eventualSendBundleSource } = await bundleSource( - eventualSendSourcePath, - { format: 'endoScript' }, -); +// for (const fileName of [fileNames.endoify, fileNames.applyLockdown]) { +// await copyFile(path.resolve(src, fileName), path.resolve(dist, fileName)); +// } -await writeFile( - path.resolve(dist, fileNames.eventualSend), - eventualSendBundleSource, -); +// const eventualSendSourcePath = fileURLToPath( +// import.meta.resolve('@endo/eventual-send/shim.js'), +// ); + +// const { source: eventualSendBundleSource } = await bundleSource( +// eventualSendSourcePath, +// { format: 'endoScript' }, +// ); + +// await writeFile( +// path.resolve(dist, fileNames.eventualSend), +// eventualSendBundleSource, +// ); console.log('Success!'); diff --git a/packages/shims/src/apply-lockdown.mjs b/packages/shims/src/apply-lockdown.mjs deleted file mode 100644 index e01804d3a..000000000 --- a/packages/shims/src/apply-lockdown.mjs +++ /dev/null @@ -1,11 +0,0 @@ -import './ses.mjs'; - -lockdown({ - consoleTaming: 'unsafe', - dateTaming: 'unsafe', - domainTaming: 'unsafe', - errorTaming: 'unsafe', - mathTaming: 'unsafe', - overrideTaming: 'severe', - stackFiltering: 'verbose', -}); diff --git a/packages/shims/src/endoify.mjs b/packages/shims/src/endoify.mjs index 824aa9c32..2208eeac8 100644 --- a/packages/shims/src/endoify.mjs +++ b/packages/shims/src/endoify.mjs @@ -1,5 +1,5 @@ -import './ses.mjs'; -import './eventual-send.mjs'; +import 'ses'; +import '@endo/eventual-send/shim.js'; lockdown({ consoleTaming: 'unsafe', diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index c239be901..c577ca1f2 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -10,6 +10,7 @@ "type": "module", "exports": { ".": "./src/index.ts", + "./endoified": "./src/endoified.ts", "./package.json": "./package.json" }, "scripts": { @@ -17,6 +18,7 @@ "test": "echo 'No tests.' && exit 0" }, "devDependencies": { + "jsdom": "^24.1.1", "rimraf": "^6.0.1", "ses": "^1.7.0", "typescript": "~4.9.5", diff --git a/packages/test-utils/src/endoified.ts b/packages/test-utils/src/endoified.ts new file mode 100644 index 000000000..2e6d205b9 --- /dev/null +++ b/packages/test-utils/src/endoified.ts @@ -0,0 +1,51 @@ +/* eslint-disable n/no-sync */ +import jsdom from 'jsdom'; +import { readFileSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const endoifyBundleSourceTextURL = new URL( + '../../shims/dist/endoify.mjs', + import.meta.url.replace(/^https?:\/\/.*?\/@fs\//u, 'file:///'), +); + +/** + * Run an endoified test with endowments. + * + * @param test - The test to run. It must be a synchronous function that takes `endowments` as its only parameter. + * @param endowments - Endowments passed to the test. They must include `vitest` capabilities like `it` and `expect`. + * @param endoifySourceLocation - Optional path to the endoify bundle. It must either be absolute or relative to `process.cwd()`. + * @returns The return value of the test. + */ +export default function endoified< + // eslint-disable-next-line @typescript-eslint/consistent-type-imports + Endowments extends Partial, +>( + test: (endowments: Endowments) => void, + endowments: Endowments, + endoifySourceLocation?: string | URL, +): () => void { + return () => { + const relativeEndoifySourcePath = path.relative( + '.', + // eslint-disable-next-line no-nested-ternary + endoifySourceLocation && typeof endoifySourceLocation === 'string' + ? endoifySourceLocation + : endoifySourceLocation && endoifySourceLocation instanceof URL + ? fileURLToPath(endoifySourceLocation.href) + : fileURLToPath(endoifyBundleSourceTextURL.href), + ); + + console.log({ relativeEndoifySourcePath }); + + const evaluatorSourceText = String(test); + const endoifySourceText = readFileSync(relativeEndoifySourcePath, 'utf-8'); + + console.log({ endoifySourceText }); + + const dom = new jsdom.JSDOM(``, { runScripts: 'outside-only' }); + + dom.window.eval(endoifySourceText); + (dom.window.eval(evaluatorSourceText) as typeof test)(endowments); + }; +} diff --git a/yarn.lock b/yarn.lock index d5df286f8..9a9bbcf84 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1357,6 +1357,7 @@ __metadata: version: 0.0.0-use.local resolution: "@ocap/test-utils@workspace:packages/test-utils" dependencies: + jsdom: "npm:^24.1.1" rimraf: "npm:^6.0.1" ses: "npm:^1.7.0" typescript: "npm:~4.9.5" From 0767f261928eb631711f40c86f7f1f0db45983bc Mon Sep 17 00:00:00 2001 From: Saleh Abdel Motaal Date: Wed, 21 Aug 2024 17:49:49 -0400 Subject: [PATCH 2/6] chore: Add `build` step before running `test` in `lint-build-test` --- .github/workflows/lint-build-test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/lint-build-test.yml b/.github/workflows/lint-build-test.yml index 319f02df1..bc15abab1 100644 --- a/.github/workflows/lint-build-test.yml +++ b/.github/workflows/lint-build-test.yml @@ -144,6 +144,7 @@ jobs: node-version: ${{ matrix.node-version }} cache: yarn - run: yarn --immutable + - run: yarn workspace ${{ matrix.package-name }} run build - run: yarn workspace ${{ matrix.package-name }} run test - name: Require clean working directory shell: bash From 75a7ef3fd9624cfe39d498be3aa36afd43dd03a8 Mon Sep 17 00:00:00 2001 From: Saleh Abdel Motaal Date: Wed, 21 Aug 2024 17:55:12 -0400 Subject: [PATCH 3/6] chore(test-utils): Add `build` script to allow the CI to pass --- packages/test-utils/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index c577ca1f2..571bbfd53 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -14,6 +14,7 @@ "./package.json": "./package.json" }, "scripts": { + "build": "exit 0", "clean": "rimraf --glob ./dist './*.tsbuildinfo'", "test": "echo 'No tests.' && exit 0" }, From 1e3fcf76b82614f980d92de99864c86940eb2544 Mon Sep 17 00:00:00 2001 From: Saleh Abdel Motaal Date: Wed, 21 Aug 2024 18:06:22 -0400 Subject: [PATCH 4/6] Revert "chore(test-utils): Add `build` script to allow the CI to pass" This reverts commit 75a7ef3fd9624cfe39d498be3aa36afd43dd03a8. --- packages/test-utils/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 571bbfd53..c577ca1f2 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -14,7 +14,6 @@ "./package.json": "./package.json" }, "scripts": { - "build": "exit 0", "clean": "rimraf --glob ./dist './*.tsbuildinfo'", "test": "echo 'No tests.' && exit 0" }, From f6755a32e0ffe3734d958c4c0345ff25d1039668 Mon Sep 17 00:00:00 2001 From: Saleh Abdel Motaal Date: Wed, 21 Aug 2024 18:06:25 -0400 Subject: [PATCH 5/6] Revert "chore: Add `build` step before running `test` in `lint-build-test`" This reverts commit 0767f261928eb631711f40c86f7f1f0db45983bc. --- .github/workflows/lint-build-test.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/lint-build-test.yml b/.github/workflows/lint-build-test.yml index bc15abab1..319f02df1 100644 --- a/.github/workflows/lint-build-test.yml +++ b/.github/workflows/lint-build-test.yml @@ -144,7 +144,6 @@ jobs: node-version: ${{ matrix.node-version }} cache: yarn - run: yarn --immutable - - run: yarn workspace ${{ matrix.package-name }} run build - run: yarn workspace ${{ matrix.package-name }} run test - name: Require clean working directory shell: bash From fb76ab7618d7d03ff91aa1b5f44dbdf4e52841eb Mon Sep 17 00:00:00 2001 From: Saleh Abdel Motaal Date: Wed, 21 Aug 2024 18:46:18 -0400 Subject: [PATCH 6/6] fix(test-utils): Let `endoified` run `shims/scripts/bundle.js` as needed --- packages/extension/src/endoify.test.ts | 7 ----- packages/test-utils/src/endoified.ts | 39 ++++++++++++++++++++++---- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/packages/extension/src/endoify.test.ts b/packages/extension/src/endoify.test.ts index 757481ff0..8b8c02a43 100644 --- a/packages/extension/src/endoify.test.ts +++ b/packages/extension/src/endoify.test.ts @@ -3,9 +3,6 @@ import endoified from '@ocap/test-utils/endoified'; import { describe } from 'vitest'; import * as vitest from 'vitest'; -// eslint-disable-next-line spaced-comment -/// - /* eslint-disable vitest/valid-describe-callback */ describe( `endoify`, @@ -36,10 +33,6 @@ describe( } }, { ...vitest }, - new URL( - '../dist/endoify.mjs', - import.meta.url.replace(/^https?:\/\/.*?\/@fs\//u, 'file:///'), - ), ), ); diff --git a/packages/test-utils/src/endoified.ts b/packages/test-utils/src/endoified.ts index 2e6d205b9..a3cbb64d7 100644 --- a/packages/test-utils/src/endoified.ts +++ b/packages/test-utils/src/endoified.ts @@ -1,6 +1,7 @@ /* eslint-disable n/no-sync */ import jsdom from 'jsdom'; -import { readFileSync } from 'node:fs'; +import { spawnSync } from 'node:child_process'; +import { readFileSync, existsSync } from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; @@ -8,6 +9,9 @@ const endoifyBundleSourceTextURL = new URL( '../../shims/dist/endoify.mjs', import.meta.url.replace(/^https?:\/\/.*?\/@fs\//u, 'file:///'), ); +const endoifyShimsBundleSourceLocation = fileURLToPath( + endoifyBundleSourceTextURL.href, +); /** * Run an endoified test with endowments. @@ -26,22 +30,45 @@ export default function endoified< endoifySourceLocation?: string | URL, ): () => void { return () => { - const relativeEndoifySourcePath = path.relative( - '.', + // console.log({ endoifySourceLocation: `${endoifySourceLocation}` }); + + const absoluteEndoifySourcePath = // eslint-disable-next-line no-nested-ternary endoifySourceLocation && typeof endoifySourceLocation === 'string' ? endoifySourceLocation : endoifySourceLocation && endoifySourceLocation instanceof URL ? fileURLToPath(endoifySourceLocation.href) - : fileURLToPath(endoifyBundleSourceTextURL.href), + : endoifyShimsBundleSourceLocation; + + const relativeEndoifySourcePath = path.relative( + '.', + absoluteEndoifySourcePath, ); - console.log({ relativeEndoifySourcePath }); + if ( + absoluteEndoifySourcePath === endoifyShimsBundleSourceLocation && + !existsSync(endoifyShimsBundleSourceLocation) + ) { + spawnSync( + 'node', + [ + fileURLToPath( + new URL( + '../../shims/scripts/bundle.js', + import.meta.url.replace(/^https?:\/\/.*?\/@fs\//u, 'file:///'), + ).href, + ), + ], + { stdio: 'inherit' }, + ); + } + + // console.log({ relativeEndoifySourcePath }); const evaluatorSourceText = String(test); const endoifySourceText = readFileSync(relativeEndoifySourcePath, 'utf-8'); - console.log({ endoifySourceText }); + // console.log({ endoifySourceText }); const dom = new jsdom.JSDOM(``, { runScripts: 'outside-only' });