fix: harden and de-slop the merged sidebar/chat/settings batch
Post-merge follow-ups for #2740 #2735 #2734 #2690 #2676 #2738 #2684 #2689 #2733 #2739 #2462 #2687 #2736 #2618 #2697, plus three regressions found while reviewing them: - ctrl/cmd+digit while typing no longer switches session tabs (#2503 was still open in practice: the guard only covered the mod+alt surface binding) - Shiki template-call sanitizer now covers every bundled grammar, including the js/ts aliases and embedding grammars; timed-out highlight requests are memoized and no longer cancel unrelated in-flight requests - settings flush on suspend uses keepalive and also fires on Capacitor appStateChange; keeps the selected model persisted across mode switches - remote-only branches fetch before checkout; range helpers fail clearly - git status invalidation now fires for runtime adapters too - settings number inputs and select triggers size in ch so they scale with the interface font - recent-activity timestamps tick from one list-level ticker - Markdown preview find goes through the shared find_in_file keybind with containment, no longer counts its own bar, and debounces observer runs - #2676 reverted; #2524 fixed by fading the sticky header's own background instead of overlaying the content below it - sticky group headers in the model picker and sidebar render again (oc-sticky-fade-scroller class restored after9b9d7069c) - project switcher names are left-aligned again (wrapper lost in26dbc2f30) - tool card quick-open icon is always visible and opens the same line as the expanded card's button - tautological tests replaced or removed; new oxlint findings fixed
This commit is contained in:
@@ -28,6 +28,7 @@ import {
|
||||
unstageGitFile,
|
||||
unstageGitFiles,
|
||||
} from './gitApiHttp';
|
||||
import type { GitStatus } from './api/types';
|
||||
|
||||
type FetchCall = {
|
||||
input: RequestInfo | URL;
|
||||
@@ -189,7 +190,7 @@ describe('gitApiHttp status cache', () => {
|
||||
});
|
||||
});
|
||||
|
||||
const statusPayload = (overrides: Record<string, unknown> = {}) => ({
|
||||
const statusPayload = (overrides: Partial<GitStatus> = {}): GitStatus => ({
|
||||
current: 'main',
|
||||
tracking: null,
|
||||
ahead: 0,
|
||||
@@ -199,16 +200,21 @@ const statusPayload = (overrides: Record<string, unknown> = {}) => ({
|
||||
...overrides,
|
||||
});
|
||||
|
||||
const jsonResponse = (payload: unknown) => new Response(JSON.stringify(payload), {
|
||||
const jsonResponse = <T>(payload: T) => new Response(JSON.stringify(payload), {
|
||||
status: 200,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
|
||||
const installStatusMutationFetchMock = () => {
|
||||
// SAFETY: `statusUrls` starts empty and only ever receives request URLs, which
|
||||
// are strings; the annotation names that element type up front.
|
||||
const mock = {
|
||||
statusUrls: [] as string[],
|
||||
behind: 0,
|
||||
};
|
||||
// SAFETY: the mock receives only the (input, init) pair production code passes
|
||||
// and always resolves to a Response, so it honours the fetch contract; the
|
||||
// assertion supplies the overload signatures a plain arrow function cannot.
|
||||
globalThis.fetch = (async (input) => {
|
||||
const url = String(input);
|
||||
if (url.startsWith('/api/git/status')) {
|
||||
@@ -225,9 +231,9 @@ const installStatusMutationFetchMock = () => {
|
||||
* read issues a fresh request that observes the post-mutation state instead of
|
||||
* serving the pre-mutation cache entry.
|
||||
*/
|
||||
const expectStatusInvalidatedBy = async (
|
||||
const expectStatusInvalidatedBy = async <T>(
|
||||
directory: string,
|
||||
mutate: () => Promise<unknown>
|
||||
mutate: () => Promise<T>
|
||||
): Promise<void> => {
|
||||
const mock = installStatusMutationFetchMock();
|
||||
|
||||
@@ -310,6 +316,8 @@ describe('gitApiHttp post-mutation status invalidation (#2281)', () => {
|
||||
test('a failed mutation does not invalidate cached status', async () => {
|
||||
installWindowMock();
|
||||
const statusUrls: string[] = [];
|
||||
// SAFETY: see installStatusMutationFetchMock - the mock honours the fetch
|
||||
// contract; the assertion supplies its overload signatures.
|
||||
globalThis.fetch = (async (input) => {
|
||||
const url = String(input);
|
||||
if (url.startsWith('/api/git/status')) {
|
||||
@@ -330,6 +338,7 @@ describe('gitApiHttp post-mutation status invalidation (#2281)', () => {
|
||||
await checkoutBranch(directory, 'feature');
|
||||
});
|
||||
expect(error).toBeInstanceOf(Error);
|
||||
// SAFETY: the assertion above established that `error` is an Error.
|
||||
expect((error as Error).message).toBe('checkout failed');
|
||||
|
||||
await getGitStatus(directory);
|
||||
@@ -343,6 +352,8 @@ describe('gitApiHttp post-mutation status invalidation (#2281)', () => {
|
||||
installWindowMock();
|
||||
const statusResolvers: Array<(response: Response) => void> = [];
|
||||
const statusUrls: string[] = [];
|
||||
// SAFETY: see installStatusMutationFetchMock - the mock honours the fetch
|
||||
// contract; the assertion supplies its overload signatures.
|
||||
globalThis.fetch = (async (input) => {
|
||||
const url = String(input);
|
||||
if (url.startsWith('/api/git/status')) {
|
||||
|
||||
Reference in New Issue
Block a user