Files
paseo/packages/app/vitest.config.ts
T
Mohamed Boudra adf27de7d0 Keep Explorer consistent across phone, tablet and desktop layouts (#7424)
* Upgrade to Expo 57 and unify Explorer across layouts

* feat(app): retain Explorer across layout and appearance changes

Keep one workspace Explorer host and destination-aware navigation so folding, theme updates and compact sheets preserve the current panel. Scope pull request discovery and timeline demand to active surfaces.

* fix(app): keep Explorer resize handles stretched through the dock

The shared frame moved the divider into an absolute column, leaving its horizontal handle without height. Restore the row so pointer drags reach it, preserving the existing resize and performance contract.

* fix(app): keep Explorer drag widths out of the shared stylesheet

Carry the existing inline sizing policy into the shared frame so pointer previews do not accumulate CSS rules.

* fix(test): align isolated test runtimes with Expo 57

Hoisted React consumers need the matching DOM renderer at the repository root. Automated Metro processes must not install native DevTools into disposable homes, and development toast overlays must not cover compact actions.

Keep Explorer journeys on the shared host and synchronize setup fixtures on daemon output rather than a one-second file-marker window. A delayed real child start reproduces the readiness failure on every platform.

* fix(test): await retained shell layout after composer resize

The gesture host now stays mounted across breakpoints, so its presence no longer identifies compact layout. Report the current layout on the retained host and synchronize composer journeys with that state.

* fix(app): preserve browser interactions in retained workspace hosts

Initially disabled gesture hosts still registered pointer and wheel listeners, while hover cards used window positions inside a workspace-local sheet portal. Respect initial gesture enablement and position cards relative to their measured floating host.

Keep developer-toast dismissal independent of pointer input and replacement toast visibility so modal, hover, and focus checks retain their input state.

* fix(test): share developer toast handling with Electron browser runs

The standalone Electron browser runner bypassed the browser fixture, allowing developer errors to cover Settings. Use the same non-pointer dismissal in both runners while retaining their console logs.
2026-10-20 11:59:49 +07:00

251 lines
8.9 KiB
TypeScript

import { defineConfig, configDefaults } from "vitest/config";
import { playwright } from "@vitest/browser-playwright";
import path from "path";
import fs from "fs";
import { transformSync } from "@babel/core";
import { build } from "esbuild";
// Set this before Vite resolves dependencies and Vitest starts its workers.
Object.assign(process.env, { NODE_ENV: "test" });
const appNodeModules = path.resolve(__dirname, "node_modules");
const rootNodeModules = path.resolve(__dirname, "../../node_modules");
const resolvePackageEntry = (packageName: string) => {
const appPackagePath = path.resolve(appNodeModules, packageName);
return fs.existsSync(appPackagePath)
? appPackagePath
: path.resolve(rootNodeModules, packageName);
};
export default defineConfig({
plugins: [
{
name: "compile-usage-components",
enforce: "pre",
transform(source, id) {
// These labels must react to time and language in the compiled app too.
if (!id.includes("/src/usage/") || !id.endsWith(".tsx") || id.includes(".test.")) return;
const result = transformSync(source, {
filename: id,
babelrc: false,
configFile: false,
parserOpts: { plugins: ["typescript", "jsx"] },
plugins: [["babel-plugin-react-compiler", { target: "19" }]],
sourceMaps: true,
});
if (result?.code) return { code: result.code, map: result.map };
},
},
{
name: "bundle-i18n-resources",
// Metro resolves literal requires lazily. Vite's TS transform leaves them
// to Node, which cannot resolve the resource modules. Bundle this boundary
// so unit and browser consumers exercise the same deferred module loading.
async transform(_source, id) {
if (path.resolve(id) !== path.resolve(__dirname, "src/i18n/i18next.ts")) return;
const result = await build({
entryPoints: [id],
bundle: true,
format: "esm",
platform: "neutral",
external: ["i18next", "react-i18next"],
write: false,
});
return { code: result.outputFiles[0].text, map: null };
},
},
],
test: {
environment: "node",
exclude: [...configDefaults.exclude, "e2e/**"],
projects: [
{
extends: true,
test: {
name: "unit",
environment: "node",
include: [
"src/**/*.{test,spec}.{ts,tsx}",
"native-release-version.test.ts",
"app.config.test.ts",
"vitest-environment.test.ts",
],
setupFiles: [path.resolve(__dirname, "vitest.setup.ts")],
exclude: [...configDefaults.exclude, "e2e/**", "src/**/*.browser.{test,spec}.{ts,tsx}"],
},
},
{
extends: true,
test: {
name: "browser",
fileParallelism: false,
include: ["src/**/*.browser.{test,spec}.{ts,tsx}"],
browser: {
enabled: true,
provider: playwright(),
headless: true,
connectTimeout: 180_000,
instances: [{ browser: "chromium" }],
screenshotDirectory: ".vitest-screenshots",
},
globalSetup: path.resolve(__dirname, "src/runtime/websocket-test-global-setup.ts"),
},
},
],
/**
* Expo pulls in native tooling (xcode, etc.) that executes files relying on `process.send`.
* Vitest's default worker pool uses worker_threads, which intentionally stub that API and
* immediately throw `Unexpected call to process.send`. Running the suite in forked processes
* keeps `process.send` intact so the app tests can boot before hitting the intentional failures.
*/
pool: "forks",
maxWorkers: 2,
server: {
deps: {
fallbackCJS: true,
inline: [
"zustand",
"@tanstack/react-query",
"react-native-web",
"react-native-reanimated",
"react-native-worklets",
"react-native-gesture-handler",
"react-native-keyboard-controller",
],
},
},
},
// Reanimated and gesture-handler pick platform files by extension
// (e.g. `GestureHandlerRootView.web.js`). Vite's optimizer does not apply `resolve.extensions`,
// so it scans the native files and dies on imports react-native-web has no answer for.
// Unbundled, the same imports go through the resolver below and land on the web files.
optimizeDeps: {
// Bundle the CJS dependencies of the excluded gesture-handler package for the browser.
include: [
"react/jsx-runtime",
"react/compiler-runtime",
"react-native-gesture-handler > hoist-non-react-statics",
"react-native-gesture-handler > invariant",
],
exclude: [
"react-native-reanimated",
"react-native-gesture-handler",
"react-native-keyboard-controller",
],
},
// The globals a React Native bundler defines, which esbuild is no longer there to supply for
// the package excluded above.
define: {
"process.env.JEST_WORKER_ID": "undefined",
__DEV__: "false",
global: "globalThis",
},
resolve: {
extensions: [
".web.mjs",
".web.js",
".web.mts",
".web.ts",
".web.jsx",
".web.tsx",
".mjs",
".js",
".mts",
".ts",
".jsx",
".tsx",
".json",
],
alias: [
{
find: /^@getpaseo\/relay\/e2ee$/,
replacement: path.resolve(__dirname, "../relay/src/e2ee.ts"),
},
{
find: /^@getpaseo\/relay$/,
replacement: path.resolve(__dirname, "../relay/src/index.ts"),
},
{ find: "@", replacement: path.resolve(__dirname, "src") },
// Keep keyboard-controller's imports in Vite so native aliases and platform extensions apply.
{
find: /^react-native-keyboard-controller$/,
replacement: path.resolve(
resolvePackageEntry("react-native-keyboard-controller"),
"lib/module/index.js",
),
},
// The CJS entry bypasses Vite's React Native alias and web-extension resolution.
{
find: /^react-native-gesture-handler$/,
replacement: path.resolve(
rootNodeModules,
"react-native-gesture-handler/lib/module/index.js",
),
},
// Must precede the `react-native` alias: a string `find` matches by prefix, so this subpath
// would otherwise resolve inside a react-native-web *file* and break the dependency scan.
// Reanimated only imports it on the native path, which no test takes.
{
find: /^react-native\/Libraries\/Renderer\/shims\/ReactFabric$/,
replacement: path.resolve(__dirname, "test-stubs/react-native-fabric-shim.ts"),
},
// Point to the ESM build so Vite can transform its imports and apply the
// react alias below (the CJS build uses require('react') which bypasses
// Vite alias resolution).
{
find: "react-native",
replacement: path.resolve(rootNodeModules, "react-native-web/dist/index.js"),
},
{ find: "react", replacement: resolvePackageEntry("react") },
{
find: "react-dom",
replacement: resolvePackageEntry("react-dom"),
},
{
find: /^@xterm\/addon-ligatures\/lib\/addon-ligatures\.mjs$/,
replacement: path.resolve(__dirname, "test-stubs/xterm-addon-ligatures.ts"),
},
{
find: /^@xterm\/addon-ligatures$/,
replacement: path.resolve(__dirname, "test-stubs/xterm-addon-ligatures.ts"),
},
{
find: /^react-native-unistyles$/,
replacement: path.resolve(__dirname, "test-stubs/react-native-unistyles.ts"),
},
{
find: /^react-native-svg$/,
replacement: path.resolve(__dirname, "test-stubs/react-native-svg.ts"),
},
// Both ship untranspiled Flow and fail to parse on import, which takes out any test that
// mounts a menu surface.
{
find: /^react-native-safe-area-context$/,
replacement: path.resolve(__dirname, "test-stubs/react-native-safe-area-context.ts"),
},
{
find: /^@gorhom\/bottom-sheet$/,
replacement: path.resolve(__dirname, "test-stubs/gorhom-bottom-sheet.ts"),
},
{
find: /^react-native-reanimated\/scripts\/validate-worklets-version$/,
replacement: path.resolve(__dirname, "test-stubs/reanimated-validate-worklets-version.ts"),
},
{
find: /^expo-linking$/,
replacement: path.resolve(__dirname, "test-stubs/expo-linking.ts"),
},
// No Node implementation: every call on the real module rejects, so a test
// touching a persisted store drowns in unhandled rejections.
{
find: /^@react-native-async-storage\/async-storage$/,
replacement: path.resolve(__dirname, "test-stubs/async-storage.ts"),
},
{
find: /^lucide-react-native$/,
replacement: path.resolve(__dirname, "test-stubs/lucide-react-native.ts"),
},
],
},
});