mirror of
https://github.com/stablyai/orca.git
synced 2026-11-14 01:47:24 +00:00
test: remove low-value mobile and example coverage (#42087)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
@@ -21,14 +21,3 @@ test('adds a task from the input and counts it as open', async () => {
|
||||
assert.equal(stored.length, 3)
|
||||
assert.deepEqual(panel.errors, [])
|
||||
})
|
||||
|
||||
test('starts from the persisted list and clears done tasks', async () => {
|
||||
panel = await mount(panelModule, {
|
||||
persisted: { plugin: { todos: [{ id: 7, title: 'Ship it', done: true }] } }
|
||||
})
|
||||
assert.equal(panel.find({ role: 'status' })?.text, '0 open')
|
||||
|
||||
await panel.press({ text: 'Clear done' })
|
||||
|
||||
assert.ok(panel.find({ label: 'Nothing to do' }))
|
||||
})
|
||||
|
||||
@@ -2,7 +2,6 @@ import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { parseCompilerPass } from './check-node-tests-typecheck.mjs'
|
||||
import {
|
||||
checkTransfers,
|
||||
hasTsNocheckDirective,
|
||||
parseFailingFiles,
|
||||
parseProgramTestFiles
|
||||
@@ -17,18 +16,6 @@ vi.mock('./mobile-typecheck-compiler.mjs', () => ({
|
||||
})
|
||||
}))
|
||||
|
||||
const transfers = [
|
||||
'src/components/pr-sidebar/reviewer-picker.target-contract.test.tsx',
|
||||
'src/test-support/rpc-recording/recorded-request-params.test.ts'
|
||||
]
|
||||
const cleanTransfers = () => ({
|
||||
ok: true,
|
||||
expected: transfers,
|
||||
programTestFiles: transfers,
|
||||
failures: [],
|
||||
problems: []
|
||||
})
|
||||
|
||||
describe('the failing-file parser', () => {
|
||||
// Normalize Windows separators or every baseline entry appears stale and added.
|
||||
it("normalises the Windows separators tsc prints to the baseline's POSIX ones", () => {
|
||||
@@ -58,24 +45,11 @@ describe('the @ts-nocheck guard', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('checked transfers', () => {
|
||||
it('rejects a declared checked transfer missing from the configured roots', () => {
|
||||
expect(
|
||||
checkTransfers(
|
||||
{ ok: true, expected: [], programTestFiles: [], failures: [], problems: [] },
|
||||
new Map([['checked.test.ts', 'Checked transfer: strict Node program']])
|
||||
)
|
||||
).toEqual(['checked transfer is absent from tsconfig.test.node.json: checked.test.ts'])
|
||||
})
|
||||
const allowed = new Map(transfers.map((file) => [file, 'checked transfer']))
|
||||
|
||||
it('never reads a failed declaration producer or final compiler as clean output', () => {
|
||||
describe('compiler pass failures', () => {
|
||||
it('rejects nonzero exits and diagnostics returned with a zero exit', () => {
|
||||
expect(parseCompilerPass('/repo/mobile/src/a.test.ts\n', 2).ok).toBe(false)
|
||||
expect(
|
||||
parseCompilerPass('/repo/mobile/src/a.test.ts\nsrc/a.test.ts(1,1): error TS2322: x\n', 0).ok
|
||||
).toBe(false)
|
||||
expect(checkTransfers({ ...cleanTransfers(), ok: false }, allowed)).toEqual([
|
||||
'the phone/host contract program did not pass'
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -62,11 +62,5 @@ describe('mobile automation host contracts', () => {
|
||||
|
||||
expect(params.updates).toEqual({ name: 'Renamed' })
|
||||
expect(params.destination).toBeUndefined()
|
||||
expect(original).toMatchObject({
|
||||
agentId: 'codex',
|
||||
rrule: saved.rrule,
|
||||
grace: '0',
|
||||
reuseSession: true
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -52,20 +52,6 @@ function catalogHarness(respond: () => unknown) {
|
||||
}
|
||||
|
||||
describe('automation catalog', () => {
|
||||
it('shows the last list at once when the screen reopens, then the host’s newer one', async () => {
|
||||
let reply: ReturnType<typeof deferred<unknown>> | null = null
|
||||
const { read, mount } = catalogHarness(() => reply?.promise ?? listReply('Box review'))
|
||||
await mount()
|
||||
expect(read().rows.map((row) => row.automation.name)).toEqual(['Local review', 'Box review'])
|
||||
act(() => tree?.unmount())
|
||||
reply = deferred<unknown>()
|
||||
await mount()
|
||||
expect(read().loaded).toBe(true)
|
||||
expect(read().rows.map((row) => row.automation.name)).toEqual(['Local review', 'Box review'])
|
||||
await act(async () => reply?.resolve(listReply('Box nightly')))
|
||||
expect(read().rows.map((row) => row.automation.name)).toEqual(['Local review', 'Box nightly'])
|
||||
})
|
||||
|
||||
it('keeps a saved change when a list read sent before the write lands after it', async () => {
|
||||
let held: ReturnType<typeof deferred<unknown>> | null = null
|
||||
let remoteName = 'Box review'
|
||||
|
||||
@@ -40,7 +40,5 @@ describe('browser touch geometry', () => {
|
||||
|
||||
expect(delta.dy).toBe(-244)
|
||||
expect(delta.dx).toBe(0)
|
||||
// What it sent before: the screen delta itself, 41% of the scroll asked for.
|
||||
expect(delta.dy).not.toBe(-100)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -18,19 +18,7 @@ describe('drag completion and cancellation', () => {
|
||||
await act(async () => fixture.root.unmount())
|
||||
})
|
||||
|
||||
it('does not write after a failed activation or an unchanged drop', async () => {
|
||||
const fixture = await mounted()
|
||||
await act(async () => {
|
||||
fixture.gesture.finalize!({ absoluteY: 100, translationY: 0 }, false)
|
||||
fixture.gesture.start!({ absoluteY: 100, translationY: 0 }, true)
|
||||
fixture.gesture.finalize!({ absoluteY: 100, translationY: 0 }, true)
|
||||
})
|
||||
expect(fixture.onReorder).not.toHaveBeenCalled()
|
||||
await act(async () => fixture.root.unmount())
|
||||
})
|
||||
|
||||
it.each([
|
||||
{ ids: ['a', 'fixed'], distance: 80, expected: { a: 0, fixed: 1 }, changed: false },
|
||||
{ ids: ['a', 'fixed', 'b'], distance: 80, expected: { a: 0, fixed: 1, b: 2 }, changed: false },
|
||||
{ ids: ['a', 'fixed', 'b'], distance: 160, expected: { b: 0, fixed: 1, a: 2 }, changed: true }
|
||||
])('settles a drag across excluded slots at $distance with $ids', async (test) => {
|
||||
@@ -90,22 +78,6 @@ describe('catalog changes during a drag', () => {
|
||||
await act(async () => fixture.root.unmount())
|
||||
})
|
||||
|
||||
it.each([{ ids: ['a', 'b', 'c'] }, { ids: ['b', 'a'] }])(
|
||||
'does not commit a stale drag after the catalog becomes $ids',
|
||||
async ({ ids }) => {
|
||||
const fixture = await mounted()
|
||||
await act(async () => {
|
||||
fixture.gesture.start!({ absoluteY: 100, translationY: 0 }, true)
|
||||
fixture.gesture.update!({ absoluteY: 180, translationY: 80 }, true)
|
||||
})
|
||||
await fixture.updateItems(ids)
|
||||
await act(async () => fixture.gesture.finalize!({ absoluteY: 180, translationY: 80 }, true))
|
||||
expect(fixture.onReorder).not.toHaveBeenCalled()
|
||||
expect(fixture.onActive.mock.calls).toEqual([[true], [false]])
|
||||
await act(async () => fixture.root.unmount())
|
||||
}
|
||||
)
|
||||
|
||||
it('releases its owner when unmounted while a row is held', async () => {
|
||||
const fixture = await mounted()
|
||||
await act(async () => fixture.gesture.start!({ absoluteY: 100, translationY: 0 }, true))
|
||||
@@ -135,34 +107,24 @@ describe('catalog changes during a drag', () => {
|
||||
})
|
||||
|
||||
describe('late gesture callbacks', () => {
|
||||
it('ignores movement delivered after the row catalog changes', async () => {
|
||||
const fixture = await mounted()
|
||||
await act(async () => fixture.gesture.start!({ absoluteY: 100, translationY: 0 }, true))
|
||||
await fixture.updateItems(['a', 'b', 'c'])
|
||||
await act(async () => fixture.gesture.update!({ absoluteY: 180, translationY: 80 }, true))
|
||||
expect(positions()).toEqual({ a: 0, b: 1, c: 2 })
|
||||
expect(fixture.onReorder).not.toHaveBeenCalled()
|
||||
await act(async () => fixture.root.unmount())
|
||||
})
|
||||
|
||||
it.each([
|
||||
['a', 'b', 'c'],
|
||||
['b', 'a']
|
||||
])('does not save a completed drop queued before catalog %j replaces it', async (...ids) => {
|
||||
const fixture = await mounted()
|
||||
await act(async () => {
|
||||
fixture.gesture.start!({ absoluteY: 100, translationY: 0 }, true)
|
||||
fixture.gesture.update!({ absoluteY: 180, translationY: 80 }, true)
|
||||
probe.deferJS = true
|
||||
fixture.gesture.finalize!({ absoluteY: 180, translationY: 80 }, true)
|
||||
})
|
||||
await fixture.updateItems(ids)
|
||||
await act(async () => {
|
||||
probe.jsQueue.splice(0).forEach((fn) => fn())
|
||||
})
|
||||
expect(fixture.onReorder).not.toHaveBeenCalled()
|
||||
await act(async () => fixture.root.unmount())
|
||||
})
|
||||
it.each([['a', 'b', 'c']])(
|
||||
'does not save a completed drop queued before catalog %j replaces it',
|
||||
async (...ids) => {
|
||||
const fixture = await mounted()
|
||||
await act(async () => {
|
||||
fixture.gesture.start!({ absoluteY: 100, translationY: 0 }, true)
|
||||
fixture.gesture.update!({ absoluteY: 180, translationY: 80 }, true)
|
||||
probe.deferJS = true
|
||||
fixture.gesture.finalize!({ absoluteY: 180, translationY: 80 }, true)
|
||||
})
|
||||
await fixture.updateItems(ids)
|
||||
await act(async () => {
|
||||
probe.jsQueue.splice(0).forEach((fn) => fn())
|
||||
})
|
||||
expect(fixture.onReorder).not.toHaveBeenCalled()
|
||||
await act(async () => fixture.root.unmount())
|
||||
}
|
||||
)
|
||||
|
||||
it('does not revive a queued drop when the original row order returns', async () => {
|
||||
const fixture = await mounted()
|
||||
|
||||
@@ -33,9 +33,6 @@ describe('normalizeMobileMarkdownPreviewHtml', () => {
|
||||
expect(normalizeMobileMarkdownPreviewHtml('```html\n<div>x</div>\n```')).toBe(
|
||||
'```html\n<div>x</div>\n```'
|
||||
)
|
||||
expect(normalizeMobileMarkdownPreviewHtml('```ts\nconst x: Array<string> = []\n```')).toBe(
|
||||
'```ts\nconst x: Array<string> = []\n```'
|
||||
)
|
||||
expect(normalizeMobileMarkdownPreviewHtml('```ts\nconst x: Array<string> = []')).toBe(
|
||||
'```ts\nconst x: Array<string> = []'
|
||||
)
|
||||
@@ -49,12 +46,6 @@ describe('normalizeMobileMarkdownPreviewHtml', () => {
|
||||
expect(normalizeMobileMarkdownPreviewHtml('Array<string> in prose')).toBe(
|
||||
'Array<string> in prose'
|
||||
)
|
||||
expect(normalizeMobileMarkdownPreviewHtml('Promise<Result> in prose')).toBe(
|
||||
'Promise<Result> in prose'
|
||||
)
|
||||
expect(normalizeMobileMarkdownPreviewHtml('Promise<Array<string>> in prose')).toBe(
|
||||
'Promise<Array<string>> in prose'
|
||||
)
|
||||
expect(normalizeMobileMarkdownPreviewHtml('Map<string, Array<number>> in prose')).toBe(
|
||||
'Map<string, Array<number>> in prose'
|
||||
)
|
||||
@@ -73,13 +64,6 @@ describe('normalizeMobileMarkdownPreviewHtml', () => {
|
||||
expect(normalizeMobileMarkdownPreviewHtml('<mailto:orca@example.com>')).toBe(
|
||||
'<mailto:orca@example.com>'
|
||||
)
|
||||
expect(normalizeMobileMarkdownPreviewHtml('<ftp://example.com/file>')).toBe(
|
||||
'<ftp://example.com/file>'
|
||||
)
|
||||
expect(normalizeMobileMarkdownPreviewHtml('<tel:+15551234567>')).toBe('<tel:+15551234567>')
|
||||
expect(normalizeMobileMarkdownPreviewHtml('<https://example.com>')).toBe(
|
||||
'<https://example.com>'
|
||||
)
|
||||
expect(
|
||||
normalizeMobileMarkdownPreviewHtml(
|
||||
"<https://en.wikipedia.org/wiki/O'Brien> <svg>hidden</svg>"
|
||||
@@ -96,11 +80,6 @@ describe('normalizeMobileMarkdownPreviewHtml', () => {
|
||||
expect(
|
||||
normalizeMobileMarkdownPreviewHtml('<span title="</string>"></span> Array<string> now')
|
||||
).toBe('Array<string> now')
|
||||
expect(
|
||||
normalizeMobileMarkdownPreviewHtml(
|
||||
'<span title=</your-api-key>></span> Replace <your-api-key> now'
|
||||
)
|
||||
).toBe('Replace <your-api-key> now')
|
||||
expect(
|
||||
normalizeMobileMarkdownPreviewHtml('<span title=</string>></span> Array<string> now')
|
||||
).toBe('Array<string> now')
|
||||
@@ -139,30 +118,14 @@ describe('normalizeMobileMarkdownPreviewHtml', () => {
|
||||
'Logo done'
|
||||
)
|
||||
expect(normalizeMobileMarkdownPreviewHtml('Logo<svg=invalid>Hi')).toBe('LogoHi')
|
||||
expect(normalizeMobileMarkdownPreviewHtml('Logo<SVG=invalid>Hi')).toBe('LogoHi')
|
||||
expect(normalizeMobileMarkdownPreviewHtml('Logo<path=invalid>Hi')).toBe('LogoHi')
|
||||
expect(normalizeMobileMarkdownPreviewHtml('Logo<p=invalid>Hi')).toBe('LogoHi')
|
||||
expect(normalizeMobileMarkdownPreviewHtml('Logo<strong=invalid>Hi')).toBe('LogoHi')
|
||||
expect(normalizeMobileMarkdownPreviewHtml('Logo<math=invalid>Hi')).toBe('LogoHi')
|
||||
expect(normalizeMobileMarkdownPreviewHtml('Logo<foo=invalid>Hi')).toBe('LogoHi')
|
||||
expect(normalizeMobileMarkdownPreviewHtml('Logo<my-widget=invalid>Hi')).toBe('LogoHi')
|
||||
expect(normalizeMobileMarkdownPreviewHtml('Logo<my-widget disabled>Hi')).toBe('LogoHi')
|
||||
expect(normalizeMobileMarkdownPreviewHtml('<svg=invalid><path/>')).toBe('')
|
||||
expect(normalizeMobileMarkdownPreviewHtml('Logo<my-widget=invalid>Hi</my-widget> done')).toBe(
|
||||
'LogoHi done'
|
||||
)
|
||||
expect(normalizeMobileMarkdownPreviewHtml('<SVG><PATH/></SVG>')).toBe('')
|
||||
expect(normalizeMobileMarkdownPreviewHtml('Logo<svg>Hi<path>there')).toBe('LogoHithere')
|
||||
expect(normalizeMobileMarkdownPreviewHtml('Logo<p>Hi')).toBe('LogoHi')
|
||||
expect(normalizeMobileMarkdownPreviewHtml('Logo<B>Hi')).toBe('LogoHi')
|
||||
expect(normalizeMobileMarkdownPreviewHtml('Logo<strong>Hi')).toBe('LogoHi')
|
||||
expect(normalizeMobileMarkdownPreviewHtml('Logo<center>Hi <font>there <marquee>now')).toBe(
|
||||
'LogoHi there now'
|
||||
)
|
||||
expect(normalizeMobileMarkdownPreviewHtml('<center>Title</center>')).toBe('Title')
|
||||
expect(normalizeMobileMarkdownPreviewHtml('<my-widget>Hi</my-widget>')).toBe('Hi')
|
||||
expect(normalizeMobileMarkdownPreviewHtml('<my:widget>')).toBe('')
|
||||
expect(normalizeMobileMarkdownPreviewHtml('<svg:path>')).toBe('')
|
||||
expect(normalizeMobileMarkdownPreviewHtml('Logo<foo>Hi</foo> done')).toBe('LogoHi done')
|
||||
expect(normalizeMobileMarkdownPreviewHtml('</1>Logo<svg>Hi</svg> done')).toBe('</1>LogoHi done')
|
||||
expect(normalizeMobileMarkdownPreviewHtml('Logo<svg>Hi</svg > done')).toBe('LogoHi done')
|
||||
|
||||
@@ -38,12 +38,11 @@ const RELEASES_URL = 'https://github.com/stablyai/orca/releases'
|
||||
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
|
||||
function render(verdict: BlockedVerdict, mobileUpdate: KnownAppUpdate | null = null): string {
|
||||
function render(verdict: BlockedVerdict, mobileUpdate: KnownAppUpdate | null = null): void {
|
||||
wallUpdate.current = mobileUpdate
|
||||
act(() => {
|
||||
renderer = create(createElement(ProtocolBlockScreen, { verdict }))
|
||||
})
|
||||
return JSON.stringify(renderer?.toJSON())
|
||||
}
|
||||
|
||||
/** The mocked host components are plain strings, which `ElementType` does not admit. */
|
||||
@@ -72,33 +71,22 @@ describe('ProtocolBlockScreen', () => {
|
||||
renderer = null
|
||||
})
|
||||
|
||||
it('tells a host without a bundle, or without this screen, the desktop is too old', () => {
|
||||
for (const reason of ['bundle-unavailable', 'page-route-unlisted'] as const) {
|
||||
const output = render({ kind: 'blocked', reason })
|
||||
expect(output).toContain('Update Orca on your computer')
|
||||
expect(output).toContain(
|
||||
'This paired desktop app is too old for your current Orca Mobile app. Update Orca on your computer, then try this host again.'
|
||||
)
|
||||
expect(output).not.toContain('does not include the mobile workspace')
|
||||
expect(primaryActionUrl()).toBe(RELEASES_URL)
|
||||
}
|
||||
it('sends a host without a bundle to the desktop release', () => {
|
||||
render({ kind: 'blocked', reason: 'bundle-unavailable' })
|
||||
expect(primaryActionUrl()).toBe(RELEASES_URL)
|
||||
})
|
||||
|
||||
it('sends an unknown manifest schema to the mobile update', () => {
|
||||
const output = render({
|
||||
render({
|
||||
kind: 'blocked',
|
||||
reason: 'bundle-shell-too-old',
|
||||
schemaVersion: 2
|
||||
})
|
||||
expect(output).toContain('Update Orca Mobile')
|
||||
expect(output).toContain(
|
||||
"This desktop's mobile workspace needs a newer Orca Mobile app. Update Orca Mobile from the App Store, then try this host again."
|
||||
)
|
||||
expect(primaryActionUrl()).toBe('itms-apps://apps.apple.com/app/orca-ide/id6766130217')
|
||||
})
|
||||
|
||||
it('offers no download for a cached bundle the host outgrew, because none would clear it', () => {
|
||||
const output = render({
|
||||
render({
|
||||
kind: 'blocked',
|
||||
reason: 'bundle-incompatible',
|
||||
side: 'mobile',
|
||||
@@ -106,32 +94,18 @@ describe('ProtocolBlockScreen', () => {
|
||||
requiredBundleRuntimeProtocolVersion: 4
|
||||
})
|
||||
|
||||
expect(output).toContain('Refresh the mobile workspace')
|
||||
expect(output).toContain(
|
||||
'The workspace cached for this host is older than the desktop expects. Reconnect to this host to download the current one.'
|
||||
)
|
||||
// A store update cannot replace a stale cache, so neither store link is offered.
|
||||
expect(output).not.toContain('Open App Store')
|
||||
expect(output).not.toContain('Open GitHub Releases')
|
||||
expect(output).not.toContain('Update Orca')
|
||||
// Back to hosts is the only button left, and it is not a download.
|
||||
expect(pressableCount()).toBe(1)
|
||||
expect(output).toContain('Back to hosts')
|
||||
// Nothing was "already updated" here; the note keeps only the pairing fallback.
|
||||
expect(output).not.toContain('Already updated?')
|
||||
expect(output).toContain('If this message stays, remove this host and pair it again.')
|
||||
})
|
||||
|
||||
it('sends a host older than its own bundle to the desktop update', () => {
|
||||
const output = render({
|
||||
render({
|
||||
kind: 'blocked',
|
||||
reason: 'bundle-incompatible',
|
||||
side: 'desktop',
|
||||
hostProtocolVersion: 1,
|
||||
requiredHostProtocolVersion: 2
|
||||
})
|
||||
expect(output).toContain('Update Orca on your computer')
|
||||
expect(output).toContain('This paired desktop app is too old for your current Orca Mobile app')
|
||||
expect(primaryActionUrl()).toBe(RELEASES_URL)
|
||||
})
|
||||
|
||||
@@ -141,36 +115,27 @@ describe('ProtocolBlockScreen', () => {
|
||||
url: 'https://github.com/stablyai/orca/releases/tag/mobile-v0.0.52'
|
||||
}
|
||||
|
||||
it.each(['android'] as const)('opens that exact release on %s', (os) => {
|
||||
nativeTestState.platform.OS = os
|
||||
const output = render(
|
||||
{ kind: 'blocked', reason: 'bundle-shell-too-old', schemaVersion: 2 },
|
||||
release
|
||||
)
|
||||
expect(output).toContain('Get Orca 0.0.52')
|
||||
expect(output).not.toContain('Open App Store')
|
||||
expect(output).not.toContain('Open GitHub Releases')
|
||||
it('opens the known Android release', () => {
|
||||
nativeTestState.platform.OS = 'android'
|
||||
render({ kind: 'blocked', reason: 'bundle-shell-too-old', schemaVersion: 2 }, release)
|
||||
expect(primaryActionUrl()).toBe(release.url)
|
||||
})
|
||||
|
||||
it('leaves the desktop wall on the desktop releases', () => {
|
||||
const output = render({ kind: 'blocked', reason: 'bundle-unavailable' }, release)
|
||||
expect(output).not.toContain('Get Orca')
|
||||
render({ kind: 'blocked', reason: 'bundle-unavailable' }, release)
|
||||
expect(primaryActionUrl()).toBe(RELEASES_URL)
|
||||
})
|
||||
})
|
||||
|
||||
it.each([
|
||||
['ios', 'Open App Store', 'itms-apps://apps.apple.com/app/orca-ide/id6766130217'],
|
||||
['android', 'Open GitHub Releases', RELEASES_URL]
|
||||
] as const)('keeps the %s store link when no release is known', (os, label, url) => {
|
||||
['ios', 'itms-apps://apps.apple.com/app/orca-ide/id6766130217'],
|
||||
['android', RELEASES_URL]
|
||||
] as const)('keeps the %s store link when no release is known', (os, url) => {
|
||||
nativeTestState.platform.OS = os
|
||||
const output = render(
|
||||
render(
|
||||
{ kind: 'blocked', reason: 'mobile-too-old', desktopVersion: 5, requiredMobileVersion: 9 },
|
||||
null
|
||||
)
|
||||
expect(output).toContain(label)
|
||||
expect(output).not.toContain('Get Orca')
|
||||
expect(primaryActionUrl()).toBe(url)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
import { createElement } from 'react'
|
||||
import { act, create, type ReactTestRenderer } from 'react-test-renderer'
|
||||
import { afterEach, expect, it, vi } from 'vitest'
|
||||
import { SmartWorkspaceSourceRow } from './SmartWorkspaceSourceRow'
|
||||
|
||||
vi.mock('react-native', () => ({ Text: 'Text', View: 'View' }))
|
||||
vi.mock('lucide-react-native', () => ({ GitBranch: 'Icon', Sparkles: 'Icon' }))
|
||||
vi.mock('../theme/mobile-theme', () => ({ colors: {}, radii: {}, spacing: {} }))
|
||||
vi.mock('../theme/themed-styles', () => ({ themedStyleSheet: (make: () => unknown) => make() }))
|
||||
vi.mock('../theme/use-mobile-color-scheme', () => ({ useMobileColorScheme: () => 'dark' }))
|
||||
vi.mock('../i18n/use-mobile-translation', () => ({
|
||||
useMobileTranslation: () => (_key: string, fallback: string) => fallback
|
||||
}))
|
||||
vi.mock('./TaskProviderLogo', () => ({ TaskProviderLogo: () => null }))
|
||||
vi.mock('./list/ListGroup', () => ({ ListRow: 'ListRow' }))
|
||||
let renderer: ReactTestRenderer
|
||||
|
||||
afterEach(() => {
|
||||
act(() => renderer?.unmount())
|
||||
})
|
||||
|
||||
it('renders a natural remote label without a namespace subtitle and keeps collision qualification', async () => {
|
||||
const onPress = vi.fn()
|
||||
const row = {
|
||||
kind: 'branch' as const,
|
||||
value: 'branch-refs/remotes/origin/topic',
|
||||
refName: 'refs/remotes/origin/topic',
|
||||
localBranchName: 'topic'
|
||||
}
|
||||
await act(async () => {
|
||||
renderer = create(createElement(SmartWorkspaceSourceRow, { row, onPress }))
|
||||
})
|
||||
expect(renderer.toJSON()).toMatchObject({ props: { title: 'origin/topic', subtitle: 'topic' } })
|
||||
await act(async () => {
|
||||
renderer.update(
|
||||
createElement(SmartWorkspaceSourceRow, {
|
||||
row: { ...row, label: 'refs/remotes/origin/topic' },
|
||||
onPress
|
||||
})
|
||||
)
|
||||
})
|
||||
expect(renderer.toJSON()).toMatchObject({
|
||||
props: { title: 'refs/remotes/origin/topic', subtitle: 'topic' }
|
||||
})
|
||||
})
|
||||
@@ -72,7 +72,7 @@ describe('BottomDrawer close lifecycle', () => {
|
||||
vi.restoreAllMocks()
|
||||
})
|
||||
|
||||
it('keeps close stable and delivers the latest action once after unmount', () => {
|
||||
it('delivers the latest action once after unmount', () => {
|
||||
const firstAfterClose = vi.fn()
|
||||
const rendered: { current?: ReactTestRenderer } = {}
|
||||
const latestAfterClose = vi.fn(() => {
|
||||
@@ -80,16 +80,11 @@ describe('BottomDrawer close lifecycle', () => {
|
||||
})
|
||||
const renderer = renderDrawer(true, vi.fn(), firstAfterClose)
|
||||
rendered.current = renderer
|
||||
const initialOnHidden = mountedDrawer(renderer).props.onHidden
|
||||
|
||||
updateDrawer(renderer, false, vi.fn(), firstAfterClose)
|
||||
const closingOnHidden = mountedDrawer(renderer).props.onHidden
|
||||
updateDrawer(renderer, false, vi.fn(), latestAfterClose)
|
||||
const rerenderedOnHidden = mountedDrawer(renderer).props.onHidden
|
||||
|
||||
expect(closingOnHidden).toBe(initialOnHidden)
|
||||
expect(rerenderedOnHidden).toBe(initialOnHidden)
|
||||
|
||||
act(() => {
|
||||
rerenderedOnHidden()
|
||||
rerenderedOnHidden()
|
||||
@@ -102,22 +97,4 @@ describe('BottomDrawer close lifecycle', () => {
|
||||
expect(latestAfterClose).toHaveBeenCalledTimes(1)
|
||||
expect(renderer.toJSON()).toBeNull()
|
||||
})
|
||||
|
||||
// Reopen before queued hide callback must invalidate it or next close leaves invisible tap-swallowing Modal.
|
||||
it('a hide that lands after a reopen does not swallow the next close', () => {
|
||||
const onAfterClose = vi.fn()
|
||||
const renderer = renderDrawer(true, vi.fn(), onAfterClose)
|
||||
const lateOnHidden = mountedDrawer(renderer).props.onHidden
|
||||
updateDrawer(renderer, false, vi.fn(), onAfterClose)
|
||||
updateDrawer(renderer, true, vi.fn(), onAfterClose)
|
||||
|
||||
act(() => lateOnHidden())
|
||||
expect(mountedDrawer(renderer).props.visible).toBe(true)
|
||||
expect(onAfterClose).not.toHaveBeenCalled()
|
||||
|
||||
updateDrawer(renderer, false, vi.fn(), onAfterClose)
|
||||
act(() => mountedDrawer(renderer).props.onHidden())
|
||||
expect(renderer.toJSON()).toBeNull()
|
||||
expect(onAfterClose).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { bottomDrawerDismissMotion, shouldDismissBottomDrawer } from './bottom-drawer-drag-motion'
|
||||
import { shouldDismissBottomDrawer } from './bottom-drawer-drag-motion'
|
||||
|
||||
describe('bottom drawer release', () => {
|
||||
it('judges where the sheet is heading, not where the finger let go', () => {
|
||||
@@ -9,14 +9,4 @@ describe('bottom drawer release', () => {
|
||||
// A flick back up keeps the sheet even after a long drag.
|
||||
expect(shouldDismissBottomDrawer(180, -600, 777)).toBe(false)
|
||||
})
|
||||
|
||||
it('leaves at the finger speed, slow or thrown, within an ordinary close', () => {
|
||||
const remaining = 825 - 112
|
||||
for (const velocity of [800, 6000, 12000]) {
|
||||
const { duration, x1, y1 } = bottomDrawerDismissMotion(112, velocity, 825, 300)
|
||||
expect(duration).toBeLessThanOrEqual(300)
|
||||
// The curve's start slope (y1 / x1) times the average speed is the speed it starts at.
|
||||
expect(((y1 / x1) * remaining) / (duration / 1000)).toBeCloseTo(velocity)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user