Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
a9647b8
Move GitHub Actions and scripts tests into tests/tooling/
roryabraham Aug 12, 2026
24a4f74
Wire tests/tooling/ into Bun, Jest, TypeScript, ESLint, knip and CI
roryabraham Aug 12, 2026
f6be790
Port the .github tooling tests from Jest to bun:test
roryabraham Aug 12, 2026
b4a8a56
Export markPullRequestsAsDeployed's action with ESM
roryabraham Aug 12, 2026
335f5fa
Port the remaining scripts and libs tests to bun:test
roryabraham Aug 12, 2026
2e81c1f
Satisfy typecheck, ESLint and knip for tests/tooling
roryabraham Aug 12, 2026
7015ca2
Silence console output in the tooling tests
roryabraham Aug 12, 2026
66e751c
Address review feedback on the tooling test migration
roryabraham Aug 12, 2026
ad37453
Gate main on the Bun tests, and document tests/tooling
roryabraham Aug 12, 2026
7f72bc9
Qualify the Bun tests concurrency group by SHA on main
roryabraham Aug 12, 2026
843852e
Move the last four @actions-importing tests to bun:test
roryabraham Aug 12, 2026
95da8ae
Merge remote-tracking branch 'origin/main' into rory/bun-test-migrati…
roryabraham Aug 12, 2026
6c0e0bd
Keep generateTranslationsTest on Jest; narrow ChatGPTTranslator's imp…
roryabraham Aug 12, 2026
77c0326
Merge remote-tracking branch 'origin/main' into rory/bun-test-migrati…
roryabraham Aug 12, 2026
9229a8f
Fix CI: stale action bundle, undeclared dep, two unknown words
roryabraham Aug 12, 2026
32eab02
Merge origin/main into rory/bun-test-migration-gh-scripts
roryabraham Aug 12, 2026
95ea535
Migrate generateTranslationsTest to bun:test
roryabraham Aug 12, 2026
9421060
Fold the tooling tests into test:bun instead of a second script
roryabraham Aug 12, 2026
ba0fcf9
Give CIGitLogic a throw-away sandbox and run test files in parallel
roryabraham Aug 12, 2026
5ce6c4c
Run CIGitLogic's git commands through the Bun shell
roryabraham Aug 12, 2026
b278188
Wrap Bun's shell in a Proxy instead of an exec() helper
roryabraham Aug 12, 2026
e3740d8
Drop the TEST_VERBOSE escape hatch
roryabraham Aug 12, 2026
fc8405b
Keep commit message prose out of the shell templates
roryabraham Aug 12, 2026
9283dcf
Merge remote-tracking branch 'origin/main' into rory/bun-test-migrati…
roryabraham Aug 12, 2026
f56d1ee
Merge remote-tracking branch 'origin/main' into rory/bun-test-migrati…
roryabraham Aug 12, 2026
1ec3196
Merge origin/main into rory/bun-test-migration-gh-scripts
roryabraham Aug 13, 2026
32f931d
Type the deploy checklist assertions instead of casting to unknown
roryabraham Aug 13, 2026
f4184d1
Run ESLint on the 64GB runner
roryabraham Aug 13, 2026
040be27
Trigger the lint workflow on changes to itself
roryabraham Aug 13, 2026
c8b0180
Merge origin/main into rory/bun-test-migration-gh-scripts
roryabraham Aug 13, 2026
d9750f6
Fix the unsafe octokit assertions the rename un-grandfathered
roryabraham Aug 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12707,7 +12707,7 @@ function wrappy (fn, cb) {
/***/ }),

/***/ 2483:
/***/ (function(module, exports, __nccwpck_require__) {
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {

"use strict";

Expand Down Expand Up @@ -12748,7 +12748,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
/* eslint-disable @typescript-eslint/naming-convention, import/no-import-module-exports */
/* eslint-disable @typescript-eslint/naming-convention */
const ActionUtils = __importStar(__nccwpck_require__(6981));
const CONST_1 = __importDefault(__nccwpck_require__(9873));
const GithubUtils_1 = __importDefault(__nccwpck_require__(9296));
Expand Down Expand Up @@ -12937,7 +12937,7 @@ async function run() {
if (require.main === require.cache[eval('__filename')]) {
run();
}
module.exports = run;
exports["default"] = run;


/***/ }),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* eslint-disable @typescript-eslint/naming-convention, import/no-import-module-exports */
/* eslint-disable @typescript-eslint/naming-convention */
import * as ActionUtils from '@github/libs/ActionUtils';
import CONST from '@github/libs/CONST';
import GithubUtils from '@github/libs/GithubUtils';
Expand Down Expand Up @@ -219,4 +219,4 @@ if (require.main === module) {
run();
}

module.exports = run;
export default run;
9 changes: 8 additions & 1 deletion .github/workflows/bunTests.yml
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
name: Bun tests

on:
workflow_call:
pull_request:
types: [opened, synchronize]
branches-ignore: [staging, production]
paths:
- 'server/**'
- 'src/**'
- 'tests/tooling/**'
- 'tests/utils/**'
- '.github/actions/javascript/**'
- '.github/libs/**'
- '.github/scripts/**'
- 'scripts/**'
- 'bunfig.toml'
- '.bun-version'
- 'package.json'
- 'package-lock.json'
- '.github/workflows/bunTests.yml'

concurrency:
group: ${{ github.ref }}-bun-tests
group: ${{ github.ref == 'refs/heads/main' && format('{0}-{1}', github.ref, github.sha) || github.ref }}-bun-tests
cancel-in-progress: true

jobs:
Expand Down
23 changes: 18 additions & 5 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,21 @@ on:
types: [opened, synchronize]
branches-ignore: [staging, production]
paths:
['**.js', '**.ts', '**.tsx', '**.json', '**.mjs', '**.cjs', 'config/.editorconfig', 'config/eslint/**', 'scripts/lint.sh', 'scripts/lintChanged.sh', '.watchmanconfig', '.imgbotconfig']
[
'**.js',
'**.ts',
'**.tsx',
'**.json',
'**.mjs',
'**.cjs',
'config/.editorconfig',
'config/eslint/**',
'scripts/lint.sh',
'scripts/lintChanged.sh',
'.watchmanconfig',
'.imgbotconfig',
'.github/workflows/lint.yml',
]

concurrency:
group: ${{ github.ref == 'refs/heads/main' && format('{0}-{1}', github.ref, github.sha) || github.ref }}-lint
Expand All @@ -16,10 +30,9 @@ jobs:
lint:
name: ESLint check
if: ${{ github.event.head_commit.author.name != 'OSBotify' || github.event_name == 'push' }}
# A cold-cache run lints the whole repo with type-aware rules, which loads the full TypeScript
# program in every worker (~12GB of heap each, regardless of how files are split between workers).
# 2 workers with a 14GB heap cap need ~30GB of memory, so this requires the 32GB (8vcpu) runner.
runs-on: blacksmith-8vcpu-ubuntu-2404
# A cold-cache run loads the full TypeScript program into every worker (~12GB each), plus any extra
# tsconfig project a file is mapped to. 2 workers need more than the 32GB an 8vcpu runner has.
runs-on: blacksmith-16vcpu-ubuntu-2404
steps:
- name: Checkout
uses: useblacksmith/checkout@1c9394c220d293645707b625ba9d79685f093a8f # v1
Expand Down
9 changes: 6 additions & 3 deletions .github/workflows/preDeploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,21 @@ jobs:
uses: ./.github/workflows/test.yml
secrets: inherit

bunTests:
uses: ./.github/workflows/bunTests.yml

confirmPassingBuild:
runs-on: blacksmith-4vcpu-ubuntu-2404
needs: [typecheck, lint, test]
needs: [typecheck, lint, test, bunTests]
if: ${{ always() }}

steps:
- uses: useblacksmith/checkout@1c9394c220d293645707b625ba9d79685f093a8f # v1

- name: Exit failed workflow
if: ${{ needs.typecheck.result == 'failure' || needs.lint.result == 'failure' || needs.test.result == 'failure' }}
if: ${{ needs.typecheck.result == 'failure' || needs.lint.result == 'failure' || needs.test.result == 'failure' || needs.bunTests.result == 'failure' }}
run: |
echo "Checks failed, exiting ~ typecheck: ${{ needs.typecheck.result }}, lint: ${{ needs.lint.result }}, test: ${{ needs.test.result }}"
echo "Checks failed, exiting ~ typecheck: ${{ needs.typecheck.result }}, lint: ${{ needs.lint.result }}, test: ${{ needs.test.result }}, bunTests: ${{ needs.bunTests.result }}"
exit 1

chooseDeployActions:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/typecheck.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:
pull_request:
types: [opened, synchronize]
branches-ignore: [staging, production]
paths: ['**.js', '**.ts', '**.tsx', 'package.json', 'package-lock.json', 'tsconfig.json']
paths: ['**.js', '**.ts', '**.tsx', 'package.json', 'package-lock.json', '**/tsconfig.json']

concurrency:
group: ${{ github.ref == 'refs/heads/main' && format('{0}-{1}', github.ref, github.sha) || github.ref }}-typecheck
Expand Down
6 changes: 3 additions & 3 deletions .oxfmtrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
"ignoreCase": true,
"customGroups": [
{
"groupName": "jest",
"elementNamePattern": ["@jest/globals", "@testing-library/**"]
"groupName": "test-runner",
"elementNamePattern": ["@jest/globals", "@testing-library/**", "bun:test"]
},
{"groupName": "assets", "elementNamePattern": ["@assets/**"]},
{"groupName": "components", "elementNamePattern": ["@components/**"]},
Expand All @@ -28,7 +28,7 @@
{"groupName": "src", "elementNamePattern": ["@src/**"]}
],
"groups": [
"jest",
"test-runner",
"assets",
"components",
"github",
Expand Down
6 changes: 5 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,8 @@ React Compiler auto-memoizes object literals, callbacks, JSX, and derived values

### Testing

- **Unit Tests**: Jest with React Native Testing Library
- **Unit Tests**: Jest with React Native Testing Library. Tests for `.github/` and `scripts/` live in
`tests/tooling/` and run under `bun:test` (`npm run test:bun`) — see `tests/tooling/README.md`.
- **Performance Tests**: Reassure framework

## Special Considerations
Expand Down Expand Up @@ -292,6 +293,9 @@ npm run fmt

# Testing
npm run test

# Bun tests: server/ plus the repo's own tooling (.github/ and scripts/)
npm run test:bun
```

### Platform Builds
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ Often times in order to write a unit test, you may need to mock data, a componen
to help run our Unit tests.

* To run the **Jest unit tests**: `npm run test`
* To run the **Bun tests** — `server/` plus the `.github/` and `scripts/` tooling tests: `npm run test:bun` — see [tests/tooling/README.md](tests/tooling/README.md)
* UI tests guidelines can be found [here](tests/ui/README.md)

## Performance tests
Expand Down
3 changes: 2 additions & 1 deletion bunfig.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
# `import` and `require()`, so scripts don't load the real Flow-typed RN packages at runtime).
preload = ["./scripts/stubReactNative.js"]

# Bun test discovery for server-side tooling (Jest owns src/ and tests/).
# Where a bare `bun test` looks. Both suites are actually run through the `test:bun` npm script, which passes
# their roots explicitly along with the --parallel and --preload flags they need (see tests/tooling/README.md).
[test]
root = "server"
pathIgnorePatterns = ["**/fixtures/**", "**/dist/**", "**/.dev/**", "**/__output__/**", "**/__golden__/**"]
23 changes: 23 additions & 0 deletions config/eslint/eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,29 @@ const config = defineConfig([
},
},

{
// CIGitLogic is excluded from the root tsconfig because it needs @types/bun, so type-aware rules have to
// be pointed at the project that does own it. See tests/tooling/README.md.
files: ['tests/tooling/CIGitLogic.test.ts'],
languageOptions: {
parserOptions: {
project: path.resolve(projectRoot, 'tests/tooling/tsconfig.json'),
projectService: false,
},
},
},

{
files: ['tests/tooling/**/*.ts'],
rules: {
// bun-types declares `expect(...).resolves`/`.rejects` matchers as returning `void` even though Bun's
// own docs recommend (and its runtime requires) awaiting them, so this rule reports every correct use
// of that pattern here. See https://github.com/oven-sh/bun/pull/23425. The cost of turning it off is
// that a *missing* await on `.rejects` also lints clean, so check those by hand in review.
'@typescript-eslint/await-thenable': 'off',
},
},

{
files: ['server/victory-chart-renderer/**/*.ts', 'server/victory-chart-renderer/**/*.tsx'],
languageOptions: {
Expand Down
2 changes: 2 additions & 0 deletions cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"Bronn",
"Buildscript",
"Bunq",
"bunfig",
"Bushwick",
"CARDFROZEN",
"CARDUNFROZEN",
Expand Down Expand Up @@ -250,6 +251,7 @@
"Nonfinancial",
"Nonmortgage",
"Nonnull",
"nothrow",
"Nonstore",
"Nonupholstered",
"Noto",
Expand Down
5 changes: 4 additions & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ module.exports = {
// Prevent Babel from transforming worklets in this file so they are treated as normal functions, otherwise FormatSelectionUtilsTest won't run.
'<rootDir>/node_modules/@expensify/react-native-live-markdown/lib/commonjs/parseExpensiMark.js',
],
testPathIgnorePatterns: ['<rootDir>/node_modules'],
// tests/tooling/ covers .github/ and scripts/ and runs under `bun test` instead (see the `test:bun` npm
// script), so those files import `bun:test` rather than Jest's globals. They aren't in testMatch above, and
// this keeps them out even if a future testMatch entry broadens to all of tests/.
testPathIgnorePatterns: ['<rootDir>/node_modules', '<rootDir>/tests/tooling/'],
// .worktrees/ and .claude/worktrees/ hold parallel git worktrees a developer may check out locally.
// Each one carries its own modules/hybrid-app/package.json, which trips
// jest-haste-map's "duplicate package name" assertion. Skip them entirely.
Expand Down
1 change: 1 addition & 0 deletions knip.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"web/proxy.ts",
"config/rsbuild/**/*.{js,mjs,cjs,ts}",
".github/scripts/**/*.ts",
"tests/tooling/**/*.ts",
".github/actions/javascript/**/*.ts",
".storybook/**/*.{js,ts,tsx}",
"metro.config.js",
Expand Down
21 changes: 3 additions & 18 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 4 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@
"test:verbose": "TZ=utc NODE_OPTIONS=\"--experimental-vm-modules --max_old_space_size=4096\" JEST_VERBOSE=true jest",
"test:debug": "TZ=utc NODE_OPTIONS='--inspect-brk --experimental-vm-modules' jest --runInBand",
"perf-test": "NODE_OPTIONS=--experimental-vm-modules npx reassure",
"typecheck": "NODE_OPTIONS=--max_old_space_size=8192 tsc && NODE_OPTIONS=--max_old_space_size=8192 tsc -p server/tsconfig.json && NODE_OPTIONS=--max_old_space_size=8192 tsc -p server/victory-chart-renderer/tsconfig.json",
"typecheck-tsgo": "tsgo --noEmit --incremental --tsBuildInfoFile tsconfig.tsgo.tsbuildinfo && tsgo --noEmit -p server/tsconfig.json --incremental --tsBuildInfoFile server/tsconfig.tsgo.tsbuildinfo && tsgo --noEmit -p server/victory-chart-renderer/tsconfig.json --incremental --tsBuildInfoFile server/victory-chart-renderer/tsconfig.tsgo.tsbuildinfo",
"typecheck": "NODE_OPTIONS=--max_old_space_size=8192 tsc && NODE_OPTIONS=--max_old_space_size=8192 tsc -p tests/tooling/tsconfig.json && NODE_OPTIONS=--max_old_space_size=8192 tsc -p server/tsconfig.json && NODE_OPTIONS=--max_old_space_size=8192 tsc -p server/victory-chart-renderer/tsconfig.json",
"typecheck-tsgo": "tsgo --noEmit --incremental --tsBuildInfoFile tsconfig.tsgo.tsbuildinfo && tsgo --noEmit -p tests/tooling/tsconfig.json --incremental --tsBuildInfoFile tests/tooling/tsconfig.tsgo.tsbuildinfo && tsgo --noEmit -p server/tsconfig.json --incremental --tsBuildInfoFile server/tsconfig.tsgo.tsbuildinfo && tsgo --noEmit -p server/victory-chart-renderer/tsconfig.json --incremental --tsBuildInfoFile server/victory-chart-renderer/tsconfig.tsgo.tsbuildinfo",
Comment thread
NicolasBonet marked this conversation as resolved.
"lint": "./scripts/lint.sh",
"lint-changed": "./scripts/lintChanged.sh",
"lint-watch": "onchange '**/*.{js,jsx,ts,tsx,mjs,cjs}' -- ./scripts/lint.sh {{changed}}",
Expand Down Expand Up @@ -81,7 +81,7 @@
"compress-svg": "bun scripts/compressSvg.ts --dir assets/images && bun scripts/compressSvg.ts --dir docs/assets/images",
"server:vcr:dev": "npm run dev -w @expensify/victory-chart-renderer --",
"server:vcr:test": "npm run test -w @expensify/victory-chart-renderer --",
"test:bun": "bun test --concurrent --max-concurrency 7",
"test:bun": "TZ=utc bun test --parallel --preload ./scripts/stubReactNative.js --preload ./tests/tooling/setup.ts ./server ./tests/tooling",
"server:vcr:build:linux": "npm run build:linux -w @expensify/victory-chart-renderer --",
"server:vcr:build:linux-arm": "npm run build:linux-arm -w @expensify/victory-chart-renderer --",
"server:vcr:build:macos": "npm run build:macos -w @expensify/victory-chart-renderer --"
Expand Down Expand Up @@ -248,6 +248,7 @@
"@octokit/core": "4.0.4",
"@octokit/plugin-paginate-rest": "3.1.0",
"@octokit/plugin-throttling": "4.1.0",
"@octokit/request-error": "3.0.3",
"@octokit/webhooks-types": "^7.5.1",
"@react-native-community/cli": "20.1.0",
"@react-native-community/cli-platform-android": "20.1.0",
Expand Down Expand Up @@ -279,7 +280,6 @@
"@types/geojson": "^7946.0.16",
"@types/howler": "^2.2.12",
"@types/jest": "^29.5.14",
"@types/jest-when": "^3.5.2",
"@types/js-yaml": "^4.0.5",
"@types/lodash-es": "4.17.12",
"@types/mapbox-gl": "^2.7.13",
Expand Down Expand Up @@ -332,7 +332,6 @@
"jest-environment-jsdom": "^29.7.0",
"jest-expo": "57.0.2",
"jest-transformer-svg": "^2.0.1",
"jest-when": "^3.5.2",
"knip": "^6.14.0",
"lefthook": "2.1.9",
"link": "^2.1.1",
Expand Down
3 changes: 2 additions & 1 deletion server/victory-chart-renderer/tests/log.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Log from '@server/libs/log';
import {afterEach, beforeEach, describe, expect, test} from 'bun:test';

import Log from '@server/libs/log';

import vcrLog from '../src/log';

// VCR_LOG_DESTINATION=stderr forces the fallback path so these tests are deterministic regardless
Expand Down
1 change: 1 addition & 0 deletions server/victory-chart-renderer/tests/render.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {afterAll, beforeAll, describe, expect, test} from 'bun:test';

import {spawnSync} from 'node:child_process';
import {chmodSync, copyFileSync, existsSync, mkdtempSync, readFileSync, rmSync} from 'node:fs';
import {tmpdir} from 'node:os';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {describe, expect, test} from 'bun:test';

import type {TNode} from 'react-native-render-html';

import {describe, expect, test} from 'bun:test';
import createMock from 'tests/utils/createMock';

import resolveCanvasSize from '../src/resolveCanvasSize';
Expand Down
Loading
Loading