mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 08:37:49 +00:00
test: remove low-value copy and CI fixture checks (#36159)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
@@ -7023,7 +7023,6 @@
|
||||
"src/renderer/src/components/artifacts/ArtifactPreview.test.tsx": 366,
|
||||
"src/renderer/src/components/artifacts/ArtifactPublishButton.test.tsx": 568,
|
||||
"src/renderer/src/components/artifacts/ArtifactsPage.test.tsx": 1464,
|
||||
"src/renderer/src/components/artifacts/artifact-display-labels.test.ts": 114,
|
||||
"src/renderer/src/components/artifacts/artifact-list-search.test.ts": 127,
|
||||
"src/renderer/src/components/artifacts/artifact-list-windowing.test.tsx": 1563,
|
||||
"src/renderer/src/components/artifacts/artifact-publish-flow.test.ts": 80,
|
||||
|
||||
@@ -94,19 +94,6 @@ function makeTree(): { root: string; source: string } {
|
||||
}
|
||||
|
||||
describe('shareTree', () => {
|
||||
// Stub mechanism selection: macOS cp -c and GNU reflink cannot both run on one host.
|
||||
it('prefers the strongest isolation each platform offers', () => {
|
||||
const stub = () =>
|
||||
vi.fn((_source: string, target: string) => mkdirSync(target, { recursive: true }))
|
||||
const stubs = { clone: stub(), reflink: stub(), hardlink: stub() }
|
||||
const { root, source } = makeTree()
|
||||
expect(shareTree(source, path.join(root, 'a'), { platform: 'darwin', ...stubs })).toBe('clone')
|
||||
expect(shareTree(source, path.join(root, 'b'), { platform: 'linux', ...stubs })).toBe('reflink')
|
||||
expect(shareTree(source, path.join(root, 'c'), { platform: 'win32', ...stubs })).toBe(
|
||||
'hardlink'
|
||||
)
|
||||
})
|
||||
|
||||
it('keeps relative symlinks unresolved on whatever this host supports', () => {
|
||||
const { root, source } = makeTree()
|
||||
const destination = path.join(root, 'shared')
|
||||
@@ -318,17 +305,6 @@ describe('copyPrivateTree', () => {
|
||||
expect(readlinkSync(path.join(destination, 'relative-link'))).toBe(path.join('nested', 'file'))
|
||||
})
|
||||
|
||||
it('reports the private mechanism it used', () => {
|
||||
const { root, source } = makeTree()
|
||||
const clone = vi.fn((_source: string, target: string) => mkdirSync(target, { recursive: true }))
|
||||
expect(
|
||||
copyPrivateTree(source, path.join(root, 'private'), { platform: 'darwin', clone })
|
||||
).toEqual({
|
||||
mechanism: 'clone',
|
||||
copyError: null
|
||||
})
|
||||
})
|
||||
|
||||
it('falls back to a byte copy when the private mechanism fails', () => {
|
||||
const { root, source } = makeTree()
|
||||
const destination = path.join(root, 'private')
|
||||
|
||||
@@ -8,7 +8,7 @@ import { WINDOWS_BOUNDARY_TESTS } from './pr-package-test-paths.mjs'
|
||||
|
||||
// Register every Windows-named or suite-gated file in sole Windows lane’s curated list; otherwise Ubuntu self-skips can pass without execution.
|
||||
// Follow suite/describe gates, nested aliases, local boolean polarity and narrowing &&; do not follow case-level gates, imported/function flags, broad OR/skipIf conjunctions.
|
||||
// Proves registration/routing list membership, not actual suite execution (manual env gates can skip), diff-trigger coverage or assertion quality. Literal caps expose allowance growth for review.
|
||||
// Proves registration/routing list membership, not actual suite execution (manual env gates can skip), diff-trigger coverage or assertion quality.
|
||||
|
||||
const projectDir = resolve(import.meta.dirname, '../..')
|
||||
const WINDOWS_LANE_JOB = 'package_windows'
|
||||
@@ -16,7 +16,6 @@ const WINDOWS_LANE_STEPS = [
|
||||
'Test Windows-specific boundaries',
|
||||
'Test Windows installer process probe'
|
||||
]
|
||||
const WINDOWS_LANE_RUNNER = 'windows-2022'
|
||||
|
||||
// Shrink-only pre-existing Windows files missing lane registration; remove entry when registered, never add.
|
||||
const UNREGISTERED_ON_MAIN = [
|
||||
@@ -59,19 +58,11 @@ const MANUAL_OPT_IN = [
|
||||
'src/main/wsl/wsl-runner.wsl.test.ts'
|
||||
]
|
||||
|
||||
/** Caps so growing either list is two deliberate edits, not one. */
|
||||
const UNREGISTERED_MAX = 8
|
||||
const MANUAL_OPT_IN_MAX = 9
|
||||
|
||||
// Pin population floor against broken discovery; lower only when real gated file is deleted.
|
||||
const GATED_FILE_FLOOR = 23
|
||||
|
||||
const TEST_FILE_PATTERN = /\.(?:test|spec)\.(?:ts|tsx|mjs|cjs|js)$/
|
||||
|
||||
// Exclude mobile: separate Vitest job never triggers desktop package_windows lane.
|
||||
const UNREACHABLE_BY_THE_WINDOWS_LANE = 'mobile/'
|
||||
|
||||
// Exempt exact self path because matcher fixtures quote gates; guard must run on Ubuntu and must not exempt real sibling suites.
|
||||
const SCANNER_SELF_PATH = 'config/scripts/win32-test-lane-registration.test.mjs'
|
||||
|
||||
export function isScannerSelfPath(path) {
|
||||
@@ -118,7 +109,6 @@ function buildGates(source) {
|
||||
]
|
||||
}
|
||||
|
||||
/** Exported shape of the rule, so the fixtures below exercise the real matcher. */
|
||||
export function isWindows32GatedTestFile(path, source) {
|
||||
if (/\.win32\.(?:test|spec)\./.test(path)) {
|
||||
return true
|
||||
@@ -149,22 +139,9 @@ export function requiresEnvOptIn(source) {
|
||||
})
|
||||
}
|
||||
|
||||
// Treat any runs-on label/array/group/matrix that may be Windows as a lane; unresolvable expression fails closed for review.
|
||||
export function couldRunOnWindows(runsOn) {
|
||||
const labels =
|
||||
typeof runsOn === 'string'
|
||||
? [runsOn]
|
||||
: Array.isArray(runsOn)
|
||||
? runsOn
|
||||
: [...(runsOn?.labels ?? []), runsOn?.group ?? ''].flat()
|
||||
return labels.some((label) => /windows/i.test(String(label)) || String(label).includes('${{'))
|
||||
}
|
||||
|
||||
/** The Windows job's curated files: the boundary list plus any argv the lane steps spell out. */
|
||||
function readWindowsWorkflow() {
|
||||
const workflow = parse(readFileSync(join(projectDir, '.github/workflows/pr.yml'), 'utf8'))
|
||||
const jobs = Object.entries(workflow.jobs ?? {})
|
||||
const windowsJobs = jobs.filter(([, job]) => couldRunOnWindows(job?.['runs-on']))
|
||||
const steps = workflow.jobs?.[WINDOWS_LANE_JOB]?.steps ?? []
|
||||
const runs = WINDOWS_LANE_STEPS.map((name) => {
|
||||
const step = steps.find((candidate) => candidate?.name === name)
|
||||
@@ -184,7 +161,6 @@ function readWindowsWorkflow() {
|
||||
}
|
||||
const run = runs.join(' ')
|
||||
return {
|
||||
windowsJobNames: windowsJobs.map(([name]) => name),
|
||||
laneFiles: [
|
||||
...WINDOWS_BOUNDARY_TESTS,
|
||||
...run.split(/\s+/).filter((token) => TEST_FILE_PATTERN.test(token))
|
||||
@@ -192,7 +168,7 @@ function readWindowsWorkflow() {
|
||||
}
|
||||
}
|
||||
|
||||
const { windowsJobNames, laneFiles } = readWindowsWorkflow()
|
||||
const { laneFiles } = readWindowsWorkflow()
|
||||
const scannedTestFiles = scanSourceTree(projectDir, {
|
||||
includeTests: true,
|
||||
extensions: TEST_FILE_PATTERN
|
||||
@@ -239,19 +215,6 @@ describe('Windows-gated test files are registered in the Windows CI lane', () =>
|
||||
)
|
||||
})
|
||||
|
||||
it('scans a plausible number of test files', () => {
|
||||
// A broken root or extension filter would make every assertion below vacuous.
|
||||
expect(scannedTestFiles.length).toBeGreaterThan(5000)
|
||||
})
|
||||
|
||||
it('has exactly one windows-2022 job to register into', () => {
|
||||
// Require sole Windows lane so curated registration cannot point at wrong job and silently skip.
|
||||
expect(
|
||||
windowsJobNames,
|
||||
`Expected only ${WINDOWS_LANE_JOB} to run on ${WINDOWS_LANE_RUNNER}.`
|
||||
).toEqual([WINDOWS_LANE_JOB])
|
||||
})
|
||||
|
||||
it('parses a plausible Windows lane invocation', () => {
|
||||
expect(laneFiles.length).toBeGreaterThan(15)
|
||||
const missingFromDisk = laneFiles.filter((path) => {
|
||||
@@ -267,41 +230,6 @@ describe('Windows-gated test files are registered in the Windows CI lane', () =>
|
||||
).toEqual([])
|
||||
})
|
||||
|
||||
it('rediscovers Windows-gated files that are already registered', () => {
|
||||
// Prove both filename/alias and gate-only discovery against real files, not just fixtures.
|
||||
expect(gatedFiles).toContain(
|
||||
'src/shared/__tests__/process-host/windows-command-line.win32.test.ts'
|
||||
)
|
||||
expect(gatedFiles).toContain('src/main/agent-hooks/windows-hook-payload-delivery.test.ts')
|
||||
// And a compound gate, the case this guard was blind to at first.
|
||||
expect(gatedFiles).toContain('src/main/git/runner-wsl-linked-gitdir-windows.test.ts')
|
||||
})
|
||||
|
||||
it('exempts itself, and nothing else, from the scan', () => {
|
||||
expect(scannedTestFiles.map(({ relativePath }) => relativePath)).toContain(SCANNER_SELF_PATH)
|
||||
// The exemption is load-bearing only while the fixtures below still match.
|
||||
expect(isWindows32GatedTestFile(SCANNER_SELF_PATH, sourceOf(SCANNER_SELF_PATH))).toBe(true)
|
||||
expect(gatedFiles).not.toContain(SCANNER_SELF_PATH)
|
||||
// The other half of the claim: no sibling rides the exemption.
|
||||
expect(isScannerSelfPath('config/scripts/pr-code-change-scope.test.mjs')).toBe(false)
|
||||
})
|
||||
|
||||
it('holds the Windows-gated population at or above the floor', () => {
|
||||
// Keep literal population floor independent of debt lengths; derived bound moves with lists and proves nothing.
|
||||
expect(
|
||||
gatedFiles.length,
|
||||
`Found ${gatedFiles.length} Windows-gated test files; the floor is ${GATED_FILE_FLOOR}. ` +
|
||||
'A drop means the scan stopped matching, not that the files went away. Lower the floor ' +
|
||||
'only for a genuine deletion.'
|
||||
).toBeGreaterThanOrEqual(GATED_FILE_FLOOR)
|
||||
})
|
||||
|
||||
it('confirms the classifier distinguishes registered from unregistered paths', () => {
|
||||
// Reject classifier answering true for all paths or registration assertions become vacuous.
|
||||
expect(isInClassifier('src/main/windows/windows-pty-job.win32.test.ts')).toBe(true)
|
||||
expect(isInClassifier('src/main/windows/not-a-real-file.win32.test.ts')).toBe(false)
|
||||
})
|
||||
|
||||
it('has every Windows-gated test file registered in the Windows lane', () => {
|
||||
const grandfathered = new Set([...UNREGISTERED_ON_MAIN, ...MANUAL_OPT_IN])
|
||||
const failures = gatedFiles
|
||||
@@ -327,17 +255,6 @@ describe('Windows-gated test files are registered in the Windows CI lane', () =>
|
||||
).toEqual([])
|
||||
})
|
||||
|
||||
it('caps growth of both grandfathered lists', () => {
|
||||
expect(
|
||||
UNREGISTERED_ON_MAIN.length,
|
||||
'Never raise UNREGISTERED_MAX. Register the file instead.'
|
||||
).toBeLessThanOrEqual(UNREGISTERED_MAX)
|
||||
expect(
|
||||
MANUAL_OPT_IN.length,
|
||||
'Never raise MANUAL_OPT_IN_MAX to avoid registering a file that CI could actually run.'
|
||||
).toBeLessThanOrEqual(MANUAL_OPT_IN_MAX)
|
||||
})
|
||||
|
||||
it('keeps MANUAL_OPT_IN to suites CI genuinely cannot run', () => {
|
||||
// Otherwise this list is just a quieter way to skip registration.
|
||||
const notActuallyOptIn = MANUAL_OPT_IN.filter((path) => !requiresEnvOptIn(sourceOf(path)))
|
||||
@@ -357,193 +274,3 @@ describe('Windows-gated test files are registered in the Windows CI lane', () =>
|
||||
).toEqual([])
|
||||
})
|
||||
})
|
||||
|
||||
describe('manual opt-in classification', () => {
|
||||
it('requires the env read to reach the gate', () => {
|
||||
// Unrelated env use must not exempt CI-satisfiable compound gate (native-addon-bytes shape).
|
||||
expect(
|
||||
requiresEnvOptIn(
|
||||
"const tmp = process.env.RUNNER_TEMP\ndescribe.runIf(process.platform === 'win32' && hasAddon)('x', () => {})"
|
||||
)
|
||||
).toBe(false)
|
||||
// One const hop matches real manual opt-in gates.
|
||||
expect(
|
||||
requiresEnvOptIn(
|
||||
"const distro = process.env.ORCA_TEST_WSL_DISTRO\ndescribe.runIf(process.platform === 'win32' && Boolean(distro))('x', () => {})"
|
||||
)
|
||||
).toBe(true)
|
||||
// Read inline in the conjunct: the other real shape.
|
||||
expect(
|
||||
requiresEnvOptIn(
|
||||
"const RUN = process.platform === 'win32' && process.env.ORCA_REAL_X === '1'"
|
||||
)
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('requires the gate to be compound at all', () => {
|
||||
// Bare win32 gate stays registrable despite unrelated env reads.
|
||||
expect(
|
||||
requiresEnvOptIn(
|
||||
"const t = process.env.CI\ndescribe.runIf(process.platform === 'win32')('x', () => {})"
|
||||
)
|
||||
).toBe(false)
|
||||
// Require && before env conjunct; same-line env read after bare gate must not reopen manual-parking hole.
|
||||
expect(
|
||||
requiresEnvOptIn(
|
||||
"describe.runIf(process.platform === 'win32')(`x ${process.env.ORCA_TAG}`, () => {})"
|
||||
)
|
||||
).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('Windows runner detection', () => {
|
||||
it('reads every runs-on spelling that could land on Windows', () => {
|
||||
expect(couldRunOnWindows('windows-2022')).toBe(true)
|
||||
// The spelling that would have slipped past an equality test.
|
||||
expect(couldRunOnWindows('windows-latest')).toBe(true)
|
||||
expect(couldRunOnWindows(['self-hosted', 'Windows', 'X64'])).toBe(true)
|
||||
expect(couldRunOnWindows({ group: 'windows-runners', labels: ['x64'] })).toBe(true)
|
||||
// Unresolvable from the file, so it fails closed rather than reading as safe.
|
||||
expect(couldRunOnWindows('${{ matrix.os }}')).toBe(true)
|
||||
expect(couldRunOnWindows('ubuntu-latest')).toBe(false)
|
||||
expect(couldRunOnWindows(['self-hosted', 'linux'])).toBe(false)
|
||||
expect(couldRunOnWindows(undefined)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('Windows-gate detection', () => {
|
||||
// Pair actual gate shapes with near misses so fixtures test repository semantics rather than echo regex.
|
||||
const cases = [
|
||||
[
|
||||
'describe.runIf equality',
|
||||
"describe.runIf(process.platform === 'win32')('x', () => {})",
|
||||
"describe.runIf(process.platform !== 'win32')('x', () => {})"
|
||||
],
|
||||
[
|
||||
'describe.skipIf inequality',
|
||||
"describe.skipIf(process.platform !== 'win32')('x', () => {})",
|
||||
"describe.skipIf(process.platform === 'win32')('x', () => {})"
|
||||
],
|
||||
[
|
||||
'ternary describe alias',
|
||||
"const d = process.platform === 'win32' ? describe : describe.skip",
|
||||
"const d = process.platform === 'win32' ? describe.skip : describe"
|
||||
],
|
||||
[
|
||||
'inverted ternary describe alias',
|
||||
"const d = process.platform !== 'win32' ? describe.skip : describe",
|
||||
"const d = process.platform !== 'win32' ? describe : describe.skip"
|
||||
],
|
||||
[
|
||||
'local isWindows flag',
|
||||
"const isWindows = process.platform === 'win32'\ndescribe.skipIf(!isWindows)('x', () => {})",
|
||||
"const isWindows = process.platform === 'win32'\ndescribe.skipIf(isWindows)('x', () => {})"
|
||||
],
|
||||
[
|
||||
'local isWindows flag, runIf',
|
||||
"const isWindows = process.platform === 'win32'\ndescribe.runIf(isWindows)('x', () => {})",
|
||||
"const isWindows = process.platform === 'win32'\ndescribe.runIf(!isWindows)('x', () => {})"
|
||||
],
|
||||
[
|
||||
// The blocking miss: a second conjunct made the gate invisible.
|
||||
'compound gate with a second conjunct',
|
||||
"describe.runIf(process.platform === 'win32' && Boolean(distro))('x', () => {})",
|
||||
"describe.runIf(process.platform === 'win32' || Boolean(distro))('x', () => {})"
|
||||
],
|
||||
[
|
||||
'compound gate behind a named flag assigned on the next line',
|
||||
"const RUN_REAL =\n process.platform === 'win32' && process.env.X === '1'\ndescribe.runIf(RUN_REAL)('x', () => {})",
|
||||
"const RUN_REAL =\n process.platform !== 'win32' && process.env.X === '1'\ndescribe.runIf(RUN_REAL)('x', () => {})"
|
||||
],
|
||||
[
|
||||
'named flag driving a ternary suite alias',
|
||||
"const enabled = process.platform === 'win32' && process.env.X === '1'\nconst d = enabled ? describe : describe.skip",
|
||||
"const enabled = process.platform === 'win32' && process.env.X === '1'\nconst d = enabled ? describe.skip : describe"
|
||||
],
|
||||
[
|
||||
'compound skipIf widened with ||',
|
||||
"describe.skipIf(process.platform !== 'win32' || !hasAddon)('x', () => {})",
|
||||
"describe.skipIf(process.platform !== 'win32' && !hasAddon)('x', () => {})"
|
||||
],
|
||||
[
|
||||
'double-quoted and loosely spaced',
|
||||
'describe . runIf ( process.platform === "win32" )("x", () => {})',
|
||||
'describe . runIf ( process.platform === "darwin" )("x", () => {})'
|
||||
]
|
||||
]
|
||||
|
||||
for (const [label, gated, nearMiss] of cases) {
|
||||
it(`detects ${label} and rejects its near miss`, () => {
|
||||
expect(isWindows32GatedTestFile('src/x/sample.test.ts', gated)).toBe(true)
|
||||
expect(isWindows32GatedTestFile('src/x/sample.test.ts', nearMiss)).toBe(false)
|
||||
})
|
||||
}
|
||||
|
||||
it('detects the .win32 filename with no gate expression at all', () => {
|
||||
expect(isWindows32GatedTestFile('src/x/sample.win32.test.ts', 'describe("x", () => {})')).toBe(
|
||||
true
|
||||
)
|
||||
// Near miss: `.win32.ts` is production source, not a test the lane can run.
|
||||
expect(isWindows32GatedTestFile('src/x/sample.win32.ts', 'export const x = 1')).toBe(false)
|
||||
})
|
||||
|
||||
it('does not read a flag whose name merely starts the same', () => {
|
||||
// Without word boundaries `isWindows` would swallow `isWindowsHost`.
|
||||
expect(
|
||||
isWindows32GatedTestFile(
|
||||
'src/x/sample.test.ts',
|
||||
"const isWindows = process.platform === 'win32'\ndescribe.runIf(isWindowsHost)('x', () => {})"
|
||||
)
|
||||
).toBe(false)
|
||||
})
|
||||
|
||||
it('rejects the documented blind spots rather than half-detecting them', () => {
|
||||
// it-level gate inside a cross-platform suite: out of scope by design.
|
||||
expect(
|
||||
isWindows32GatedTestFile(
|
||||
'src/x/sample.test.ts',
|
||||
"describe('x', () => { it.skipIf(process.platform !== 'win32')('y', () => {}) })"
|
||||
)
|
||||
).toBe(false)
|
||||
// A platform branch inside a test body is not a gate.
|
||||
expect(
|
||||
isWindows32GatedTestFile(
|
||||
'src/x/sample.test.ts',
|
||||
"it('x', () => { if (process.platform === 'win32') { return } })"
|
||||
)
|
||||
).toBe(false)
|
||||
// An imported flag: the assignment is not in this file, so polarity is unknowable.
|
||||
expect(
|
||||
isWindows32GatedTestFile(
|
||||
'src/x/sample.test.ts',
|
||||
"import { isWindows } from './f'\ndescribe.runIf(isWindows)('x', () => {})"
|
||||
)
|
||||
).toBe(false)
|
||||
})
|
||||
|
||||
it('does not treat a widening conjunct behind a named flag as Windows-only', () => {
|
||||
// Falsy-on-win32 flag with && also runs POSIX when second term is false; preserve literal/alias equivalence instead of widening to Windows-only.
|
||||
expect(
|
||||
isWindows32GatedTestFile(
|
||||
'src/x/sample.test.ts',
|
||||
"const p = process.platform !== 'win32' && Boolean(x)\ndescribe.skipIf(p)('x', () => {})"
|
||||
)
|
||||
).toBe(false)
|
||||
// The narrowing direction still counts: `=== 'win32' && x` is Windows-only.
|
||||
expect(
|
||||
isWindows32GatedTestFile(
|
||||
'src/x/sample.test.ts',
|
||||
"const p = process.platform === 'win32' && Boolean(x)\ndescribe.runIf(p)('x', () => {})"
|
||||
)
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('ignores a gate that only appears in prose', () => {
|
||||
expect(
|
||||
isWindows32GatedTestFile(
|
||||
'src/x/sample.test.ts',
|
||||
"// describe.runIf(process.platform === 'win32')\ndescribe('x', () => {})"
|
||||
)
|
||||
).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
workspaceOptionsAccessibilityLabel,
|
||||
workspaceOptionsBadgeText
|
||||
} from './workspace-options-copy'
|
||||
|
||||
describe('the Workspace options chip', () => {
|
||||
it('badges the active-filter count as the desktop does, capped at 9+', () => {
|
||||
expect(workspaceOptionsBadgeText(0)).toBeNull()
|
||||
expect(workspaceOptionsBadgeText(2)).toBe('2')
|
||||
expect(workspaceOptionsBadgeText(9)).toBe('9')
|
||||
expect(workspaceOptionsBadgeText(10)).toBe('9+')
|
||||
expect(workspaceOptionsBadgeText(12)).toBe('9+')
|
||||
})
|
||||
|
||||
it("reads to screen readers as the desktop's trigger does", () => {
|
||||
expect(workspaceOptionsAccessibilityLabel(0)).toBe('Workspace options')
|
||||
expect(workspaceOptionsAccessibilityLabel(1)).toBe('Workspace options (1 filter active)')
|
||||
expect(workspaceOptionsAccessibilityLabel(3)).toBe('Workspace options (3 filters active)')
|
||||
})
|
||||
})
|
||||
@@ -117,22 +117,6 @@ describe('getWorktreeHostContextLabels', () => {
|
||||
expect(otherHostUnhealthy[0].hostContextLabel).toBeUndefined()
|
||||
})
|
||||
|
||||
it('names every row by host once the list spans hosts', () => {
|
||||
const rows = [
|
||||
worktree({ hostId: 'local', worktreeId: 'a' }),
|
||||
worktree({ hostId: sshHostId, worktreeId: 'b' }),
|
||||
worktree({ hostId: 'ssh:unlabeled', worktreeId: 'c' }),
|
||||
worktree({ hostId: 'runtime:env-1', worktreeId: 'd' })
|
||||
]
|
||||
const labeled = applyWorktreeHostContextLabels(rows, sources)
|
||||
expect(labeled.map((row) => row.hostContextLabel)).toEqual([
|
||||
'Local Mac',
|
||||
'openclaw',
|
||||
'unlabeled',
|
||||
'env-1'
|
||||
])
|
||||
})
|
||||
|
||||
it('adds the health word only for hosts the desktop reports as unhealthy', () => {
|
||||
const rows = [
|
||||
worktree({ hostId: 'local', worktreeId: 'a' }),
|
||||
|
||||
@@ -9,14 +9,8 @@ vi.mock('electron', () => ({
|
||||
}))
|
||||
|
||||
import {
|
||||
UI_LANGUAGE_CHINESE,
|
||||
UI_LANGUAGE_CHINESE_TRADITIONAL,
|
||||
UI_LANGUAGE_ENGLISH,
|
||||
UI_LANGUAGE_GERMAN,
|
||||
UI_LANGUAGE_JAPANESE,
|
||||
UI_LANGUAGE_KOREAN,
|
||||
UI_LANGUAGE_PORTUGUESE,
|
||||
UI_LANGUAGE_RUSSIAN,
|
||||
UI_LANGUAGE_SPANISH
|
||||
} from '../../shared/ui-language'
|
||||
import { mainI18n, setMainPluginLanguagePacks, setMainUiLanguage, translateMain } from './main-i18n'
|
||||
@@ -29,12 +23,6 @@ describe('main-i18n lazy locale loading', () => {
|
||||
await setMainUiLanguage(UI_LANGUAGE_ENGLISH)
|
||||
})
|
||||
|
||||
it('serves English synchronously from caller fallbacks', () => {
|
||||
expect(translateMain('missing.main.key', 'Fallback copy')).toBe('Fallback copy')
|
||||
expect(translateMain('menu.file', 'File')).toBe('File')
|
||||
expect(translateMain('menu.settings', 'Settings')).toBe('Settings')
|
||||
})
|
||||
|
||||
it('lazy-loads the Spanish catalog before changeLanguage resolves', async () => {
|
||||
const locale = await setMainUiLanguage(UI_LANGUAGE_SPANISH)
|
||||
expect(locale).toBe('es')
|
||||
@@ -43,54 +31,6 @@ describe('main-i18n lazy locale loading', () => {
|
||||
expect(translateMain('menu.settings', 'Settings')).toBe('Ajustes')
|
||||
})
|
||||
|
||||
it('lazy-loads each remaining locale on demand', async () => {
|
||||
await setMainUiLanguage(UI_LANGUAGE_JAPANESE)
|
||||
expect(translateMain('menu.file', 'File')).not.toBe('File')
|
||||
|
||||
await setMainUiLanguage(UI_LANGUAGE_KOREAN)
|
||||
expect(translateMain('menu.file', 'File')).not.toBe('File')
|
||||
|
||||
await setMainUiLanguage(UI_LANGUAGE_CHINESE)
|
||||
expect(translateMain('menu.file', 'File')).not.toBe('File')
|
||||
|
||||
await setMainUiLanguage(UI_LANGUAGE_PORTUGUESE)
|
||||
expect(translateMain('menu.file', 'File')).not.toBe('File')
|
||||
|
||||
await setMainUiLanguage(UI_LANGUAGE_CHINESE_TRADITIONAL)
|
||||
expect(translateMain('menu.file', 'File')).not.toBe('File')
|
||||
})
|
||||
|
||||
it('uses caller English when a target catalog omits a key', async () => {
|
||||
await setMainUiLanguage(UI_LANGUAGE_SPANISH)
|
||||
expect(translateMain('missing.main.feature', 'English fallback')).toBe('English fallback')
|
||||
})
|
||||
|
||||
it('loads Russian before native menu registration reads it', async () => {
|
||||
expect(await setMainUiLanguage(UI_LANGUAGE_RUSSIAN)).toBe('ru')
|
||||
expect(translateMain('menu.file', 'File')).toBe('Файл')
|
||||
expect(translateMain('menu.settings', 'Settings')).toBe('Настройки')
|
||||
for (const [count, expected] of [
|
||||
[3, '3 активных терминала переподключатся после перезапуска.'],
|
||||
[5, '5 активных терминалов переподключатся после перезапуска.']
|
||||
] as const) {
|
||||
expect(
|
||||
translateMain(
|
||||
'localBuild.liveTerminals',
|
||||
'{{count}} live terminals will reconnect after restart.',
|
||||
{ count, defaultValue_one: '{{count}} live terminal will reconnect after restart.' }
|
||||
)
|
||||
).toBe(expected)
|
||||
}
|
||||
})
|
||||
|
||||
it('returns to English from a lazily-loaded locale', async () => {
|
||||
await setMainUiLanguage(UI_LANGUAGE_SPANISH)
|
||||
expect(translateMain('menu.file', 'File')).toBe('Archivo')
|
||||
|
||||
await setMainUiLanguage(UI_LANGUAGE_ENGLISH)
|
||||
expect(translateMain('menu.file', 'File')).toBe('File')
|
||||
})
|
||||
|
||||
it('keeps a newer explicit language when system fallback catalog loading finishes late', async () => {
|
||||
const { getSystemLanguageDetection } = await import('./system-language')
|
||||
expect(getSystemLanguageDetection(['fr-FR'])?.locale).toBe('fr')
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import es from '../../renderer/src/i18n/locales/es.json'
|
||||
import { UI_LANGUAGE_ENGLISH, UI_LANGUAGE_SPANISH } from '../../shared/ui-language'
|
||||
import { setMainUiLanguage, translateMain } from '../i18n/main-i18n'
|
||||
import { formatProfileStateStartupFailure } from '../persistence/profile-state/profile-state-startup-failure'
|
||||
|
||||
vi.mock('electron', () => ({ app: { getLocale: () => 'en-US' } }))
|
||||
|
||||
afterEach(async () => {
|
||||
await setMainUiLanguage(UI_LANGUAGE_ENGLISH)
|
||||
})
|
||||
|
||||
describe('profile-state startup failure localization', () => {
|
||||
it('reads dialog copy from the active catalog and keeps commands verbatim', async () => {
|
||||
await setMainUiLanguage(UI_LANGUAGE_SPANISH)
|
||||
expect(formatProfileStateStartupFailure({ code: 'newer-schema' }, translateMain)).toBe(
|
||||
es.profileState.startupFailure.newerSchema
|
||||
)
|
||||
const repair = formatProfileStateStartupFailure(
|
||||
{
|
||||
code: 'profile-state-recovery-required',
|
||||
dataFile: '/profile/orca-data.json',
|
||||
databaseFile: '/profile/profile-state.db',
|
||||
exportPaths: [],
|
||||
profileId: 'profile-a',
|
||||
repairableSections: [{ domain: 'settings' }]
|
||||
},
|
||||
translateMain
|
||||
)
|
||||
expect(repair).toContain(es.profileState.section.settings)
|
||||
expect(repair).toContain('`orca profile state rollback --repair --profile-id profile-a`')
|
||||
// Logs and the CLI keep English when no translator is passed.
|
||||
expect(formatProfileStateStartupFailure({ code: 'newer-schema' })).toContain(
|
||||
'newer version of Orca'
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -1,48 +0,0 @@
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { ArtifactListItem } from '../../../../shared/artifacts'
|
||||
import {
|
||||
artifactTypeLabel,
|
||||
formatArtifactExpiryCompact,
|
||||
formatArtifactUpdatedCompact
|
||||
} from './artifact-display-labels'
|
||||
|
||||
function item(sourceContentType: string): ArtifactListItem {
|
||||
return {
|
||||
artifact: {
|
||||
version: 1,
|
||||
slug: 'doc',
|
||||
title: 'Doc',
|
||||
originalFileName: 'doc.md',
|
||||
sourceContentType,
|
||||
renderedContentType: 'text/html',
|
||||
createdAt: '2026-08-01T12:00:00.000Z',
|
||||
updatedAt: '2026-08-02T12:00:00.000Z',
|
||||
expiresAt: '2026-09-01T12:00:00.000Z',
|
||||
byteSize: 1,
|
||||
deletedAt: null
|
||||
},
|
||||
shareUrl: 'https://share.onorca.dev/a/doc'
|
||||
}
|
||||
}
|
||||
|
||||
describe('artifact display labels', () => {
|
||||
afterEach(() => {
|
||||
vi.useRealTimers()
|
||||
})
|
||||
|
||||
it('labels known artifact types', () => {
|
||||
expect(artifactTypeLabel(item('text/html'))).toBe('HTML')
|
||||
expect(artifactTypeLabel(item('text/markdown'))).toBe('Markdown')
|
||||
expect(artifactTypeLabel(item('application/pdf'))).toBe('application/pdf')
|
||||
})
|
||||
|
||||
it('uses compact relative times for table cells', () => {
|
||||
vi.useFakeTimers()
|
||||
vi.setSystemTime(new Date('2026-08-10T12:00:00.000Z'))
|
||||
expect(formatArtifactUpdatedCompact('2026-08-08T12:00:00.000Z')).toBe('2 days ago')
|
||||
expect(formatArtifactUpdatedCompact('not-a-date')).toBe('recently')
|
||||
expect(formatArtifactExpiryCompact('2026-08-20T12:00:00.000Z')).toBe('in 10 days')
|
||||
expect(formatArtifactExpiryCompact('2026-08-01T12:00:00.000Z')).toBe('Expired')
|
||||
expect(formatArtifactExpiryCompact('not-a-date')).toBe('Expiry unknown')
|
||||
})
|
||||
})
|
||||
@@ -1,23 +0,0 @@
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
import { i18n } from '@/i18n/i18n'
|
||||
import es from '@/i18n/locales/es.json'
|
||||
import { ephemeralVmFailure } from '../../../shared/ephemeral-vm-failure-copy'
|
||||
import { ephemeralVmErrorText } from './ephemeral-vm-error-copy'
|
||||
|
||||
const originalLanguage = i18n.language
|
||||
|
||||
afterEach(async () => {
|
||||
await i18n.changeLanguage(originalLanguage)
|
||||
})
|
||||
|
||||
describe('ephemeralVmErrorText', () => {
|
||||
it('localizes a coded failure and keeps the interpolated id verbatim', async () => {
|
||||
i18n.addResourceBundle('es', 'translation', es)
|
||||
await i18n.changeLanguage('es')
|
||||
const missing = ephemeralVmFailure('recipeNotFound', { recipeId: 'cloud-{{ref}}' })
|
||||
expect(missing.error).toBe('Recipe not found: cloud-{{ref}}')
|
||||
expect(ephemeralVmErrorText(missing)).toBe(
|
||||
es.ephemeralVm.errors.recipeNotFound.replace('{{recipeId}}', 'cloud-{{ref}}')
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -1,29 +1,10 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
getLocalhostWorktreeHostLabel,
|
||||
getLocalhostWorktreeRouteKey,
|
||||
slugifyLocalhostWorktreeLabel
|
||||
getLocalhostWorktreeRouteKey
|
||||
} from './localhost-worktree-labels'
|
||||
|
||||
describe('localhost worktree labels', () => {
|
||||
it('uses project-main for the primary worktree', () => {
|
||||
expect(
|
||||
getLocalhostWorktreeHostLabel({
|
||||
projectName: 'Snap Studio',
|
||||
worktreeName: 'main'
|
||||
})
|
||||
).toBe('snap-studio-main')
|
||||
})
|
||||
|
||||
it('keeps non-main worktree labels short', () => {
|
||||
expect(
|
||||
getLocalhostWorktreeHostLabel({
|
||||
projectName: 'Snap Studio',
|
||||
worktreeName: 'analytics'
|
||||
})
|
||||
).toBe('analytics')
|
||||
})
|
||||
|
||||
it('uses the worktree folder name over branch owner prefixes for non-main labels', () => {
|
||||
expect(
|
||||
getLocalhostWorktreeHostLabel({
|
||||
@@ -34,10 +15,6 @@ describe('localhost worktree labels', () => {
|
||||
).toBe('ui-auth')
|
||||
})
|
||||
|
||||
it('normalizes labels for localhost hostnames', () => {
|
||||
expect(slugifyLocalhostWorktreeLabel(' Drive DB Mismatch! ')).toBe('drive-db-mismatch')
|
||||
})
|
||||
|
||||
it('keeps route keys unique for different ports in the same worktree', () => {
|
||||
const route = {
|
||||
projectName: 'Snap Studio',
|
||||
|
||||
@@ -32,17 +32,6 @@ describe('getRepoDisplayLabelsByPath', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('scopes labels by execution host so same-path repos on different hosts do not collide', () => {
|
||||
// SSH folder repos omit executionHostId; connectionId must still separate them from local same-path repos.
|
||||
const localRepo = { path: '/Users/alice', displayName: 'alice' }
|
||||
const sshRepo = { path: '/Users/alice', displayName: 'alice-prod', connectionId: 'prod-ssh' }
|
||||
const labels = getRepoDisplayLabelsByPath([localRepo, sshRepo])
|
||||
|
||||
expect(labels.get(getRepoDisplayLabelKey(localRepo))).toBe('alice')
|
||||
expect(labels.get(getRepoDisplayLabelKey(sshRepo))).toBe('alice-prod')
|
||||
expect(labels.size).toBe(2)
|
||||
})
|
||||
|
||||
it('keeps cross-host repos with identical path AND name as separate entries', () => {
|
||||
const localRepo = { path: '/Users/alice', displayName: 'home' }
|
||||
const sshRepo = { path: '/Users/alice', displayName: 'home', connectionId: 'prod-ssh' }
|
||||
@@ -54,20 +43,6 @@ describe('getRepoDisplayLabelsByPath', () => {
|
||||
expect(labels.get(getRepoDisplayLabelKey(sshRepo))).toBe('home')
|
||||
})
|
||||
|
||||
it('normalizes Windows separators to slash display labels', () => {
|
||||
const labels = getRepoDisplayLabelsByPath([
|
||||
{ path: 'C:\\workspace\\payments\\api', displayName: 'api' },
|
||||
{ path: 'C:\\workspace\\billing\\api', displayName: 'api' }
|
||||
])
|
||||
|
||||
expect(labels.get(getRepoDisplayLabelKey({ path: 'C:\\workspace\\payments\\api' }))).toBe(
|
||||
'payments/api'
|
||||
)
|
||||
expect(labels.get(getRepoDisplayLabelKey({ path: 'C:\\workspace\\billing\\api' }))).toBe(
|
||||
'billing/api'
|
||||
)
|
||||
})
|
||||
|
||||
it('disambiguates collisions only within their owning host, even when paths overlap', () => {
|
||||
const local = { path: '/workspace/payments/api', displayName: 'api' }
|
||||
const sibling = { path: '/workspace/billing/api', displayName: 'api' }
|
||||
|
||||
Reference in New Issue
Block a user