diff --git a/config/scripts/verify-linux-glibc-floor.test.mjs b/config/scripts/verify-linux-glibc-floor.test.mjs index 73ff2c7f67d..4b77ba8e046 100644 --- a/config/scripts/verify-linux-glibc-floor.test.mjs +++ b/config/scripts/verify-linux-glibc-floor.test.mjs @@ -9,7 +9,6 @@ const { readElfMachine, declaredArchFromPath, findArchViolation, - ELF_MACHINE_BY_ARCH, parseGlibcVersion, compareGlibcVersions, parseVersionNeeds, @@ -409,8 +408,8 @@ describe('bundled native binary architecture', () => { it('reads e_machine from a little-endian ELF', async () => { const dir = await mkdtemp(join(tmpdir(), 'orca-elf-arch-')) const file = join(dir, 'pty.node') - await writeFile(file, elfHeader(ELF_MACHINE_BY_ARCH.arm64)) - expect(readElfMachine(file)).toBe(ELF_MACHINE_BY_ARCH.arm64) + await writeFile(file, elfHeader(0xb7)) + expect(readElfMachine(file)).toBe(0xb7) await rm(dir, { recursive: true, force: true }) }) @@ -423,7 +422,7 @@ describe('bundled native binary architecture', () => { const pkg = join(dir, '@parcel', 'watcher-linux-arm64-glibc') await mkdir(pkg, { recursive: true }) const file = join(pkg, 'watcher.node') - await writeFile(file, elfHeader(ELF_MACHINE_BY_ARCH.arm64)) + await writeFile(file, elfHeader(0xb7)) expect(declaredArchFromPath(file)).toBe('arm64') expect(findArchViolation(file, 'x64')).toBeNull() await rm(dir, { recursive: true, force: true }) @@ -435,7 +434,7 @@ describe('bundled native binary architecture', () => { const nested = join(dir, 'bin', 'linux-arm64-148') await mkdir(nested, { recursive: true }) const file = join(nested, 'node-pty.node') - await writeFile(file, elfHeader(ELF_MACHINE_BY_ARCH.x64)) + await writeFile(file, elfHeader(0x3e)) expect(findArchViolation(file, 'arm64')).toMatchObject({ actual: 'x64', expectedArch: 'arm64' }) // Still caught even when the slice being built is x64. expect(findArchViolation(file, 'x64')).toMatchObject({ actual: 'x64', expectedArch: 'arm64' }) @@ -448,7 +447,7 @@ describe('bundled native binary architecture', () => { const dir = join(root, 'resources', 'ripgrep', 'linux-x64') await mkdir(dir, { recursive: true }) const file = join(dir, 'rg') - await writeFile(file, elfHeader(ELF_MACHINE_BY_ARCH.x64)) + await writeFile(file, elfHeader(0x3e)) expect(findArchViolation(file, 'arm64', root)).toBeNull() await rm(dirname(root), { recursive: true, force: true }) }) @@ -456,7 +455,7 @@ describe('bundled native binary architecture', () => { it('flags an x86-64 binary in an arm64 slice', async () => { const dir = await mkdtemp(join(tmpdir(), 'orca-elf-arch-')) const file = join(dir, 'pty.node') - await writeFile(file, elfHeader(ELF_MACHINE_BY_ARCH.x64)) + await writeFile(file, elfHeader(0x3e)) expect(findArchViolation(file, 'arm64')).toMatchObject({ actual: 'x64' }) await rm(dir, { recursive: true, force: true }) }) @@ -464,7 +463,7 @@ describe('bundled native binary architecture', () => { it('accepts a matching architecture', async () => { const dir = await mkdtemp(join(tmpdir(), 'orca-elf-arch-')) const file = join(dir, 'pty.node') - await writeFile(file, elfHeader(ELF_MACHINE_BY_ARCH.x64)) + await writeFile(file, elfHeader(0x3e)) expect(findArchViolation(file, 'x64')).toBeNull() await rm(dir, { recursive: true, force: true }) }) @@ -472,7 +471,7 @@ describe('bundled native binary architecture', () => { it('stays silent when no target architecture is supplied', async () => { const dir = await mkdtemp(join(tmpdir(), 'orca-elf-arch-')) const file = join(dir, 'pty.node') - await writeFile(file, elfHeader(ELF_MACHINE_BY_ARCH.x64)) + await writeFile(file, elfHeader(0x3e)) expect(findArchViolation(file, undefined)).toBeNull() await rm(dir, { recursive: true, force: true }) }) diff --git a/config/scripts/verify-packaged-node-pty-job-ownership.test.mjs b/config/scripts/verify-packaged-node-pty-job-ownership.test.mjs index 9d2317c8a33..2901af651d5 100644 --- a/config/scripts/verify-packaged-node-pty-job-ownership.test.mjs +++ b/config/scripts/verify-packaged-node-pty-job-ownership.test.mjs @@ -1,4 +1,4 @@ -import { mkdirSync, mkdtempSync, readFileSync, writeFileSync } from 'node:fs' +import { mkdirSync, mkdtempSync, writeFileSync } from 'node:fs' import { createRequire } from 'node:module' import { tmpdir } from 'node:os' import { dirname, join } from 'node:path' @@ -15,10 +15,6 @@ const { const { CYGWIN_BREAKAWAY_MARKER } = require('./node-pty-job-ownership.cjs') const fixtureDir = mkdtempSync(join(tmpdir(), 'packaged-node-pty-job-')) -const ELECTRON_BUILDER_CONFIG = readFileSync( - new URL('../electron-builder.config.cjs', import.meta.url), - 'utf8' -) /** A real enough addon: a machine field the arch check reads, and the marker. */ function conptyImage({ arch = 'x64', cygwinBreakawayDenied = true } = {}) { @@ -139,33 +135,16 @@ describe('verifyPackagedConptyBreakawayMarker', () => { // The cross-host package. No host but Windows can build conpty.node, so there // is no build/Release for prune to have replaced the prebuild with -- and the // published prebuild is exactly the binary that leaks every MSYS pane child. - it('fails a cross-host package left holding the published prebuild', () => { + it('rejects an unpatched published prebuild and explains both host remedies', () => { const resourcesDir = packagedResources({ 'prebuilds/win32-x64/conpty.node': { cygwinBreakawayDenied: false } }) - expect(() => verifyPackagedConptyBreakawayMarker(resourcesDir, 'x64')).toThrow( - /predates the Cygwin\/MSYS job-breakaway denial/ - ) - }) - - it('tells that package how to fix it, which is not a rebuild it can run', () => { - const resourcesDir = packagedResources({ - 'prebuilds/win32-x64/conpty.node': { cygwinBreakawayDenied: false } - }) - expect(() => verifyPackagedConptyBreakawayMarker(resourcesDir, 'x64')).toThrow( + const verify = () => verifyPackagedConptyBreakawayMarker(resourcesDir, 'x64') + expect(verify).toThrow(/predates the Cygwin\/MSYS job-breakaway denial/) + expect(verify).toThrow( /this package holds no node-pty source build at all[\s\S]*Otherwise package this Windows slice on a host that can/ ) - }) - - // The same state reaches this from a capable host too, when the rebuild left - // nothing: telling that packager to change hosts would send them nowhere. - it('does not assume the packaging host is the wrong one', () => { - const resourcesDir = packagedResources({ - 'prebuilds/win32-x64/conpty.node': { cygwinBreakawayDenied: false } - }) - expect(() => verifyPackagedConptyBreakawayMarker(resourcesDir, 'x64')).toThrow( - /If this IS a Windows x64 host, the rebuild did not leave one/ - ) + expect(verify).toThrow(/If this IS a Windows x64 host, the rebuild did not leave one/) }) // The cross-arch package that worked: beforeBuild rebuilds node-pty for the @@ -183,30 +162,25 @@ describe('verifyPackagedConptyBreakawayMarker', () => { // The cross-arch package that silently did not: build/Release is the // packaging host's own arch, the target cannot load it, and the loader falls // through to the unpatched prebuild underneath. - it('fails a cross-arch package whose build/Release is the packaging host arch', () => { + it('rejects a wrong-arch source build with the target rebuild command', () => { const resourcesDir = packagedResources({ 'build/Release/conpty.node': { arch: 'x64' }, 'prebuilds/win32-arm64/conpty.node': { arch: 'arm64', cygwinBreakawayDenied: false } }) - expect(() => verifyPackagedConptyBreakawayMarker(resourcesDir, 'arm64')).toThrow( - /prebuilds[\\/]win32-arm64/ + const verify = () => verifyPackagedConptyBreakawayMarker(resourcesDir, 'arm64') + expect(verify).toThrow(/prebuilds[\\/]win32-arm64/) + expect(verify).toThrow( + /the source build beside it is the wrong architecture[\s\S]*machine 0x8664/ ) + expect(verify).toThrow(/rebuild-native-deps\.mjs --platform=win32 --arch=arm64/) + expect(verify).toThrow(/^(?![\s\S]*Package this Windows slice on such a host)[\s\S]*$/) }) - it('refuses a package whose every conpty.node is the wrong architecture', () => { + it('rejects unloadable architectures and reports their machine values', () => { const resourcesDir = packagedResources({ 'build/Release/conpty.node': { arch: 'x64' } }) - expect(() => verifyPackagedConptyBreakawayMarker(resourcesDir, 'arm64')).toThrow( - /the app can load none of them/ - ) - }) - - // Naming the machine it found is what separates a cross-arch build from a - // truncated download, which are the same "cannot load this" to the loader. - it('names what it found rather than guessing why', () => { - const resourcesDir = packagedResources({ 'build/Release/conpty.node': { arch: 'x64' } }) - expect(() => verifyPackagedConptyBreakawayMarker(resourcesDir, 'arm64')).toThrow( - /machine 0x8664[\s\S]*0xaa64/ - ) + const verify = () => verifyPackagedConptyBreakawayMarker(resourcesDir, 'arm64') + expect(verify).toThrow(/the app can load none of them/) + expect(verify).toThrow(/machine 0x8664[\s\S]*0xaa64/) }) it('calls a candidate that is not a PE image what it is', () => { @@ -245,38 +219,6 @@ describe('verifyPackagedConptyBreakawayMarker', () => { ) }) - // The remedy for this one is a rebuild, not a different host, and the - // difference is a build somebody has to run twice to find out. - it('blames the wrong-arch source build rather than the host, when there is one', () => { - const resourcesDir = packagedResources({ - 'build/Release/conpty.node': { arch: 'x64' }, - 'prebuilds/win32-arm64/conpty.node': { arch: 'arm64', cygwinBreakawayDenied: false } - }) - expect(() => verifyPackagedConptyBreakawayMarker(resourcesDir, 'arm64')).toThrow( - /the source build beside it is the wrong architecture[\s\S]*machine 0x8664/ - ) - }) - - it('tells that build the command that would fix it', () => { - const resourcesDir = packagedResources({ - 'build/Release/conpty.node': { arch: 'x64' }, - 'prebuilds/win32-arm64/conpty.node': { arch: 'arm64', cygwinBreakawayDenied: false } - }) - expect(() => verifyPackagedConptyBreakawayMarker(resourcesDir, 'arm64')).toThrow( - /rebuild-native-deps\.mjs --platform=win32 --arch=arm64/ - ) - }) - - it('does not tell it to change hosts, which would not help', () => { - const resourcesDir = packagedResources({ - 'build/Release/conpty.node': { arch: 'x64' }, - 'prebuilds/win32-arm64/conpty.node': { arch: 'arm64', cygwinBreakawayDenied: false } - }) - expect(() => verifyPackagedConptyBreakawayMarker(resourcesDir, 'arm64')).toThrow( - /^(?![\s\S]*Package this Windows slice on such a host)[\s\S]*$/ - ) - }) - it('ignores a prebuild for an arch this slice will never load', () => { const resourcesDir = packagedResources({ 'build/Release/conpty.node': {}, @@ -310,18 +252,11 @@ describe('verifyPackagedConptyBreakawayMarker', () => { // Nothing to load is not "a layout we do not recognise", it is a package with // no ConPTY backend, and a gate that cannot see its subject is not a gate. - it('refuses rather than skip a package with no conpty.node at all', () => { + it('rejects a package with no ConPTY backend and lists the searched paths', () => { const resourcesDir = packagedResources({}) - expect(() => verifyPackagedConptyBreakawayMarker(resourcesDir, 'x64')).toThrow( - /no conpty\.node on any path its loader tries/ - ) - }) - - it('names every path it looked at when it finds none', () => { - const resourcesDir = packagedResources({}) - expect(() => verifyPackagedConptyBreakawayMarker(resourcesDir, 'x64')).toThrow( - /build[\\/]Release[\s\S]*build[\\/]Debug[\s\S]*prebuilds[\\/]win32-x64/ - ) + const verify = () => verifyPackagedConptyBreakawayMarker(resourcesDir, 'x64') + expect(verify).toThrow(/no conpty\.node on any path its loader tries/) + expect(verify).toThrow(/build[\\/]Release[\s\S]*build[\\/]Debug[\s\S]*prebuilds[\\/]win32-x64/) }) // Present but unreadable is the state that used to pass, so it must not warn. @@ -421,10 +356,4 @@ describe('verifyPackagedWindowsNodePty', () => { }) ).toThrow(/predates the Cygwin\/MSYS job-breakaway denial/) }) - - it('is what the afterPack hook calls for a Windows slice', () => { - expect(ELECTRON_BUILDER_CONFIG).toContain( - 'verifyPackagedWindowsNodePty(resourcesDir, context.arch, { canExecuteTargetArch })' - ) - }) }) diff --git a/config/scripts/verify-skills-cli-runtime.test.mjs b/config/scripts/verify-skills-cli-runtime.test.mjs index e827548434d..38aa658b3ea 100644 --- a/config/scripts/verify-skills-cli-runtime.test.mjs +++ b/config/scripts/verify-skills-cli-runtime.test.mjs @@ -1,4 +1,4 @@ -import { mkdir, mkdtemp, readFile, rm, symlink, writeFile } from 'node:fs/promises' +import { mkdir, mkdtemp, rm, symlink, writeFile } from 'node:fs/promises' import { realpathSync } from 'node:fs' import { createRequire } from 'node:module' import { tmpdir } from 'node:os' @@ -21,23 +21,6 @@ async function writeSkillsCliFixture(outDir, handlerSource) { } describe('skills CLI runtime closure', () => { - it('runs after Electron composes the final output', async () => { - const packageJson = JSON.parse( - await readFile(new URL('../../package.json', import.meta.url), 'utf8') - ) - for (const scriptName of ['build:desktop', 'build:desktop:ci', 'build:release']) { - const script = packageJson.scripts[scriptName].replace( - 'pnpm run build:desktop:assets', - packageJson.scripts['build:desktop:assets'] - ) - expect(script).toContain('build:electron-vite') - expect(script).toContain('verify:built-skills-cli') - expect(script.indexOf('build:electron-vite')).toBeLessThan( - script.indexOf('verify:built-skills-cli') - ) - } - }) - it('reports the missing final-artifact import and its owner', async () => { const root = await mkdtemp(join(tmpdir(), 'orca-skills-cli-closure-')) try { diff --git a/config/scripts/wsl-orcad-qualification.test.mjs b/config/scripts/wsl-orcad-qualification.test.mjs index 0b19db24064..5be7fee0480 100644 --- a/config/scripts/wsl-orcad-qualification.test.mjs +++ b/config/scripts/wsl-orcad-qualification.test.mjs @@ -1,6 +1,7 @@ import { expect, it } from 'vitest' -import { WSL_FAMILIES, wslOrcadQualification } from './wsl-orcad-qualification.mjs' +import { wslOrcadQualification } from './wsl-orcad-qualification.mjs' +const fullFamilies = ['ubuntu-24.04', 'debian-12', 'fedora-41'] const runtime = 'src/main/runtime/rpc/methods/session-title.ts' const events = Array.from({ length: 100 }, (_, index) => ({ name: 'pull_request', @@ -45,7 +46,7 @@ it.each([ 'src/main/runtime/../outside.ts' ])('retains every distro for a mixed change containing %s', (file) => { expect(wslOrcadQualification([runtime, file], [runtime, file], event).families).toEqual( - WSL_FAMILIES + fullFamilies ) }) @@ -76,6 +77,6 @@ it.each([ ])( 'fails closed for incomplete, non-modification or reference evidence: %j', ({ changed, modified, context }) => { - expect(wslOrcadQualification(changed, modified, context).families).toEqual(WSL_FAMILIES) + expect(wslOrcadQualification(changed, modified, context).families).toEqual(fullFamilies) } ) diff --git a/mobile/src/transport/home-host-auto-connect.test.ts b/mobile/src/transport/home-host-auto-connect.test.ts index ecf04934809..70e3801f03d 100644 --- a/mobile/src/transport/home-host-auto-connect.test.ts +++ b/mobile/src/transport/home-host-auto-connect.test.ts @@ -1,7 +1,6 @@ import { describe, expect, it } from 'vitest' import type { HostProfile } from './types' import { - HOME_AUTO_CONNECT_LIMIT, resolveHomeHostConnectionState, selectHomeAutoConnectHostIds } from './home-host-auto-connect' @@ -25,11 +24,12 @@ describe('home host auto-connect', () => { host('second', 4), host('third', 3), host('fourth', 2), - host('missing-credentials', 6, false) + host('missing-credentials', 6, false), + { ...host('missing-token', 7), deviceToken: '' }, + { ...host('missing-key', 8), publicKeyB64: '' } ] expect(selectHomeAutoConnectHostIds(hosts)).toEqual(['newest', 'second', 'third']) - expect(selectHomeAutoConnectHostIds(hosts)).toHaveLength(HOME_AUTO_CONNECT_LIMIT) }) it('does not mutate the host card order', () => { diff --git a/mobile/src/transport/host-open-retry-scheduler.test.ts b/mobile/src/transport/host-open-retry-scheduler.test.ts index 6235521b256..f159a7458c8 100644 --- a/mobile/src/transport/host-open-retry-scheduler.test.ts +++ b/mobile/src/transport/host-open-retry-scheduler.test.ts @@ -17,16 +17,19 @@ describe('HostOpenRetryScheduler', () => { open }) - scheduler.recordFailure('host-1', generation) - await vi.advanceTimersByTimeAsync(1_000) - expect(open).toHaveBeenCalledOnce() - - generation = 2 - scheduler.recordFailure('host-1', generation) - await vi.advanceTimersByTimeAsync(1_999) - expect(open).toHaveBeenCalledOnce() - await vi.advanceTimersByTimeAsync(1) - expect(open).toHaveBeenCalledTimes(2) + const delays = [1_000, 2_000, 5_000, 15_000, 30_000, 60_000, 60_000] + for (const [index, delay] of delays.entries()) { + generation = index + 1 + expect(scheduler.recordFailure('host-1', generation)).toEqual({ + failureCount: index + 1, + nextDelayMs: delay + }) + await vi.advanceTimersByTimeAsync(delay - 1) + expect(open).toHaveBeenCalledTimes(index) + await vi.advanceTimersByTimeAsync(1) + expect(open).toHaveBeenCalledTimes(index + 1) + expect(open).toHaveBeenLastCalledWith('host-1') + } }) it('expedites without forgiving the failure streak', async () => { @@ -47,6 +50,13 @@ describe('HostOpenRetryScheduler', () => { expect(open).toHaveBeenCalledOnce() await vi.advanceTimersByTimeAsync(1) expect(open).toHaveBeenCalledTimes(2) + + scheduler.recordFailure('host-1', generation) + generation = 3 + scheduler.expedite('host-1') + expect(open).toHaveBeenCalledTimes(2) + await vi.advanceTimersByTimeAsync(5_000) + expect(open).toHaveBeenCalledTimes(2) }) it('schedules and clears with no injected timers when the global rejects a non-global receiver', async () => { diff --git a/mobile/src/transport/host-status-capability-ignorability.test.ts b/mobile/src/transport/host-status-capability-ignorability.test.ts index a1dcdb561fc..912d006b064 100644 --- a/mobile/src/transport/host-status-capability-ignorability.test.ts +++ b/mobile/src/transport/host-status-capability-ignorability.test.ts @@ -161,7 +161,9 @@ describe('mobileWeb.bundle.v1 on a released client', () => { expect(advertised.probedCapabilities).toContain(MOBILE_WEB_BUNDLE_CAPABILITY) expect(advertised.quickCommandsSupported).toBe(true) expect(advertised.worktreeCreateSupport?.tasksSupported).toBe(true) - expect(advertised.worktreeCreateSupport?.worktreeCreateIdempotency).not.toBe(false) + expect(advertised.worktreeCreateSupport?.worktreeCreateIdempotency).toEqual({ + dedupeTtlMs: 60_000 + }) expect(oldDesktop.gates.hostCapabilities).not.toContain(MOBILE_WEB_BUNDLE_CAPABILITY) expect(asIfNeverAdvertised(advertised)).toEqual(oldDesktop) diff --git a/mobile/src/transport/host-status-reply-schema.test.ts b/mobile/src/transport/host-status-reply-schema.test.ts index d55d953784b..10f3a05fd26 100644 --- a/mobile/src/transport/host-status-reply-schema.test.ts +++ b/mobile/src/transport/host-status-reply-schema.test.ts @@ -20,7 +20,7 @@ describe('the host status decodes from every version the gate admits', () => { protocolVersion: 5, minCompatibleMobileVersion: 1, appVersion: '1.4.200', - capabilities: ['mobile.tasks.v1', 'push.v1'], + capabilities: ['mobile.tasks.v1', 'push.v1', 'a', 'b'], floatingWorkspaceEnabled: true, hostPlatform: 'darwin', machineName: 'Studio' @@ -40,10 +40,6 @@ describe('the host status decodes from every version the gate admits', () => { }) describe('capabilities salvage whole, not per element', () => { - it('keeps a list of strings', () => { - expect(reads(hostStatusSchema, { capabilities: ['a', 'b'] }).capabilities).toEqual(['a', 'b']) - }) - it('drops the whole list for one non-string, which is what the probe published', () => { // transport-capability-probe-non-string-capabilities-drop records main publishing `[]` for // `['push.v1', 7]`; dropping only the 7 would publish a set main never published. diff --git a/src/renderer/src/components/browser-pane/history/browser-history-view-rows.test.ts b/src/renderer/src/components/browser-pane/history/browser-history-view-rows.test.ts index 68784c94e4b..725bdb625c3 100644 --- a/src/renderer/src/components/browser-pane/history/browser-history-view-rows.test.ts +++ b/src/renderer/src/components/browser-pane/history/browser-history-view-rows.test.ts @@ -21,6 +21,9 @@ describe('browser history view rows', () => { expect(listBrowserHistoryRows(history, [], 'orca github').map((r) => r.title)).toEqual([ 'Orca repo' ]) + expect(listBrowserHistoryRows(history, [], ' GITHUB ORCA ').map((r) => r.title)).toEqual([ + 'Orca repo' + ]) expect(listBrowserHistoryRows(history, [], '').map((r) => r.title)).toEqual([ 'Orca - Wikipedia', 'Orca repo' diff --git a/src/renderer/src/components/editor/viewer-zoom-request.test.ts b/src/renderer/src/components/editor/viewer-zoom-request.test.ts index 99af764cdf8..420ca3954d3 100644 --- a/src/renderer/src/components/editor/viewer-zoom-request.test.ts +++ b/src/renderer/src/components/editor/viewer-zoom-request.test.ts @@ -7,10 +7,16 @@ describe('requestViewerZoom', () => { expect(requestViewerZoom('in')).toBe(false) const onZoom = vi.fn() - const stop = listenForViewerZoomRequests(onZoom, { current: document.body }) + const rootRef: { current: HTMLElement | null } = { current: document.body } + const stop = listenForViewerZoomRequests(onZoom, rootRef) expect(requestViewerZoom('out')).toBe(true) expect(onZoom).toHaveBeenCalledWith('out') + rootRef.current = null + expect(requestViewerZoom('in')).toBe(false) + expect(onZoom).toHaveBeenCalledTimes(1) + + rootRef.current = document.body stop() expect(requestViewerZoom('reset')).toBe(false) expect(onZoom).toHaveBeenCalledTimes(1) diff --git a/src/renderer/src/components/right-sidebar/ai-vault-panel-transient-state.test.tsx b/src/renderer/src/components/right-sidebar/ai-vault-panel-transient-state.test.tsx index cd0ebfb5eda..aa1a5e38469 100644 --- a/src/renderer/src/components/right-sidebar/ai-vault-panel-transient-state.test.tsx +++ b/src/renderer/src/components/right-sidebar/ai-vault-panel-transient-state.test.tsx @@ -109,6 +109,14 @@ describe('useAiVaultPanelScope', () => { expect(hook.result.current.collapsedGroups.size).toBe(512) expect(hook.result.current.collapsedGroups.has('group-0')).toBe(false) expect(hook.result.current.collapsedGroups.has('group-512')).toBe(true) + + act(() => hook.result.current.toggleGroup('group-512')) + expect(hook.result.current.collapsedGroups.size).toBe(511) + expect(hook.result.current.collapsedGroups.has('group-512')).toBe(false) + act(() => hook.result.current.toggleGroup('group-512')) + expect(hook.result.current.collapsedGroups.size).toBe(512) + expect(hook.result.current.collapsedGroups.has('group-1')).toBe(true) + expect(hook.result.current.collapsedGroups.has('group-512')).toBe(true) }) it('keeps a dialog local without inheriting or changing sidebar choices', () => { diff --git a/src/renderer/src/components/task-page/github/task-page-github-work-item-mutation-patches.test.ts b/src/renderer/src/components/task-page/github/task-page-github-work-item-mutation-patches.test.ts index e3a7a219b79..748627f74f4 100644 --- a/src/renderer/src/components/task-page/github/task-page-github-work-item-mutation-patches.test.ts +++ b/src/renderer/src/components/task-page/github/task-page-github-work-item-mutation-patches.test.ts @@ -115,5 +115,7 @@ describe('loginSetsEqual', () => { loginSetOfUsers([{ login: 'alice', name: null, avatarUrl: '' }]) ) ).toBe(true) + expect(loginSetsEqual(new Set(['alice']), new Set(['bob']))).toBe(false) + expect(loginSetsEqual(new Set(['alice']), new Set(['alice', 'bob']))).toBe(false) }) }) diff --git a/src/renderer/src/components/task-page/linear/task-page-linear-issue-grouping.test.ts b/src/renderer/src/components/task-page/linear/task-page-linear-issue-grouping.test.ts index 09a26826ea6..4daaf047118 100644 --- a/src/renderer/src/components/task-page/linear/task-page-linear-issue-grouping.test.ts +++ b/src/renderer/src/components/task-page/linear/task-page-linear-issue-grouping.test.ts @@ -67,6 +67,10 @@ describe('groupLinearIssues', () => { 'identifier' ) expect(sections.map((section) => section.key)).toEqual(['status:Todo', 'status:Done']) + expect(sections.map((section) => section.issues.map((entry) => entry.identifier))).toEqual([ + ['COR-1'], + ['COR-2'] + ]) }) }) diff --git a/src/renderer/src/lib/agent-session/structured-agent-session-tab-activation.test.ts b/src/renderer/src/lib/agent-session/structured-agent-session-tab-activation.test.ts index 701b66007b5..acad33e8a90 100644 --- a/src/renderer/src/lib/agent-session/structured-agent-session-tab-activation.test.ts +++ b/src/renderer/src/lib/agent-session/structured-agent-session-tab-activation.test.ts @@ -48,15 +48,6 @@ describe('findStructuredAgentSessionTab', () => { createdAt: 0 } satisfies Tab - it('matches the native session identity in its workspace inventory', () => { - expect( - findStructuredAgentSessionTab( - { 'wt-1': [tab] }, - { workspaceId: 'wt-1', sessionId: 'session-1' } - ) - ).toBe(tab) - }) - it('does not match a session or tab from another workspace', () => { expect( findStructuredAgentSessionTab( diff --git a/src/renderer/src/lib/workspace-attachment-observed-origin.test.ts b/src/renderer/src/lib/workspace-attachment-observed-origin.test.ts index cfc843ab742..b637835b5bb 100644 --- a/src/renderer/src/lib/workspace-attachment-observed-origin.test.ts +++ b/src/renderer/src/lib/workspace-attachment-observed-origin.test.ts @@ -132,5 +132,23 @@ describe('reference evidence native chat identity', () => { agent: 'claude', sessionId: 'chat-session' }) + + const renamed = { + ...chat, + unifiedTabsByWorktree: { + [workspaceId]: chat.unifiedTabsByWorktree[workspaceId].map((tab) => ({ + ...tab, + customLabel: 'Pinned chat' + })) + } + } + expect( + getWorkspaceAttachmentObservedOrigin( + renamed, + workspaceId, + { kind: 'agent-session', tabId: 'chat-tab' }, + 'local' + ) + ).toMatchObject({ label: 'Pinned chat' }) }) })