diff --git a/config/scripts/mobile-web-app/preview/mobile-web-app-html-preview-render.test.mjs b/config/scripts/mobile-web-app/preview/mobile-web-app-html-preview-render.test.mjs index 17132e2e95c..555bda09064 100644 --- a/config/scripts/mobile-web-app/preview/mobile-web-app-html-preview-render.test.mjs +++ b/config/scripts/mobile-web-app/preview/mobile-web-app-html-preview-render.test.mjs @@ -304,37 +304,6 @@ for (const engine of ['chromium', 'webkit']) { expect(shown.mountedSandbox).toBe('allow-top-navigation-by-user-activation') }, 240_000) - // Check painted fixtures, granted document content and inert fragment targets. - it('paints both artifact variants and keeps hidden fragment links inert', async (ctx) => { - // A doctype with a public identifier and no system identifier, which is quirks. The bare - // name is not, so a pass that rewrote one as the other moves the whole artifact between - // layout modes -- which is what this arm exists to catch. - const quirks = '' - const shown = await open(browser(), { signal: ctx.signal, doctype: quirks }) - const hidden = await open(browser(), { - signal: ctx.signal, - doctype: quirks, - grants: ['navigate', 'storage'] - }) - // Both fixture variants painted. - expect(shown.pixel).toBe(ARTIFACT_RGB) - expect(hidden.pixel).toBe(ARTIFACT_RGB) - - // Known granted doctype remains distinct from the bare-doctype control. - expect(shown.inside?.doctypePublicId).toBe('-//W3C//DTD HTML 4.01 Transitional//EN') - // Bare-doctype control proves ID reader can distinguish lost identifier. - const bare = await open(browser(), { signal: ctx.signal, grants: ['navigate', 'storage'] }) - expect(bare.inside?.doctypePublicId).toBe('') - - // Granted fixture retains the leading pre newline. - expect(shown.inside?.preText).toBe('\nkept') - - // Granted fragment remains known broken navigation; hidden arm must strip it rather than preserve false scroll affordance. - expect(shown.inside?.fragmentHref).toBe('#fragtarget') - expect(hidden.inside?.fragmentHref).toBeNull() - expect(hidden.links?.linked).toBe(0) - }, 240_000) - // srcdoc #fragment resolves against embedder base, triggering frame-src none instead of scrolling; Chromium can replace artifact with error page. // Granted control retains known bug (followup-html-preview-fragment-links.md) and proves navigation counters see it; hidden arm must remain inert. diff --git a/mobile/src/components/rich-markdown/document/rich-markdown-web-document-mount.test.ts b/mobile/src/components/rich-markdown/document/rich-markdown-web-document-mount.test.ts deleted file mode 100644 index 36304fc6efc..00000000000 --- a/mobile/src/components/rich-markdown/document/rich-markdown-web-document-mount.test.ts +++ /dev/null @@ -1,57 +0,0 @@ -// @vitest-environment happy-dom -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { - mountRichMarkdownWebDocument, - type RichMarkdownWebDocument -} from './rich-markdown-web-document-mount' -import type { MobileRichMarkdownEditorMessage } from '../mobile-rich-markdown-editor-contract' - -// Prove page mount installs scoped style/markup and fully disposes editor state/listeners before remount (rulings 20/21). -// Browser styling/execCommand behavior belongs to mobile-web-app-rich-markdown-render.test.mjs, not happy-dom. -const mounted: RichMarkdownWebDocument[] = [] - -function mount(posts: MobileRichMarkdownEditorMessage[] = [], url: string | null = null) { - const host = document.createElement('div') - document.body.appendChild(host) - const live = mountRichMarkdownWebDocument(host, { - postToHost: (message) => posts.push(message), - promptForUrl: () => Promise.resolve(url) - }) - mounted.push(live) - return { host, live, posts } -} - -const surfaceIn = (host: HTMLElement) => host.querySelector('#editor') - -beforeEach(() => { - document.body.innerHTML = '' - document.head.innerHTML = '' -}) - -afterEach(() => { - while (mounted.length > 0) { - mounted.pop()!.dispose() - } - vi.restoreAllMocks() -}) - -describe('the editor document mounted in the page', () => { - it('leaves no listener of the first mount reporting into the second', () => { - const first = mount() - const firstSurface = surfaceIn(first.host)! - first.live.dispose() - mounted.pop() - first.posts.length = 0 - - const second = mount() - second.posts.length = 0 - surfaceIn(second.host)!.innerHTML = '

second

' - surfaceIn(second.host)!.dispatchEvent(new Event('input', { bubbles: true })) - - expect(second.posts).toEqual([{ type: 'change', markdown: 'second', generation: 0 }]) - // Assert detached surface still receives dispatched events before concluding old listeners are removed. - firstSurface.innerHTML = '

first

' - firstSurface.dispatchEvent(new Event('input', { bubbles: true })) - expect(first.posts).toEqual([]) - }) -}) diff --git a/mobile/src/components/rich-markdown/mobile-webview-editor-web-fallbacks.test.tsx b/mobile/src/components/rich-markdown/mobile-webview-editor-web-fallbacks.test.tsx index 07a1efa17a3..c24c5ecee42 100644 --- a/mobile/src/components/rich-markdown/mobile-webview-editor-web-fallbacks.test.tsx +++ b/mobile/src/components/rich-markdown/mobile-webview-editor-web-fallbacks.test.tsx @@ -52,11 +52,6 @@ import { MobileRichMarkdownEditor } from './MobileRichMarkdownEditor' const renderers: ReactTestRenderer[] = [] -// Match mocked host strings by predicate; findAllByType expects component ElementType. -function findHosts(renderer: ReactTestRenderer, type: string) { - return renderer.root.findAll((node) => node.type === type) -} - function render( element: React.ReactElement, node: HTMLElement = document.createElement('div') @@ -85,7 +80,7 @@ describe('the rich markdown editor on the page', () => { it('mounts the editor document with its content and reports edits to the host', () => { const host = document.createElement('div') const onChange = vi.fn() - const renderer = render( + render( createElement(MobileRichMarkdownEditor, { content: '# Title\n\nbody', editable: true, @@ -98,7 +93,6 @@ describe('the rich markdown editor on the page', () => { expect(surface.querySelector('p')?.textContent).toBe('body') expect(surface.getAttribute('contenteditable')).toBe('true') expect(onChange).not.toHaveBeenCalled() - expect(findHosts(renderer, 'TextInput')).toEqual([]) act(() => { surface.innerHTML = '

edited

' diff --git a/mobile/src/files/explorer/file-tree.test.ts b/mobile/src/files/explorer/file-tree.test.ts index 1a2990821f4..3b0781f49b5 100644 --- a/mobile/src/files/explorer/file-tree.test.ts +++ b/mobile/src/files/explorer/file-tree.test.ts @@ -11,27 +11,6 @@ function entry(name: string, isDirectory = false, isSymlink = false): MobileDirE } describe('file-tree', () => { - it('flattens cached directories lazily with directories before files', () => { - const cache: DirectoryCache = { - '': { entries: [entry('zeta.txt'), entry('src', true), entry('readme.md')] }, - src: { entries: [entry('app.ts'), entry('lib', true)] }, - 'src/lib': { entries: [entry('util.ts')] } - } - - const collapsed = flattenDirectoryCache(cache, new Set()) - expect(collapsed.map((row) => row.id)).toEqual(['dir:src', 'file:readme.md', 'file:zeta.txt']) - - const expanded = flattenDirectoryCache(cache, new Set(['src', 'src/lib'])) - expect(expanded.map((row) => row.id)).toEqual([ - 'dir:src', - 'dir:src/lib', - 'file:src/lib/util.ts', - 'file:src/app.ts', - 'file:readme.md', - 'file:zeta.txt' - ]) - }) - it('filters loaded folders by name and keeps the parents of nested matches', () => { const cache: DirectoryCache = { '': { entries: [entry('README.md'), entry('src', true), entry('node_modules', true)] }, diff --git a/mobile/src/files/markdown/mobile-markdown-file-link.test.ts b/mobile/src/files/markdown/mobile-markdown-file-link.test.ts index 663f31a14c3..63638ad1b74 100644 --- a/mobile/src/files/markdown/mobile-markdown-file-link.test.ts +++ b/mobile/src/files/markdown/mobile-markdown-file-link.test.ts @@ -10,23 +10,6 @@ const params = { } describe('Markdown preview file links', () => { - it.each([ - ['details.md', 'docs/details.md'], - ['../details.md', 'details.md'], - ['nested\\details.md', 'docs/nested/details.md'] - ])('opens %s relative to the current document', (path, relativePath) => { - expect(mobileMarkdownFileLink(params, path)).toEqual({ - pathname: '/h/[hostId]/files/preview/[worktreeId]', - params: { - hostId: 'remote-host', - worktreeId: 'folder-workspace', - worktreeName: 'Project', - source: 'worktree', - relativePath - } - }) - }) - it('preserves decoded filenames and line targets from rendered links', () => { const link = routeMarkdownHref('nested/a%20b.md#L12') expect(link.kind).toBe('file') diff --git a/mobile/src/files/preview/documents/html-preview-link-affordance.test.tsx b/mobile/src/files/preview/documents/html-preview-link-affordance.test.tsx deleted file mode 100644 index 271727165bb..00000000000 --- a/mobile/src/files/preview/documents/html-preview-link-affordance.test.tsx +++ /dev/null @@ -1,84 +0,0 @@ -// @vitest-environment happy-dom -// Assert component follows shell grant for both sandbox and inerted document; native test resolver needs explicit web-grant substitution. -// happy-dom supplies DOMParser for hidden arm. Real browser rig uses actual web sibling; this wiring suite still runs when bundler-dependent rig skips. -import { createElement } from 'react' -import { act, create, type ReactTestRenderer } from 'react-test-renderer' -import { afterEach, describe, expect, it, vi } from 'vitest' - -const shell = { opensLinks: true } - -vi.mock('./use-html-preview-link-grant', () => ({ - useHtmlPreviewLinkGrant: () => shell.opensLinks -})) - -vi.mock('react-native', async () => { - const React = await import('react') - return { - View: ({ children, ...props }: { children?: React.ReactNode }) => - React.createElement('View', props, children), - Text: ({ children, ...props }: { children?: React.ReactNode }) => - React.createElement('Text', props, children), - Pressable: ({ children, ...props }: { children?: React.ReactNode }) => - React.createElement('Pressable', props, children), - StyleSheet: { create: (styles: unknown) => styles } - } -}) - -vi.mock('lucide-react-native', () => ({ - Code: () => null, - Eye: () => null -})) - -const { MobileHtmlPreview } = await import('./MobileHtmlPreview') - -const ARTIFACT = - 'tap' - -const renderers: ReactTestRenderer[] = [] - -// Match mock host tag through String: renderer ElementType rejects literal Pressable comparison without cast. -function findHosts(renderer: ReactTestRenderer, tag: string) { - return renderer.root.findAll((node) => String(node.type) === tag) -} - -// Mount inside act as required by IS_REACT_ACT_ENVIRONMENT; otherwise create commits nothing. -function mount(renderSource: () => React.ReactNode): ReactTestRenderer { - let renderer: ReactTestRenderer | null = null - act(() => { - renderer = create(createElement(MobileHtmlPreview, { html: ARTIFACT, renderSource })) - }) - if (renderer === null) { - throw new Error('nothing mounted') - } - renderers.push(renderer) - return renderer -} - -function frameOf(opensLinks: boolean) { - shell.opensLinks = opensLinks - const frames = findHosts( - mount(() => createElement('SourceView', null)), - 'iframe' - ) - expect(frames).toHaveLength(1) - return frames[0]?.props -} - -afterEach(() => { - shell.opensLinks = true - for (const renderer of renderers.splice(0)) { - act(() => renderer.unmount()) - } -}) - -describe('the frame the preview mounts for each answer the shell gives', () => { - it('hands over an artifact with no link, in a frame that cannot navigate, when it cannot', () => { - const frame = frameOf(false) - // Both fences follow the one answer, which is the wiring this file exists for. - expect(frame?.sandbox).toBe('') - expect(frame?.srcDoc).not.toContain('href') - // The author's text is still there, so this is a hidden affordance and not a deletion. - expect(frame?.srcDoc).toContain('tap') - expect(frame?.srcDoc).toContain('id="x"') - }) -})