diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 229ba5dc..a7473815 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,6 +27,7 @@ jobs: androidapp: ${{ steps.filter.outputs.androidapp }} appleapp: ${{ steps.filter.outputs.appleapp }} gradle-plugins: ${{ steps.filter.outputs.gradle-plugins }} + scripts: ${{ steps.filter.outputs.scripts }} ci: ${{ steps.filter.outputs.ci }} steps: - name: Checkout @@ -56,6 +57,8 @@ jobs: - 'apps/brownfield-example-shared-tests/**' gradle-plugins: - 'gradle-plugins/**' + scripts: + - 'scripts/**' ci: - '.github/**' @@ -81,7 +84,10 @@ jobs: name: Build, lint, typecheck & Jest runs-on: ubuntu-latest needs: filter - if: needs.filter.outputs.packages == 'true' || needs.filter.outputs.ci == 'true' + if: | + needs.filter.outputs.packages == 'true' || + needs.filter.outputs.scripts == 'true' || + needs.filter.outputs.ci == 'true' steps: - name: Checkout uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 @@ -104,6 +110,9 @@ jobs: - name: Run packages' tests (Jest & vitest) run: yarn test:packages + - name: Run repo script tests (node:test) + run: yarn test:scripts + - name: Test Brownfield CLI (version) run: | yarn workspace @callstack/react-native-brownfield brownfield --version diff --git a/package.json b/package.json index 4dc635da..a88afff0 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "build": "turbo run build", "test:packages": "turbo run test --filter='./packages/*'", "test:apps": "turbo run test --filter='./apps/*'", + "test:scripts": "node --experimental-strip-types --no-warnings --test 'scripts/__tests__/**/*.test.ts'", "dev": "yarn workspaces foreach -Api run dev", "ci:version": "changeset version && yarn install --no-immutable && node --experimental-strip-types --no-warnings ./scripts/consolidate-changelog.ts", "ci:publish": "changeset publish", diff --git a/scripts/__tests__/check-expo-preview.test.ts b/scripts/__tests__/check-expo-preview.test.ts index b45e78f0..0911c7e7 100644 --- a/scripts/__tests__/check-expo-preview.test.ts +++ b/scripts/__tests__/check-expo-preview.test.ts @@ -1,5 +1,8 @@ import assert from 'node:assert/strict'; +import { spawnSync } from 'node:child_process'; +import path from 'node:path'; import test from 'node:test'; +import { fileURLToPath } from 'node:url'; import { ensureConsumerNavigationSpec, @@ -136,3 +139,28 @@ test('fails loudly when expo dependency is missing', () => { /Could not locate dependencies\.expo in ExpoAppPreview\/package\.json/, }); }); + +const repoRoot = path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + '../..' +); + +test('importing the module does not run the CLI or reach the network', () => { + // main() used to be invoked unconditionally at module scope, so merely + // importing this file fetched registry.npmjs.org — and an unhandled + // rejection from that fetch failed the whole suite. Importing must be + // a pure, offline operation. + const result = spawnSync( + process.execPath, + [ + '--experimental-strip-types', + '--no-warnings', + '--eval', + "import('./scripts/check-expo-preview.ts').then(() => console.log('IMPORT_ONLY'));", + ], + { cwd: repoRoot, encoding: 'utf8' } + ); + + assert.equal(result.status, 0, result.stderr); + assert.equal(result.stdout.trim(), 'IMPORT_ONLY'); +}); diff --git a/scripts/check-expo-preview.ts b/scripts/check-expo-preview.ts index 49fe4b1a..9b88b877 100644 --- a/scripts/check-expo-preview.ts +++ b/scripts/check-expo-preview.ts @@ -8,7 +8,7 @@ import { readFileSync, writeFileSync, } from 'node:fs'; -import { fileURLToPath } from 'node:url'; +import { fileURLToPath, pathToFileURL } from 'node:url'; interface CliOptions { expoVersion?: string; @@ -198,7 +198,7 @@ export function ensureConsumerNavigationSpec(contents: string): string { } const updated = contents.replace( - /\n}\s*$/u, + /\n\}\s*$/u, `${CONSUMER_ROAD_TEST_NAVIGATION_METHODS}\n}` ); @@ -395,4 +395,16 @@ async function main(): Promise { console.log(`Applied: ${applied}`); } -main(); +const isDirectInvocation = + process.argv[1] !== undefined && + import.meta.url === pathToFileURL(process.argv[1]).href; + +// Importing this module (e.g. from the test suite) must not run the CLI — +// main() fetches the npm registry, and an unhandled rejection from that +// fetch would fail the importer for reasons unrelated to it. +if (isDirectInvocation) { + main().catch((error: unknown) => { + console.error(error); + process.exitCode = 1; + }); +}