From 5e4c47d5f0bbdd8f41c5df61359f970d4f3de2fc Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Sat, 14 Nov 2026 02:55:55 -0800 Subject: [PATCH] test: remove low-value markdown fold and invalidation cases (#42361) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- config/flaky-test-pattern-baseline.txt | 2 +- .../mobile-web-shell/host-area-fold.test.tsx | 109 ------------ .../ipc/settings-delayed-response.test.ts | 7 +- .../service/service-minimax-usage.test.ts | 45 +---- .../markdown-preview-document.test.ts | 157 +----------------- .../native-chat-link-peek-context.test.tsx | 75 --------- 6 files changed, 9 insertions(+), 386 deletions(-) delete mode 100644 mobile/src/mobile-web-shell/host-area-fold.test.tsx delete mode 100644 src/renderer/src/components/native-chat/links/previews/native-chat-link-peek-context.test.tsx diff --git a/config/flaky-test-pattern-baseline.txt b/config/flaky-test-pattern-baseline.txt index f4f8af58d02..b5331b0f1e3 100644 --- a/config/flaky-test-pattern-baseline.txt +++ b/config/flaky-test-pattern-baseline.txt @@ -449,7 +449,7 @@ 4 test-no-wall-clock:src/main/pty/process-retirement/windows-live-tree-kill.win32.test.ts 5 test-no-wall-clock:src/main/rate-limits/service/service-account-target-selection.test.ts 1 test-no-wall-clock:src/main/rate-limits/service/service-cursor-usage.test.ts -3 test-no-wall-clock:src/main/rate-limits/service/service-minimax-usage.test.ts +1 test-no-wall-clock:src/main/rate-limits/service/service-minimax-usage.test.ts 3 test-no-wall-clock:src/main/rate-limits/service/service-zcode-usage.test.ts 1 test-no-wall-clock:src/main/runtime/agent-session/lifecycle/structured-codex-child-work-runtime.test.ts 2 test-no-wall-clock:src/main/runtime/agent-session/structured-agent-session-held-signal.test.ts diff --git a/mobile/src/mobile-web-shell/host-area-fold.test.tsx b/mobile/src/mobile-web-shell/host-area-fold.test.tsx deleted file mode 100644 index 15ea197dee7..00000000000 --- a/mobile/src/mobile-web-shell/host-area-fold.test.tsx +++ /dev/null @@ -1,109 +0,0 @@ -// Folding a foldable while its host-area page shows a session must keep that session, not drop to the list beneath. -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' - -const harness = await vi.hoisted(async () => await import('./mobile-web-shell-screen-test-harness')) -const dependencies = vi.hoisted(() => harness.createScreenDependencies()) -const { screenModuleMocks } = await vi.hoisted( - async () => await import('./mobile-web-shell-screen-test-mocks') -) -const mocks = vi.hoisted(() => screenModuleMocks(dependencies)) -const device = vi.hoisted(() => ({ width: 951, height: 669 })) - -vi.mock('react-native', () => ({ - ...mocks['react-native'](), - useWindowDimensions: () => ({ width: device.width, height: device.height }) -})) -vi.mock('expo-router', mocks['expo-router']) -vi.mock('expo-clipboard', mocks['expo-clipboard']) -vi.mock('expo-haptics', mocks['expo-haptics']) -vi.mock('expo-document-picker', mocks['expo-document-picker']) -vi.mock('@orca/expo-two-way-audio', mocks['@orca/expo-two-way-audio']) -vi.mock('expo-keep-awake', mocks['expo-keep-awake']) -vi.mock('expo-image-picker', mocks['expo-image-picker']) -vi.mock('expo-file-system', mocks['expo-file-system']) -vi.mock('../device-store/device-store', mocks['../device-store/device-store']) -vi.mock('lucide-react-native', mocks['lucide-react-native']) -vi.mock('react-native-safe-area-context', mocks['react-native-safe-area-context']) -vi.mock('../../modules/orca-mobile-web-shell/src', mocks['../../modules/orca-mobile-web-shell/src']) -vi.mock('../app-update/use-wall-app-update', mocks['../app-update/use-wall-app-update']) -vi.mock( - '../transport/connection/host-client-hooks', - mocks['../transport/connection/host-client-hooks'] -) -vi.mock('./use-page-host-snapshot', mocks['./use-page-host-snapshot']) -vi.mock('./use-mobile-web-shell-session', mocks['./use-mobile-web-shell-session']) - -import { createElement } from 'react' -import { act, create, type ReactTestRenderer } from 'react-test-renderer' -import { clientFrame, createFakeRpcClient } from './bridge-host-test-fakes' -import { MobileWebShellScreen } from './MobileWebShellScreen' - -const HOST = '/h/host-1' -const DETAIL = '/h/host-1/session/wt-1' -const UNFOLDED = { width: 951, height: 669 } -const COVER = { width: 466, height: 678 } - -const hostEntry = { key: 'entry-0', name: '[hostId]/index', params: { hostId: 'host-1' } } -const sessionEntry = { - key: 'entry-1', - name: '[hostId]/session/[worktreeId]', - params: { hostId: 'host-1', worktreeId: 'wt-1' } -} - -function stackOf(routes: readonly (typeof hostEntry | typeof sessionEntry)[]) { - const hostStack = { key: 'host-stack', index: routes.length - 1, routes: [...routes] } - return { key: 'root-stack', index: 0, routes: [{ key: 'h', name: 'h', state: hostStack }] } -} - -async function foldAfterPageShows(location: string | null, focused = true): Promise { - Object.assign(device, UNFOLDED) - dependencies.ownsHostArea = true - dependencies.screenFocused = focused - // Unfocused means a native session was pushed over this host-route shell. - dependencies.rootState = focused ? stackOf([hostEntry]) : stackOf([hostEntry, sessionEntry]) - dependencies.client = createFakeRpcClient() - dependencies.state = harness.readyState('session-one') - dependencies.pageRoutes = ['/h/[hostId]', '/h/[hostId]/session/[worktreeId]'] - const screen = () => - createElement(MobileWebShellScreen, { hostId: 'host-1', route: { pathname: HOST } }) - const mounted: { tree: ReactTestRenderer | null } = { tree: null } - await act(async () => { - mounted.tree = create(screen()) - }) - const view = mounted.tree?.root.findAll((node) => String(node.type) === 'ShellViewProbe')[0] - await act(async () => { - view?.props.onBridgeMessage({ nativeEvent: { json: clientFrame({ type: 'ready' }) } }) - if (location !== null) { - view?.props.onBridgeMessage({ - nativeEvent: { json: clientFrame({ type: 'notify', name: 'location', href: location }) } - }) - } - }) - Object.assign(device, COVER) - dependencies.ownsHostArea = false - await act(async () => { - mounted.tree?.update(screen()) - }) - act(() => mounted.tree?.unmount()) -} - -beforeEach(() => { - harness.resetScreenDependencies(dependencies) -}) - -afterEach(() => { - vi.clearAllMocks() -}) - -describe('folding while the host-area page shows a session', () => { - it('reopens that session natively on the cover screen', async () => { - await foldAfterPageShows(DETAIL) - expect(dependencies.push.mock.calls).toEqual([[DETAIL]]) - }) - - it('leaves the stack alone from a host-route shell covered by a pushed session', async () => { - await foldAfterPageShows(HOST, false) - expect(dependencies.push).not.toHaveBeenCalled() - expect(dependencies.dispatch).not.toHaveBeenCalled() - }) -}) diff --git a/src/main/ipc/settings-delayed-response.test.ts b/src/main/ipc/settings-delayed-response.test.ts index 227bbad518a..5a62d0f81de 100644 --- a/src/main/ipc/settings-delayed-response.test.ts +++ b/src/main/ipc/settings-delayed-response.test.ts @@ -7,13 +7,11 @@ import { const { handleMock, setMainUiLanguageMock, - rebuildSystemTrayMenuMock, applyElectronProxySettingsMock, applyAgentStatusHooksEnabledMock } = vi.hoisted(() => ({ handleMock: vi.fn(), setMainUiLanguageMock: vi.fn(), - rebuildSystemTrayMenuMock: vi.fn(), applyElectronProxySettingsMock: vi.fn(), applyAgentStatusHooksEnabledMock: vi.fn() })) @@ -54,7 +52,7 @@ vi.mock('../workspaces/creation/worktree-root-preparation', () => ({ prepareLocalWorktreeRootsForRepos: vi.fn().mockResolvedValue(undefined) })) vi.mock('../menu/register-app-menu', () => ({ rebuildAppMenu: vi.fn() })) -vi.mock('../tray/system-tray', () => ({ rebuildSystemTrayMenu: rebuildSystemTrayMenuMock })) +vi.mock('../tray/system-tray', () => ({ rebuildSystemTrayMenu: vi.fn() })) vi.mock('../i18n/main-i18n', () => ({ setMainUiLanguage: setMainUiLanguageMock })) import { registerSettingsHandlers } from './settings' @@ -62,7 +60,6 @@ import { registerSettingsHandlers } from './settings' beforeEach(() => { handleMock.mockClear() setMainUiLanguageMock.mockReset().mockResolvedValue('en') - rebuildSystemTrayMenuMock.mockClear() applyElectronProxySettingsMock.mockReset().mockResolvedValue({ source: 'settings' }) applyAgentStatusHooksEnabledMock.mockReset().mockResolvedValue([]) }) @@ -93,11 +90,9 @@ it('returns current settings when a language change finishes after a newer theme const first = write(event, { theme: 'dark', uiLanguage: 'ko' }) await started.promise - expect(rebuildSystemTrayMenuMock).not.toHaveBeenCalled() await write(event, { theme: 'light' }) release.resolve() const reply = await first - expect(rebuildSystemTrayMenuMock).toHaveBeenCalledOnce() await store.flushPendingOrThrowAsync() expect(store.getSettings().theme).toBe('light') diff --git a/src/main/rate-limits/service/service-minimax-usage.test.ts b/src/main/rate-limits/service/service-minimax-usage.test.ts index 48f06189b09..dd9e6c797be 100644 --- a/src/main/rate-limits/service/service-minimax-usage.test.ts +++ b/src/main/rate-limits/service/service-minimax-usage.test.ts @@ -1,14 +1,9 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' -import type { ProviderRateLimits } from '../../../shared/rate-limit-types' import { RateLimitService } from './rate-limit-service' import { fetchActiveClaudeRateLimits } from '../providers/claude/claude-active-usage-fetch' import { fetchCodexRateLimits } from '../providers/codex/codex-fetcher' import { fetchMiniMaxRateLimits } from '../providers/minimax/minimax-fetcher' -import { - deferred, - okProvider, - resetRateLimitProviderMocks -} from './rate-limit-service-test-harness' +import { okProvider, resetRateLimitProviderMocks } from './rate-limit-service-test-harness' vi.mock('../providers/claude/claude-active-usage-fetch', () => ({ fetchActiveClaudeRateLimits: vi.fn() @@ -108,42 +103,4 @@ describe('RateLimitService', () => { expect.objectContaining({ apiKey: 'sk-account-b' }) ) }) - - it('does not apply an in-flight MiniMax result after credential invalidation', async () => { - const service = new RateLimitService() - const firstMiniMax = deferred() - const secondMiniMax = deferred() - service.setMiniMaxConfigResolver(() => ({ - sessionCookie: '_token=abc', - groupId: '', - models: 'general', - endpoint: 'overseas', - apiKey: '' - })) - vi.mocked(fetchMiniMaxRateLimits) - .mockImplementationOnce(() => firstMiniMax.promise) - .mockImplementationOnce(() => secondMiniMax.promise) - - const firstRefresh = service.refresh() - await Promise.resolve() - - service.invalidateMiniMaxCredentialState() - const queuedRefresh = service.refresh() - await Promise.resolve() - - firstMiniMax.resolve(okProvider('minimax', 50, Date.now())) - await Promise.resolve() - await Promise.resolve() - - expect(service.getState().minimax?.status).toBe('fetching') - expect(service.getState().minimax?.session).toBeNull() - - secondMiniMax.resolve(okProvider('minimax', 10, Date.now())) - await firstRefresh - await queuedRefresh - - const state = service.getState() - expect(fetchMiniMaxRateLimits).toHaveBeenCalledTimes(2) - expect(state.minimax?.session?.usedPercent).toBe(10) - }) }) diff --git a/src/renderer/src/components/editor/markdown-preview/document-worker/markdown-preview-document.test.ts b/src/renderer/src/components/editor/markdown-preview/document-worker/markdown-preview-document.test.ts index 037955e69bc..1d572d3fc33 100644 --- a/src/renderer/src/components/editor/markdown-preview/document-worker/markdown-preview-document.test.ts +++ b/src/renderer/src/components/editor/markdown-preview/document-worker/markdown-preview-document.test.ts @@ -1,99 +1,25 @@ -import { describe, expect, it, vi } from 'vitest' -import type { Nodes, RootContent } from 'hast' -import { createElement } from 'react' -import { renderToStaticMarkup } from 'react-dom/server' -import Markdown from 'react-markdown' -import { - MARKDOWN_REMARK_PLUGINS, - MARKDOWN_REHYPE_PLUGINS -} from '../render-tree/markdown-preview-plugins' -import { renderMarkdownPreviewTree } from '../render-tree/markdown-preview-render-tree' -import { markdownPreviewUrlTransform } from '../links/markdown-preview-url-transform' +import { describe, expect, it } from 'vitest' +import type { Nodes } from 'hast' import { MarkdownPreviewDocumentEngine } from './markdown-preview-document-engine' -import { - parseMarkdownPreviewDocument, - renderMarkdownPreviewBlock -} from './markdown-preview-document-tree' -import { countMarkdownPreviewNodes } from '../render-tree/markdown-preview-tree-content' -import { - MARKDOWN_PREVIEW_BLOCK_MAX_NODES, - MARKDOWN_PREVIEW_DOCUMENT_MAX_NODES, - MARKDOWN_PREVIEW_MAX_SEARCH_MATCHES, - MARKDOWN_PREVIEW_VIEWPORT_MAX_NODES -} from './markdown-preview-document-types' +import { parseMarkdownPreviewDocument } from './markdown-preview-document-tree' function elements(node: Nodes): Nodes[] { return [node, ...('children' in node ? node.children.flatMap(elements) : [])] } describe('large Markdown preview documents', () => { - it('rejects atomic blocks before allocating a copy of their trees', () => { - const { tree } = parseMarkdownPreviewDocument(`\`\`\`text\n${'x'.repeat(32_769)}\n\`\`\``) - const broadBlock: RootContent = { - type: 'element', - tagName: 'div', - properties: {}, - children: Array.from({ length: MARKDOWN_PREVIEW_BLOCK_MAX_NODES }, () => ({ - type: 'text', - value: 'x' - })) - } - const clone = vi.spyOn(globalThis, 'structuredClone') - try { - expect(renderMarkdownPreviewBlock(tree.children[0], 0).oversized).toBe(true) - expect(renderMarkdownPreviewBlock(broadBlock, 1).oversized).toBe(true) - expect(clone).not.toHaveBeenCalled() - } finally { - clone.mockRestore() - } - }) - - it('renders the same safe HTML as the ordinary preview pipeline', () => { - const content = - '# Repeat\n\n[Global][end] **bold** ~~deleted~~\n\n# Repeat\n\n' + - '| a | b |\n| --- | --- |\n| x | y |\n\n' + - '```javascript\nconst value = 42\n```\n\n$x^2$\n\n' + - '
Read\n\nNested **content**\n\n
\n\n' + - 'Note[^n].\n\n[^n]: A footnote\n\n[end]: https://example.com\n' - const engine = new MarkdownPreviewDocumentEngine() - const document = engine.load(content) - const actual = engine - .blocks(document.blocks.map((block) => block.index)) - .map((block) => renderToStaticMarkup(renderMarkdownPreviewTree(block.tree, {}))) - .join('') - const ordinary = renderToStaticMarkup( - createElement( - Markdown, - { - remarkPlugins: MARKDOWN_REMARK_PLUGINS, - rehypePlugins: MARKDOWN_REHYPE_PLUGINS, - urlTransform: markdownPreviewUrlTransform - }, - content - ) - ) - expect(actual.replace(/>\n+<')).toBe(ordinary.replace(/>\n+<')) - }) - - it('evicts compiled blocks and invalidates searches on document replacement', async () => { + it('invalidates searches on document replacement', async () => { const engine = new MarkdownPreviewDocumentEngine() engine.load('needle\n\n'.repeat(100)) - const first = engine.blocks([0])[0] - expect(engine.blocks([0])[0]).toBe(first) - engine.blocks(Array.from({ length: 40 }, (_, index) => index + 1)) - engine.blocks(Array.from({ length: 40 }, (_, index) => index + 41)) - expect(engine.blocks([0])[0]).not.toBe(first) const pending = engine.search('needle') engine.load('# Replacement') expect(await pending).toBeNull() expect(await engine.search('needle')).toEqual({ matches: [], truncated: false }) - expect(() => engine.load('x'.repeat(8 * 1024 * 1024 + 1))).toThrow('size limit') - expect(() => engine.blocks([0])).toThrow('Invalid preview viewport') }) it('preserves raw HTML nesting across Markdown blocks and sanitizes executable HTML', () => { const { tree } = parseMarkdownPreviewDocument( - '
\nRead\n\n**Nested**\n\n
\n\n\n\n' + '
\nRead\n\n**Nested**\n\n
\n\n' ) const nodes = elements(tree) expect(nodes.some((node) => node.type === 'element' && node.tagName === 'script')).toBe(false) @@ -102,63 +28,9 @@ describe('large Markdown preview documents', () => { properties: { open: true }, children: expect.arrayContaining([expect.objectContaining({ tagName: 'p' })]) }) - expect(nodes.find((node) => node.type === 'element' && node.tagName === 'img')).toMatchObject({ - properties: {} - }) }) - it('keeps code searchable when syntax expansion alone exceeds the node budget', async () => { - const engine = new MarkdownPreviewDocumentEngine() - engine.load(`\`\`\`javascript\n${'const needle = 42;\n'.repeat(900)}\`\`\``) - const block = engine.blocks([0])[0] - expect(block.oversized).toBe(false) - expect(countMarkdownPreviewNodes(block.tree, MARKDOWN_PREVIEW_BLOCK_MAX_NODES)).toBeLessThan( - MARKDOWN_PREVIEW_BLOCK_MAX_NODES - ) - expect((await engine.search('const needle'))?.matches).toHaveLength(900) - }) - - it('rejects giant atomic blocks and excessive input without attempting a full DOM', () => { - const engine = new MarkdownPreviewDocumentEngine() - engine.load(`\`\`\`javascript\n${'const value = 3;\n'.repeat(10_000)}\`\`\``) - expect(engine.blocks([0])).toEqual([ - { index: 0, oversized: true, tree: { type: 'root', children: [] } } - ]) - expect(() => engine.load('x'.repeat(8 * 1024 * 1024 + 1))).toThrow('size limit') - expect(() => engine.load('🙂'.repeat(3 * 1024 * 1024))).toThrow('size limit') - expect(() => engine.blocks([-1])).toThrow('Invalid preview viewport') - expect(() => engine.blocks(Array.from({ length: 41 }, (_, index) => index))).toThrow( - 'Invalid preview viewport' - ) - }) - - it('bounds document and viewport complexity independently of source bytes', () => { - const child = { type: 'text', value: 'x' } - expect( - countMarkdownPreviewNodes( - { children: Array.from({ length: MARKDOWN_PREVIEW_DOCUMENT_MAX_NODES }, () => child) }, - MARKDOWN_PREVIEW_DOCUMENT_MAX_NODES - ) - ).toBe(MARKDOWN_PREVIEW_DOCUMENT_MAX_NODES + 1) - const engine = new MarkdownPreviewDocumentEngine() - engine.load( - Array.from( - { length: 40 }, - () => `| a | b |\n| --- | --- |\n${'| a | b |\n'.repeat(100)}` - ).join('\n\n') - ) - const blocks = engine.blocks(Array.from({ length: 40 }, (_, index) => index)) - expect( - blocks.reduce( - (sum, block) => - sum + countMarkdownPreviewNodes(block.tree, MARKDOWN_PREVIEW_BLOCK_MAX_NODES), - 0 - ) - ).toBeLessThanOrEqual(MARKDOWN_PREVIEW_VIEWPORT_MAX_NODES + 40) - expect(blocks.some((block) => block.oversized)).toBe(true) - }) - - it('finds rendered text beyond the first viewport, including highlighted code and footnotes', async () => { + it('finds code and footnote text in later document blocks', async () => { const engine = new MarkdownPreviewDocumentEngine() engine.load( `# Start\n\n${'Ordinary paragraph.\n\n'.repeat( @@ -168,22 +40,5 @@ describe('large Markdown preview documents', () => { const result = await engine.search('needle') expect(result?.matches).toHaveLength(3) expect(result?.matches.every((match) => match.block > 90)).toBe(true) - expect(await engine.search('missing')).toEqual({ matches: [], truncated: false }) - expect(await engine.search('x'.repeat(2049))).toEqual({ matches: [], truncated: false }) - }) - - it('bounds search results and supersedes work when the query changes', async () => { - const engine = new MarkdownPreviewDocumentEngine() - engine.load(`${'needle '.repeat(MARKDOWN_PREVIEW_MAX_SEARCH_MATCHES)}\n\nend needle\n`) - // One oversized paragraph is explicitly unavailable in the rendered preview. - expect((await engine.search('needle'))?.matches).toHaveLength(1) - engine.load(`${'needle '.repeat(100)}\n\n${`${'needle '.repeat(100)}\n\n`.repeat(110)}`) - const pending = engine.search('needle') - const latest = engine.search('absent') - expect(await pending).toBeNull() - expect(await latest).toEqual({ matches: [], truncated: false }) - const capped = await engine.search('needle') - expect(capped?.matches).toHaveLength(MARKDOWN_PREVIEW_MAX_SEARCH_MATCHES) - expect(capped?.truncated).toBe(true) }) }) diff --git a/src/renderer/src/components/native-chat/links/previews/native-chat-link-peek-context.test.tsx b/src/renderer/src/components/native-chat/links/previews/native-chat-link-peek-context.test.tsx deleted file mode 100644 index 62d314a06ad..00000000000 --- a/src/renderer/src/components/native-chat/links/previews/native-chat-link-peek-context.test.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import { useContext } from 'react' -import { renderToStaticMarkup } from 'react-dom/server' -import { expect, it, vi } from 'vitest' -import type { FolderWorkspace } from '../../../../../../shared/folder-workspaces/folder-workspace-types' -import { - resolveNativeChatFileLinkContextForOwner, - type NativeChatFileLinkState -} from '../actions/native-chat-file-link-context' -import { - NativeChatLinkPeekContext, - NativeChatLinkPeekProvider -} from './native-chat-link-peek-context' - -const mocks = vi.hoisted((): { state: NativeChatFileLinkState | null } => ({ state: null })) -vi.mock('@/store', () => ({ - useAppStore: (select: (state: NativeChatFileLinkState) => unknown) => select(mocks.state!) -})) - -function RoutingHost() { - return useContext(NativeChatLinkPeekContext).githubRoutingAuthorityKey -} - -it.each([ - { owner: {}, runtimeEnvironmentId: null, expectedHost: 'local' }, - { owner: { connectionId: 'box' }, runtimeEnvironmentId: null, expectedHost: 'ssh:box' }, - { - owner: { executionHostId: 'runtime:env' as const }, - runtimeEnvironmentId: 'env', - expectedHost: 'runtime:env' - }, - { - owner: { executionHostId: 'runtime:env' as const, connectionId: 'T' }, - runtimeEnvironmentId: 'env', - expectedHost: 'nested:env/T' - } -])( - 'routes a folder chat preview through $expectedHost', - ({ owner, runtimeEnvironmentId, expectedHost }) => { - const folder: FolderWorkspace = { - id: 'chat', - projectGroupId: 'group', - name: 'Chat', - folderPath: '/workspace', - linkedTask: null, - comment: '', - isArchived: false, - isUnread: false, - isPinned: false, - sortOrder: 0, - lastActivityAt: 0, - createdAt: 0, - updatedAt: 0, - ...owner - } - mocks.state = { - repos: [], - projectGroups: [], - folderWorkspaces: [folder], - settings: null, - worktreesByRepo: {}, - detectedWorktreesByRepo: {}, - tabsByWorktree: {}, - floatingWorkspacePath: null - } - const context = resolveNativeChatFileLinkContextForOwner(mocks.state, 'tab', 'folder:chat') - expect(context?.runtimeEnvironmentId).toBe(runtimeEnvironmentId) - expect( - renderToStaticMarkup( - - - - ) - ).toBe(expectedHost) - } -)