Files
paseo/scripts/oxlint/paseo-plugin.test.mjs
T
Mohamed Boudra d61dcaf1df fix(app): give subagent rows room on compact and standard press feedback (#7364)
The subagent track rows were as dense on a phone as in the desktop popover,
and the archive and detach actions were hand-rolled pressables that only
recoloured the glyph, so a tap or hover never showed the rounded highlight
every other icon action has.

Rows now take the compact menu-row height and padding below the `md`
breakpoint, decided once in the track row style; the desktop popover keeps
its density. The actions are `ToolbarButton`, which owns the hitbox, the
rounded highlight on hover and press, the compact size, and the tooltip.

A repo oxlint rule, `paseo/no-raw-pressable`, reports `Pressable` and
`Touchable*` imports outside `components/ui/` so the next tap target reaches
for a primitive. Existing importers are on a burn-down list they can only
leave. The rule is its own name rather than another `no-restricted-imports`
entry so its override cannot interact with the existing burn-downs. Its test
runs the real oxlint binary and is wired into the CI quality job.
2026-10-19 12:24:14 +07:00

55 lines
2.0 KiB
JavaScript

import assert from "node:assert/strict";
import { spawnSync } from "node:child_process";
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import path from "node:path";
import { test } from "node:test";
import { fileURLToPath } from "node:url";
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
const oxlintBin = path.join(repoRoot, "node_modules/.bin/oxlint");
const pluginPath = path.join(repoRoot, "scripts/oxlint/paseo-plugin.mjs");
function lint(source) {
const dir = mkdtempSync(path.join(tmpdir(), "paseo-oxlint-"));
try {
writeFileSync(
path.join(dir, ".oxlintrc.json"),
JSON.stringify({ jsPlugins: [pluginPath], rules: { "paseo/no-raw-pressable": "error" } }),
);
writeFileSync(path.join(dir, "probe.tsx"), source);
const result = spawnSync(oxlintBin, ["-c", ".oxlintrc.json", "probe.tsx"], {
cwd: dir,
encoding: "utf8",
});
return { failed: result.status !== 0, output: result.stdout + result.stderr };
} finally {
rmSync(dir, { recursive: true, force: true });
}
}
test("a raw Pressable from react-native is reported", () => {
const result = lint(
'import { Pressable, View } from "react-native";\nexport { Pressable, View };\n',
);
assert.equal(result.failed, true);
assert.match(result.output, /no-raw-pressable/);
});
test("an aliased gesture-handler touchable is reported", () => {
const result = lint(
'import { TouchableOpacity as Tap } from "react-native-gesture-handler";\nexport { Tap };\n',
);
assert.equal(result.failed, true);
assert.match(result.output, /no-raw-pressable/);
});
test("press primitives and pressable types are allowed", () => {
const result = lint(
'import { View, type PressableProps } from "react-native";\n' +
'import { ToolbarButton } from "@/components/ui/pane-content-toolbar";\n' +
"export type Props = PressableProps;\nexport { View, ToolbarButton };\n",
);
assert.equal(result.failed, false, result.output);
});