@alumbwe/anvil 1.0.42 → 1.0.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/anvil.js +18 -0
- package/package.json +13 -6
- package/src/agents/bundled-agents.generated.ts +1 -1
- package/src/index.tsx +27 -4
- package/src/login/plain-login.ts +60 -0
- package/src/login/supabase-auth.ts +346 -0
- package/src/utils/anvil-client.ts +4 -0
- package/src/utils/auth.ts +16 -4
- package/src/utils/config-dir.ts +1 -1
- package/vendor/@anvil/common/src/constants/deepseek-direct.ts +30 -1
- package/vendor/@anvil/common/src/util/project-ignore.ts +4 -3
- package/vendor/@anvil/sdk/dist/index.cjs +46 -10
- package/vendor/@anvil/sdk/dist/index.mjs +46 -10
- package/src/commands/__tests__/anvil-command-aliases.test.ts +0 -56
- package/src/commands/__tests__/bash-command.test.ts +0 -445
- package/src/commands/__tests__/command-args.test.ts +0 -349
- package/src/commands/__tests__/copy-conversation-clipboard.test.ts +0 -108
- package/src/commands/__tests__/copy-conversation.test.ts +0 -226
- package/src/commands/__tests__/diagnostics-command.test.ts +0 -35
- package/src/commands/__tests__/image.test.ts +0 -99
- package/src/commands/__tests__/init.test.ts +0 -456
- package/src/commands/__tests__/new-command-rotates-chat.test.ts +0 -91
- package/src/commands/__tests__/process-diagnostics.test.ts +0 -54
- package/src/commands/__tests__/prompt-builders.test.ts +0 -28
- package/src/commands/__tests__/router-input.test.ts +0 -298
- package/src/components/__tests__/ad-banner.test.tsx +0 -83
- package/src/components/__tests__/anvil-landing-heading-row.test.tsx +0 -91
- package/src/components/__tests__/anvil-model-selector.test.tsx +0 -519
- package/src/components/__tests__/anvil-offer-invariants.test.ts +0 -54
- package/src/components/__tests__/anvil-takeover-prompt.test.tsx +0 -212
- package/src/components/__tests__/chat-input-bar.test.tsx +0 -130
- package/src/components/__tests__/grid-layout.integration.test.tsx +0 -304
- package/src/components/__tests__/grid-layout.test.tsx +0 -1051
- package/src/components/__tests__/message-block.completion.test.tsx +0 -104
- package/src/components/__tests__/message-block.streaming.test.tsx +0 -78
- package/src/components/__tests__/message-with-agents.test.tsx +0 -579
- package/src/components/__tests__/multiline-input.test.tsx +0 -1132
- package/src/components/__tests__/queue-panel.test.tsx +0 -398
- package/src/components/__tests__/selectable-list.test.ts +0 -232
- package/src/components/__tests__/status-bar.test.tsx +0 -44
- package/src/components/__tests__/status-indicator.test.tsx +0 -204
- package/src/components/__tests__/terminal-thinking-orb.test.tsx +0 -111
- package/src/components/__tests__/user-error-banner.test.tsx +0 -102
- package/src/components/ask-user/__tests__/multiple-choice-form.test.ts +0 -432
- package/src/components/ask-user/__tests__/validation.test.ts +0 -213
- package/src/components/tools/__tests__/apply-patch.test.tsx +0 -84
- package/src/components/tools/__tests__/code-search.test.tsx +0 -45
- package/src/components/tools/__tests__/gravity-index.test.ts +0 -76
- package/src/components/tools/__tests__/render-ui.test.tsx +0 -92
- package/src/components/tools/__tests__/run-terminal-command.test.ts +0 -233
- package/src/contexts/__tests__/chat-runtime-context.test.tsx +0 -149
- package/src/data/slash-commands.test.ts +0 -24
- package/src/hooks/__tests__/holds-live-anvil-slot.test.ts +0 -49
- package/src/hooks/__tests__/session-fetch-signal.test.ts +0 -63
- package/src/hooks/__tests__/use-activity-query.test.ts +0 -1000
- package/src/hooks/__tests__/use-ask-user-bridge.test.ts +0 -176
- package/src/hooks/__tests__/use-auth-query.test.ts +0 -21
- package/src/hooks/__tests__/use-chat-input-remount.test.tsx +0 -66
- package/src/hooks/__tests__/use-clipboard.test.tsx +0 -73
- package/src/hooks/__tests__/use-connection-status.test.ts +0 -112
- package/src/hooks/__tests__/use-directory-browser.test.ts +0 -203
- package/src/hooks/__tests__/use-gravity-ad.test.ts +0 -73
- package/src/hooks/__tests__/use-grid-layout.test.ts +0 -349
- package/src/hooks/__tests__/use-input-history.test.ts +0 -699
- package/src/hooks/__tests__/use-message-queue-editing.test.tsx +0 -209
- package/src/hooks/__tests__/use-message-queue.test.tsx +0 -71
- package/src/hooks/__tests__/use-path-tab-completion.test.ts +0 -315
- package/src/hooks/__tests__/use-queue-controls.test.ts +0 -68
- package/src/hooks/__tests__/use-queue-ui.test.tsx +0 -83
- package/src/hooks/__tests__/use-searchable-list.test.ts +0 -359
- package/src/hooks/__tests__/use-send-message-timer.test.ts +0 -146
- package/src/hooks/__tests__/use-suggestion-engine-mention.test.ts +0 -361
- package/src/hooks/__tests__/use-suggestion-engine.test.ts +0 -485
- package/src/hooks/__tests__/use-terminal-focus.test.ts +0 -66
- package/src/hooks/__tests__/use-terminal-layout.test.ts +0 -675
- package/src/hooks/__tests__/use-timeout.test.ts +0 -330
- package/src/hooks/__tests__/use-usage-query.test.ts +0 -502
- package/src/hooks/__tests__/use-user-details-query.test.ts +0 -195
- package/src/hooks/helpers/__tests__/send-message.test.ts +0 -1918
- package/src/init/__tests__/init-direnv.test.ts +0 -526
- package/src/state/__tests__/chat-store-focus.test.ts +0 -22
- package/src/state/__tests__/feedback-store.test.ts +0 -257
- package/src/utils/__tests__/active-run.test.ts +0 -140
- package/src/utils/__tests__/activity-tracker.test.ts +0 -440
- package/src/utils/__tests__/agent-display.test.ts +0 -139
- package/src/utils/__tests__/analytics-client.test.ts +0 -262
- package/src/utils/__tests__/anonymous-id.test.ts +0 -74
- package/src/utils/__tests__/anvil-api.test.ts +0 -578
- package/src/utils/__tests__/anvil-instance-owner.test.ts +0 -69
- package/src/utils/__tests__/anvil-model-navigation.test.ts +0 -102
- package/src/utils/__tests__/anvil-premium-reset.test.ts +0 -79
- package/src/utils/__tests__/anvil-referral-cache.test.ts +0 -116
- package/src/utils/__tests__/anvil-session-api.test.ts +0 -215
- package/src/utils/__tests__/anvil-session-display.test.ts +0 -23
- package/src/utils/__tests__/anvil-streak-line.test.ts +0 -217
- package/src/utils/__tests__/arrays.test.ts +0 -264
- package/src/utils/__tests__/bash-context-processor.test.ts +0 -38
- package/src/utils/__tests__/block-processor.test.ts +0 -810
- package/src/utils/__tests__/chat-history.test.ts +0 -203
- package/src/utils/__tests__/chat-input-key-intercept.test.ts +0 -40
- package/src/utils/__tests__/chat-meta.test.ts +0 -130
- package/src/utils/__tests__/clipboard-linux.test.ts +0 -359
- package/src/utils/__tests__/clipboard.test.ts +0 -910
- package/src/utils/__tests__/code-search-summary.test.ts +0 -84
- package/src/utils/__tests__/collapse-helpers.test.ts +0 -1109
- package/src/utils/__tests__/error-handling.test.ts +0 -658
- package/src/utils/__tests__/exit-cleanly.test.ts +0 -110
- package/src/utils/__tests__/feedback-helpers.test.ts +0 -444
- package/src/utils/__tests__/feedback-submission.test.ts +0 -26
- package/src/utils/__tests__/fetch-usage.test.ts +0 -355
- package/src/utils/__tests__/fingerprint.test.ts +0 -144
- package/src/utils/__tests__/fixtures/terminal-command-broker-child.ts +0 -7
- package/src/utils/__tests__/fixtures/terminal-command-broker-entry.ts +0 -3
- package/src/utils/__tests__/fixtures/terminal-command-broker-owner.ts +0 -33
- package/src/utils/__tests__/format-elapsed-time.test.ts +0 -103
- package/src/utils/__tests__/format-timeout.test.ts +0 -87
- package/src/utils/__tests__/image-dimensions.test.ts +0 -249
- package/src/utils/__tests__/image-processor.test.ts +0 -209
- package/src/utils/__tests__/implementor-helpers.test.ts +0 -1458
- package/src/utils/__tests__/keyboard-actions.test.ts +0 -660
- package/src/utils/__tests__/layout-helpers.test.ts +0 -36
- package/src/utils/__tests__/lazy-response-ads.test.ts +0 -167
- package/src/utils/__tests__/log-shipper.test.ts +0 -53
- package/src/utils/__tests__/markdown-renderer.test.tsx +0 -421
- package/src/utils/__tests__/message-block-helpers.test.ts +0 -1263
- package/src/utils/__tests__/message-updater.test.ts +0 -645
- package/src/utils/__tests__/osc-timeout-scenarios.test.ts +0 -172
- package/src/utils/__tests__/pending-attachments.test.ts +0 -277
- package/src/utils/__tests__/polling-backoff.test.ts +0 -51
- package/src/utils/__tests__/queue-panel-actions.test.ts +0 -148
- package/src/utils/__tests__/run-state-storage.test.ts +0 -919
- package/src/utils/__tests__/safe-json.test.ts +0 -77
- package/src/utils/__tests__/sdk-event-handlers.test.ts +0 -685
- package/src/utils/__tests__/send-message-helpers.test.ts +0 -1898
- package/src/utils/__tests__/send-message-timer.test.ts +0 -64
- package/src/utils/__tests__/settings.test.ts +0 -99
- package/src/utils/__tests__/strings.test.ts +0 -192
- package/src/utils/__tests__/terminal-color-detection.test.ts +0 -425
- package/src/utils/__tests__/terminal-command-broker.test.ts +0 -528
- package/src/utils/__tests__/terminal-enter-detection.test.ts +0 -77
- package/src/utils/__tests__/terminal-io.test.ts +0 -137
- package/src/utils/__tests__/terminal-protocol-controller.test.ts +0 -119
- package/src/utils/__tests__/text-layout.test.ts +0 -82
- package/src/utils/__tests__/theme-platform-detection.test.ts +0 -125
- package/src/utils/__tests__/think-tag-parser.test.ts +0 -159
- package/src/utils/__tests__/trace-writer.test.ts +0 -223
- package/src/utils/__tests__/trim-chat-logs.test.ts +0 -78
- package/src/utils/__tests__/usage-banner-state.test.ts +0 -298
- package/src/utils/__tests__/validation-error-formatting.test.ts +0 -126
- package/src/utils/__tests__/windows-terminal-health.test.ts +0 -101
- package/src/utils/__tests__/write-file-atomic.test.ts +0 -127
- package/vendor/@anvil/common/src/__tests__/agent-validation.test.ts +0 -846
- package/vendor/@anvil/common/src/__tests__/anvil-model-availability.test.ts +0 -101
- package/vendor/@anvil/common/src/__tests__/anvil-models.test.ts +0 -1408
- package/vendor/@anvil/common/src/__tests__/anvil-public-data-use-copy.test.ts +0 -258
- package/vendor/@anvil/common/src/__tests__/anvil-referral-tiers.test.ts +0 -48
- package/vendor/@anvil/common/src/__tests__/anvil-spend-ceilings.test.ts +0 -282
- package/vendor/@anvil/common/src/__tests__/anvil-trust.test.ts +0 -526
- package/vendor/@anvil/common/src/__tests__/deepseek-direct.test.ts +0 -68
- package/vendor/@anvil/common/src/__tests__/disposable-email.test.ts +0 -132
- package/vendor/@anvil/common/src/__tests__/dynamic-agent-template-schema.test.ts +0 -422
- package/vendor/@anvil/common/src/__tests__/env-ci.test.ts +0 -167
- package/vendor/@anvil/common/src/__tests__/env-process.test.ts +0 -145
- package/vendor/@anvil/common/src/__tests__/foreign-client-shipped-agents.test.ts +0 -234
- package/vendor/@anvil/common/src/__tests__/foreign-client-signals.test.ts +0 -379
- package/vendor/@anvil/common/src/__tests__/free-agents.test.ts +0 -807
- package/vendor/@anvil/common/src/__tests__/gravity-capi.test.ts +0 -154
- package/vendor/@anvil/common/src/__tests__/handlesteps-parsing.test.ts +0 -246
- package/vendor/@anvil/common/src/__tests__/kimi-k3-god-only.test.ts +0 -77
- package/vendor/@anvil/common/src/__tests__/model-config.test.ts +0 -89
- package/vendor/@anvil/common/src/__tests__/project-file-tree.test.ts +0 -298
- package/vendor/@anvil/common/src/__tests__/provisioned-model-tiers.test.ts +0 -116
- package/vendor/@anvil/common/src/__tests__/reasoning-effort.test.ts +0 -167
- package/vendor/@anvil/common/src/__tests__/reddit-capi.test.ts +0 -132
- package/vendor/@anvil/common/src/__tests__/response-ad-positions.test.ts +0 -72
- package/vendor/@anvil/common/src/__tests__/user-state.test.ts +0 -30
- package/vendor/@anvil/common/src/constants/__tests__/anvil-onboarding-gate.test.ts +0 -63
- package/vendor/@anvil/common/src/constants/__tests__/anvil-onboarding.test.ts +0 -246
- package/vendor/@anvil/common/src/constants/__tests__/cf-worker-signals.test.ts +0 -106
- package/vendor/@anvil/common/src/testing/TESTING_PATTERNS.md +0 -351
- package/vendor/@anvil/common/src/testing/anvil-offer-invariants.ts +0 -169
- package/vendor/@anvil/common/src/testing/errors.ts +0 -33
- package/vendor/@anvil/common/src/testing/fixtures/agent-runtime.ts +0 -334
- package/vendor/@anvil/common/src/testing/impl/agent-runtime.ts +0 -6
- package/vendor/@anvil/common/src/testing/index.ts +0 -84
- package/vendor/@anvil/common/src/testing/mock-modules.ts +0 -53
- package/vendor/@anvil/common/src/testing/mock-types.ts +0 -123
- package/vendor/@anvil/common/src/testing/mocks/analytics.ts +0 -261
- package/vendor/@anvil/common/src/testing/mocks/child-process.ts +0 -93
- package/vendor/@anvil/common/src/testing/mocks/crypto.ts +0 -218
- package/vendor/@anvil/common/src/testing/mocks/database.ts +0 -337
- package/vendor/@anvil/common/src/testing/mocks/fetch.ts +0 -219
- package/vendor/@anvil/common/src/testing/mocks/filesystem.ts +0 -166
- package/vendor/@anvil/common/src/testing/mocks/index.ts +0 -101
- package/vendor/@anvil/common/src/testing/mocks/logger.ts +0 -135
- package/vendor/@anvil/common/src/testing/mocks/stream.ts +0 -313
- package/vendor/@anvil/common/src/testing/mocks/timers.ts +0 -132
- package/vendor/@anvil/common/src/testing/mocks/tree-sitter.ts +0 -127
- package/vendor/@anvil/common/src/testing/setup.ts +0 -282
- package/vendor/@anvil/common/src/tools/__tests__/compile-tool-definitions.test.ts +0 -34
- package/vendor/@anvil/common/src/tools/params/__tests__/coerce-to-array.test.ts +0 -213
- package/vendor/@anvil/common/src/tools/params/__tests__/read-docs.test.ts +0 -47
- package/vendor/@anvil/common/src/tools/params/__tests__/render-ui.test.ts +0 -65
- package/vendor/@anvil/common/src/tools/params/tool/__tests__/run-terminal-command-timeout.test.ts +0 -49
- package/vendor/@anvil/common/src/types/__tests__/dynamic-agent-template.test.ts +0 -20
- package/vendor/@anvil/common/src/util/__tests__/ad-user-agent.test.ts +0 -26
- package/vendor/@anvil/common/src/util/__tests__/analytics-dispatcher.test.ts +0 -122
- package/vendor/@anvil/common/src/util/__tests__/analytics-log.test.ts +0 -102
- package/vendor/@anvil/common/src/util/__tests__/analytics-sampling.test.ts +0 -143
- package/vendor/@anvil/common/src/util/__tests__/anvil-streak.test.ts +0 -202
- package/vendor/@anvil/common/src/util/__tests__/anvil-usage-summary.test.ts +0 -193
- package/vendor/@anvil/common/src/util/__tests__/axiom-only-log.test.ts +0 -140
- package/vendor/@anvil/common/src/util/__tests__/client-user-agent.test.ts +0 -148
- package/vendor/@anvil/common/src/util/__tests__/engagement-tracker.test.ts +0 -115
- package/vendor/@anvil/common/src/util/__tests__/env-file-path.test.ts +0 -57
- package/vendor/@anvil/common/src/util/__tests__/error-abort.test.ts +0 -774
- package/vendor/@anvil/common/src/util/__tests__/error-api-details.test.ts +0 -206
- package/vendor/@anvil/common/src/util/__tests__/file-read-limits.test.ts +0 -173
- package/vendor/@anvil/common/src/util/__tests__/format-code-search.test.ts +0 -60
- package/vendor/@anvil/common/src/util/__tests__/log-mirror.test.ts +0 -31
- package/vendor/@anvil/common/src/util/__tests__/messages.test.ts +0 -1252
- package/vendor/@anvil/common/src/util/__tests__/partial-json-delta.test.ts +0 -505
- package/vendor/@anvil/common/src/util/__tests__/path.test.ts +0 -22
- package/vendor/@anvil/common/src/util/__tests__/project-ignore.test.ts +0 -90
- package/vendor/@anvil/common/src/util/__tests__/promise.test.ts +0 -325
- package/vendor/@anvil/common/src/util/__tests__/rate-limit.test.ts +0 -28
- package/vendor/@anvil/common/src/util/__tests__/reddit-anvil-retention.test.ts +0 -130
- package/vendor/@anvil/common/src/util/__tests__/saxy.test.ts +0 -1008
- package/vendor/@anvil/common/src/util/__tests__/split-data.test.ts +0 -289
- package/vendor/@anvil/common/src/util/__tests__/string.test.ts +0 -239
- package/vendor/@anvil/common/src/util/__tests__/thread-title.test.ts +0 -70
- package/vendor/@anvil/common/src/util/__tests__/tool-result-media-order.test.ts +0 -108
- package/vendor/@anvil/common/src/util/__tests__/ttft-histogram.test.ts +0 -180
- package/vendor/@anvil/common/src/util/__tests__/with-timeout.test.ts +0 -24
- package/vendor/@anvil/common/src/util/__tests__/zoned-time.test.ts +0 -88
- package/vendor/@anvil/common/src/util/skills.test.ts +0 -44
- package/vendor/@anvil/sdk/dist/index.cjs.map +0 -440
- package/vendor/@anvil/sdk/dist/index.mjs.map +0 -440
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from 'bun:test'
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
claimAdImpression,
|
|
5
|
-
isAnswerMessage,
|
|
6
|
-
isInlineAdEligibleAnswer,
|
|
7
|
-
} from '../use-gravity-ad'
|
|
8
|
-
|
|
9
|
-
import type { ChatMessage } from '../../types/chat'
|
|
10
|
-
|
|
11
|
-
const msg = (over: Partial<ChatMessage>): ChatMessage => ({
|
|
12
|
-
id: 'user-1',
|
|
13
|
-
variant: 'user',
|
|
14
|
-
content: 'hello',
|
|
15
|
-
timestamp: '',
|
|
16
|
-
...over,
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
// Only genuine streamed LLM answers (id 'ai-…', top-level) receive
|
|
20
|
-
// interspersed ads — not bash echoes or system notices.
|
|
21
|
-
describe('isAnswerMessage', () => {
|
|
22
|
-
const aiMsg = (over: Partial<ChatMessage>): ChatMessage =>
|
|
23
|
-
msg({ id: 'ai-1', variant: 'ai', content: '', ...over })
|
|
24
|
-
|
|
25
|
-
test('accepts a top-level streamed answer (even mid-stream)', () => {
|
|
26
|
-
expect(isAnswerMessage(aiMsg({}))).toBe(true)
|
|
27
|
-
expect(isAnswerMessage(aiMsg({ isComplete: false }))).toBe(true)
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
test('rejects bash echoes, system notices, and nested messages', () => {
|
|
31
|
-
expect(isAnswerMessage(aiMsg({ id: 'bash-result-x' }))).toBe(false)
|
|
32
|
-
expect(isAnswerMessage(aiMsg({ id: 'sys-1' }))).toBe(false)
|
|
33
|
-
expect(isAnswerMessage(aiMsg({ parentId: 'ai-0' }))).toBe(false)
|
|
34
|
-
expect(isAnswerMessage(msg({}))).toBe(false)
|
|
35
|
-
})
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
describe('isInlineAdEligibleAnswer', () => {
|
|
39
|
-
test('only accepts live response shells', () => {
|
|
40
|
-
expect(
|
|
41
|
-
isInlineAdEligibleAnswer(
|
|
42
|
-
msg({
|
|
43
|
-
id: 'ai-live',
|
|
44
|
-
variant: 'ai',
|
|
45
|
-
metadata: { allowInlineAds: true },
|
|
46
|
-
}),
|
|
47
|
-
),
|
|
48
|
-
).toBe(true)
|
|
49
|
-
expect(
|
|
50
|
-
isInlineAdEligibleAnswer(msg({ id: 'ai-restored', variant: 'ai' })),
|
|
51
|
-
).toBe(false)
|
|
52
|
-
expect(
|
|
53
|
-
isInlineAdEligibleAnswer(
|
|
54
|
-
msg({
|
|
55
|
-
id: 'sys-1',
|
|
56
|
-
variant: 'ai',
|
|
57
|
-
metadata: { allowInlineAds: true },
|
|
58
|
-
}),
|
|
59
|
-
),
|
|
60
|
-
).toBe(false)
|
|
61
|
-
})
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
describe('claimAdImpression', () => {
|
|
65
|
-
test('claims each distinct ad once even when its card is repeated', () => {
|
|
66
|
-
const fired = new Set<string>()
|
|
67
|
-
|
|
68
|
-
expect(claimAdImpression(fired, 'imp-1')).toBe(true)
|
|
69
|
-
expect(claimAdImpression(fired, 'imp-2')).toBe(true)
|
|
70
|
-
expect(claimAdImpression(fired, 'imp-1')).toBe(false)
|
|
71
|
-
expect(fired).toEqual(new Set(['imp-1', 'imp-2']))
|
|
72
|
-
})
|
|
73
|
-
})
|
|
@@ -1,349 +0,0 @@
|
|
|
1
|
-
import { describe, test, expect } from 'bun:test'
|
|
2
|
-
|
|
3
|
-
import { MIN_COLUMN_WIDTH } from '../../utils/layout-helpers'
|
|
4
|
-
import {
|
|
5
|
-
computeGridLayout,
|
|
6
|
-
WIDTH_MD_THRESHOLD,
|
|
7
|
-
WIDTH_LG_THRESHOLD,
|
|
8
|
-
WIDTH_XL_THRESHOLD,
|
|
9
|
-
} from '../use-grid-layout'
|
|
10
|
-
|
|
11
|
-
describe('computeGridLayout', () => {
|
|
12
|
-
describe('threshold constants', () => {
|
|
13
|
-
test('thresholds are in ascending order', () => {
|
|
14
|
-
expect(WIDTH_MD_THRESHOLD).toBeLessThan(WIDTH_LG_THRESHOLD)
|
|
15
|
-
expect(WIDTH_LG_THRESHOLD).toBeLessThan(WIDTH_XL_THRESHOLD)
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
test('WIDTH_MD_THRESHOLD is 100', () => {
|
|
19
|
-
expect(WIDTH_MD_THRESHOLD).toBe(100)
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
test('WIDTH_LG_THRESHOLD is 150', () => {
|
|
23
|
-
expect(WIDTH_LG_THRESHOLD).toBe(150)
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
test('WIDTH_XL_THRESHOLD is 200', () => {
|
|
27
|
-
expect(WIDTH_XL_THRESHOLD).toBe(200)
|
|
28
|
-
})
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
describe('maxColumns based on availableWidth', () => {
|
|
32
|
-
test('narrow width (< 100) gets 1 column max', () => {
|
|
33
|
-
const items = ['a', 'b', 'c', 'd']
|
|
34
|
-
const result = computeGridLayout(items, 80)
|
|
35
|
-
expect(result.columns).toBe(1)
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
test('medium width (100-149) gets 2 columns max', () => {
|
|
39
|
-
const items = ['a', 'b', 'c', 'd']
|
|
40
|
-
const result = computeGridLayout(items, 120)
|
|
41
|
-
expect(result.columns).toBe(2)
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
test('large width (150-199) gets 3 columns max', () => {
|
|
45
|
-
const items = ['a', 'b', 'c', 'd', 'e', 'f']
|
|
46
|
-
const result = computeGridLayout(items, 180)
|
|
47
|
-
expect(result.columns).toBe(3)
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
test('extra large width (>= 200) gets 4 columns max', () => {
|
|
51
|
-
const items = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
|
|
52
|
-
const result = computeGridLayout(items, 250)
|
|
53
|
-
expect(result.columns).toBe(4)
|
|
54
|
-
})
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
describe('threshold boundaries', () => {
|
|
58
|
-
test('width 99 gives 1 column max', () => {
|
|
59
|
-
const items = ['a', 'b', 'c']
|
|
60
|
-
const result = computeGridLayout(items, 99)
|
|
61
|
-
expect(result.columns).toBe(1)
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
test('width 100 gives 2 columns max', () => {
|
|
65
|
-
const items = ['a', 'b', 'c']
|
|
66
|
-
const result = computeGridLayout(items, 100)
|
|
67
|
-
expect(result.columns).toBe(2)
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
test('width 149 gives 2 columns max', () => {
|
|
71
|
-
const items = ['a', 'b', 'c']
|
|
72
|
-
const result = computeGridLayout(items, 149)
|
|
73
|
-
expect(result.columns).toBe(2)
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
test('width 150 gives 3 columns max', () => {
|
|
77
|
-
const items = ['a', 'b', 'c']
|
|
78
|
-
const result = computeGridLayout(items, 150)
|
|
79
|
-
expect(result.columns).toBe(3)
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
test('width 199 gives 3 columns max (but 4 items prefer 2x2)', () => {
|
|
83
|
-
// 4 items with maxColumns=3 prefers 2 columns (2x2 grid) via computeSmartColumns
|
|
84
|
-
const items = ['a', 'b', 'c', 'd']
|
|
85
|
-
const result = computeGridLayout(items, 199)
|
|
86
|
-
expect(result.columns).toBe(2)
|
|
87
|
-
|
|
88
|
-
// 3 items actually uses 3 columns
|
|
89
|
-
const threeItems = ['a', 'b', 'c']
|
|
90
|
-
const result3 = computeGridLayout(threeItems, 199)
|
|
91
|
-
expect(result3.columns).toBe(3)
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
test('width 200 gives 4 columns max', () => {
|
|
95
|
-
const items = ['a', 'b', 'c', 'd']
|
|
96
|
-
const result = computeGridLayout(items, 200)
|
|
97
|
-
expect(result.columns).toBe(4)
|
|
98
|
-
})
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
describe('column count based on item count', () => {
|
|
102
|
-
test('0 items gives 1 column', () => {
|
|
103
|
-
const result = computeGridLayout([], 200)
|
|
104
|
-
expect(result.columns).toBe(1)
|
|
105
|
-
})
|
|
106
|
-
|
|
107
|
-
test('1 item gives 1 column', () => {
|
|
108
|
-
const result = computeGridLayout(['a'], 200)
|
|
109
|
-
expect(result.columns).toBe(1)
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
test('2 items on wide screen gives 2 columns', () => {
|
|
113
|
-
const result = computeGridLayout(['a', 'b'], 200)
|
|
114
|
-
expect(result.columns).toBe(2)
|
|
115
|
-
})
|
|
116
|
-
|
|
117
|
-
test('3 items on wide screen gives 3 columns', () => {
|
|
118
|
-
const result = computeGridLayout(['a', 'b', 'c'], 200)
|
|
119
|
-
expect(result.columns).toBe(3)
|
|
120
|
-
})
|
|
121
|
-
|
|
122
|
-
test('4 items on 3-column max gives 2 columns (2x2 grid)', () => {
|
|
123
|
-
const result = computeGridLayout(['a', 'b', 'c', 'd'], 180)
|
|
124
|
-
expect(result.columns).toBe(2)
|
|
125
|
-
})
|
|
126
|
-
|
|
127
|
-
test('6 items on 3-column max gives 3 columns', () => {
|
|
128
|
-
const result = computeGridLayout(['a', 'b', 'c', 'd', 'e', 'f'], 180)
|
|
129
|
-
expect(result.columns).toBe(3)
|
|
130
|
-
})
|
|
131
|
-
})
|
|
132
|
-
|
|
133
|
-
describe('columnWidth calculation', () => {
|
|
134
|
-
test('single column uses full availableWidth', () => {
|
|
135
|
-
const result = computeGridLayout(['a'], 120)
|
|
136
|
-
expect(result.columnWidth).toBe(120)
|
|
137
|
-
})
|
|
138
|
-
|
|
139
|
-
test('2 columns splits width with 1 char gap', () => {
|
|
140
|
-
const result = computeGridLayout(['a', 'b'], 121)
|
|
141
|
-
// 121 - 1 gap = 120, divided by 2 = 60
|
|
142
|
-
expect(result.columnWidth).toBe(60)
|
|
143
|
-
})
|
|
144
|
-
|
|
145
|
-
test('3 columns splits width with 2 char gaps', () => {
|
|
146
|
-
const result = computeGridLayout(['a', 'b', 'c'], 182)
|
|
147
|
-
// 182 - 2 gaps = 180, divided by 3 = 60
|
|
148
|
-
expect(result.columnWidth).toBe(60)
|
|
149
|
-
})
|
|
150
|
-
|
|
151
|
-
test('4 columns splits width with 3 char gaps', () => {
|
|
152
|
-
const result = computeGridLayout(['a', 'b', 'c', 'd'], 243)
|
|
153
|
-
// 243 - 3 gaps = 240, divided by 4 = 60
|
|
154
|
-
expect(result.columnWidth).toBe(60)
|
|
155
|
-
})
|
|
156
|
-
|
|
157
|
-
test('columnWidth respects MIN_COLUMN_WIDTH', () => {
|
|
158
|
-
const result = computeGridLayout(['a', 'b', 'c', 'd'], 200)
|
|
159
|
-
expect(result.columnWidth).toBeGreaterThanOrEqual(MIN_COLUMN_WIDTH)
|
|
160
|
-
})
|
|
161
|
-
|
|
162
|
-
test('very narrow width with multiple items clamps to MIN_COLUMN_WIDTH', () => {
|
|
163
|
-
// Force 2 columns with narrow width
|
|
164
|
-
const result = computeGridLayout(['a', 'b'], 105)
|
|
165
|
-
// 105 - 1 gap = 104, divided by 2 = 52
|
|
166
|
-
expect(result.columnWidth).toBe(52)
|
|
167
|
-
})
|
|
168
|
-
})
|
|
169
|
-
|
|
170
|
-
describe('columnGroups distribution (round-robin)', () => {
|
|
171
|
-
test('empty items gives single empty column', () => {
|
|
172
|
-
const result = computeGridLayout([], 200)
|
|
173
|
-
expect(result.columnGroups).toEqual([[]])
|
|
174
|
-
})
|
|
175
|
-
|
|
176
|
-
test('1 item in 1 column', () => {
|
|
177
|
-
const result = computeGridLayout(['a'], 200)
|
|
178
|
-
expect(result.columnGroups).toEqual([['a']])
|
|
179
|
-
})
|
|
180
|
-
|
|
181
|
-
test('2 items distributed across 2 columns', () => {
|
|
182
|
-
const result = computeGridLayout(['a', 'b'], 200)
|
|
183
|
-
expect(result.columnGroups).toEqual([['a'], ['b']])
|
|
184
|
-
})
|
|
185
|
-
|
|
186
|
-
test('3 items distributed across 3 columns', () => {
|
|
187
|
-
const result = computeGridLayout(['a', 'b', 'c'], 200)
|
|
188
|
-
expect(result.columnGroups).toEqual([['a'], ['b'], ['c']])
|
|
189
|
-
})
|
|
190
|
-
|
|
191
|
-
test('4 items in 2 columns (round-robin)', () => {
|
|
192
|
-
const result = computeGridLayout(['a', 'b', 'c', 'd'], 120)
|
|
193
|
-
expect(result.columnGroups).toEqual([
|
|
194
|
-
['a', 'c'],
|
|
195
|
-
['b', 'd'],
|
|
196
|
-
])
|
|
197
|
-
})
|
|
198
|
-
|
|
199
|
-
test('5 items in 2 columns (uneven distribution)', () => {
|
|
200
|
-
const result = computeGridLayout(['a', 'b', 'c', 'd', 'e'], 120)
|
|
201
|
-
expect(result.columnGroups).toEqual([
|
|
202
|
-
['a', 'c', 'e'],
|
|
203
|
-
['b', 'd'],
|
|
204
|
-
])
|
|
205
|
-
})
|
|
206
|
-
|
|
207
|
-
test('6 items in 3 columns', () => {
|
|
208
|
-
const result = computeGridLayout(['a', 'b', 'c', 'd', 'e', 'f'], 180)
|
|
209
|
-
expect(result.columnGroups).toEqual([
|
|
210
|
-
['a', 'd'],
|
|
211
|
-
['b', 'e'],
|
|
212
|
-
['c', 'f'],
|
|
213
|
-
])
|
|
214
|
-
})
|
|
215
|
-
|
|
216
|
-
test('7 items in 3 columns (uneven)', () => {
|
|
217
|
-
const result = computeGridLayout(
|
|
218
|
-
['a', 'b', 'c', 'd', 'e', 'f', 'g'],
|
|
219
|
-
180,
|
|
220
|
-
)
|
|
221
|
-
expect(result.columnGroups).toEqual([
|
|
222
|
-
['a', 'd', 'g'],
|
|
223
|
-
['b', 'e'],
|
|
224
|
-
['c', 'f'],
|
|
225
|
-
])
|
|
226
|
-
})
|
|
227
|
-
})
|
|
228
|
-
|
|
229
|
-
describe('return value structure', () => {
|
|
230
|
-
test('returns all expected properties', () => {
|
|
231
|
-
const result = computeGridLayout(['a', 'b'], 120)
|
|
232
|
-
expect(result).toHaveProperty('columns')
|
|
233
|
-
expect(result).toHaveProperty('columnWidth')
|
|
234
|
-
expect(result).toHaveProperty('columnGroups')
|
|
235
|
-
})
|
|
236
|
-
|
|
237
|
-
test('columns is a positive integer', () => {
|
|
238
|
-
const result = computeGridLayout(['a', 'b', 'c'], 150)
|
|
239
|
-
expect(Number.isInteger(result.columns)).toBe(true)
|
|
240
|
-
expect(result.columns).toBeGreaterThan(0)
|
|
241
|
-
})
|
|
242
|
-
|
|
243
|
-
test('columnWidth is a positive number', () => {
|
|
244
|
-
const result = computeGridLayout(['a', 'b'], 120)
|
|
245
|
-
expect(result.columnWidth).toBeGreaterThan(0)
|
|
246
|
-
})
|
|
247
|
-
|
|
248
|
-
test('columnGroups length matches columns', () => {
|
|
249
|
-
const result = computeGridLayout(['a', 'b', 'c'], 150)
|
|
250
|
-
expect(result.columnGroups.length).toBe(result.columns)
|
|
251
|
-
})
|
|
252
|
-
|
|
253
|
-
test('total items in columnGroups equals input items', () => {
|
|
254
|
-
const items = ['a', 'b', 'c', 'd', 'e']
|
|
255
|
-
const result = computeGridLayout(items, 120)
|
|
256
|
-
const totalItems = result.columnGroups.flat().length
|
|
257
|
-
expect(totalItems).toBe(items.length)
|
|
258
|
-
})
|
|
259
|
-
})
|
|
260
|
-
|
|
261
|
-
describe('generic type support', () => {
|
|
262
|
-
test('works with number items', () => {
|
|
263
|
-
const result = computeGridLayout([1, 2, 3, 4], 120)
|
|
264
|
-
expect(result.columnGroups).toEqual([
|
|
265
|
-
[1, 3],
|
|
266
|
-
[2, 4],
|
|
267
|
-
])
|
|
268
|
-
})
|
|
269
|
-
|
|
270
|
-
test('works with object items', () => {
|
|
271
|
-
const items = [{ id: 1 }, { id: 2 }, { id: 3 }]
|
|
272
|
-
const result = computeGridLayout(items, 150)
|
|
273
|
-
expect(result.columnGroups[0][0]).toEqual({ id: 1 })
|
|
274
|
-
expect(result.columnGroups[1][0]).toEqual({ id: 2 })
|
|
275
|
-
expect(result.columnGroups[2][0]).toEqual({ id: 3 })
|
|
276
|
-
})
|
|
277
|
-
|
|
278
|
-
test('preserves item references', () => {
|
|
279
|
-
const obj1 = { id: 1 }
|
|
280
|
-
const obj2 = { id: 2 }
|
|
281
|
-
const result = computeGridLayout([obj1, obj2], 120)
|
|
282
|
-
expect(result.columnGroups[0][0]).toBe(obj1)
|
|
283
|
-
expect(result.columnGroups[1][0]).toBe(obj2)
|
|
284
|
-
})
|
|
285
|
-
})
|
|
286
|
-
|
|
287
|
-
describe('edge cases', () => {
|
|
288
|
-
test('very small availableWidth (< MIN_COLUMN_WIDTH)', () => {
|
|
289
|
-
const result = computeGridLayout(['a', 'b'], 5)
|
|
290
|
-
expect(result.columns).toBe(1)
|
|
291
|
-
expect(result.columnWidth).toBe(5)
|
|
292
|
-
})
|
|
293
|
-
|
|
294
|
-
test('zero availableWidth clamps columnWidth to 1', () => {
|
|
295
|
-
const result = computeGridLayout(['a'], 0)
|
|
296
|
-
expect(result.columns).toBe(1)
|
|
297
|
-
// columnWidth is clamped to at least 1 to prevent layout issues
|
|
298
|
-
expect(result.columnWidth).toBe(1)
|
|
299
|
-
})
|
|
300
|
-
|
|
301
|
-
test('negative availableWidth clamps columnWidth to 1', () => {
|
|
302
|
-
const result = computeGridLayout(['a'], -10)
|
|
303
|
-
expect(result.columns).toBe(1)
|
|
304
|
-
// columnWidth is clamped to at least 1 to prevent layout issues
|
|
305
|
-
expect(result.columnWidth).toBe(1)
|
|
306
|
-
})
|
|
307
|
-
|
|
308
|
-
test('large number of items', () => {
|
|
309
|
-
const items = Array.from({ length: 100 }, (_, i) => i)
|
|
310
|
-
const result = computeGridLayout(items, 250)
|
|
311
|
-
expect(result.columns).toBe(4)
|
|
312
|
-
expect(result.columnGroups.length).toBe(4)
|
|
313
|
-
expect(result.columnGroups.flat().length).toBe(100)
|
|
314
|
-
})
|
|
315
|
-
|
|
316
|
-
test('fractional availableWidth is floored for columnWidth', () => {
|
|
317
|
-
const result = computeGridLayout(['a', 'b'], 121)
|
|
318
|
-
// (121 - 1) / 2 = 60
|
|
319
|
-
expect(result.columnWidth).toBe(60)
|
|
320
|
-
})
|
|
321
|
-
})
|
|
322
|
-
|
|
323
|
-
describe('consistency', () => {
|
|
324
|
-
test('same input always produces same output', () => {
|
|
325
|
-
const items = ['a', 'b', 'c', 'd']
|
|
326
|
-
const width = 150
|
|
327
|
-
|
|
328
|
-
const result1 = computeGridLayout(items, width)
|
|
329
|
-
const result2 = computeGridLayout(items, width)
|
|
330
|
-
const result3 = computeGridLayout(items, width)
|
|
331
|
-
|
|
332
|
-
expect(result1.columns).toBe(result2.columns)
|
|
333
|
-
expect(result2.columns).toBe(result3.columns)
|
|
334
|
-
expect(result1.columnWidth).toBe(result2.columnWidth)
|
|
335
|
-
expect(result1.columnGroups).toEqual(result2.columnGroups)
|
|
336
|
-
})
|
|
337
|
-
|
|
338
|
-
test('deterministic across all threshold boundaries', () => {
|
|
339
|
-
const items = ['a', 'b', 'c', 'd']
|
|
340
|
-
const boundaries = [99, 100, 149, 150, 199, 200, 250]
|
|
341
|
-
|
|
342
|
-
for (const width of boundaries) {
|
|
343
|
-
const result1 = computeGridLayout(items, width)
|
|
344
|
-
const result2 = computeGridLayout(items, width)
|
|
345
|
-
expect(result1.columns).toBe(result2.columns)
|
|
346
|
-
}
|
|
347
|
-
})
|
|
348
|
-
})
|
|
349
|
-
})
|