Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ const TestRenderer = require('react-test-renderer')

const useWatchMock = vi.fn()
let mockHasProAccess = false
let mockFormIsValid = false

vi.mock('react-hook-form', () => ({
useWatch: (args: { control: { values: Record<string, unknown> }; name: string }) =>
Expand Down Expand Up @@ -48,7 +47,7 @@ vi.mock('@/hooks/use-habit-form', () => ({
reset: vi.fn(),
setValue: vi.fn(),
getValues: vi.fn(() => ({})),
formState: { isDirty: false, errors: {}, get isValid() { return mockFormIsValid } },
formState: { isDirty: false, errors: {} },
},
isOneTime: false,
isGeneral: false,
Expand Down Expand Up @@ -153,9 +152,10 @@ describe('CreateHabitModal (mobile)', () => {
beforeEach(() => {
vi.clearAllMocks()
mockHasProAccess = false
mockFormIsValid = false
useWatchMock.mockImplementation(({ name }: { name: string }) => {
switch (name) {
case 'title':
return ''
case 'dueTime':
return ''
case 'reminderEnabled':
Expand Down Expand Up @@ -216,14 +216,26 @@ describe('CreateHabitModal (mobile)', () => {
).length > 0,
)[0]

it('disables the submit button when the shared form schema is invalid', () => {
mockFormIsValid = false
it('disables the submit button until a title is entered', () => {
const tree = renderModal(<CreateHabitModal open onClose={vi.fn()} />)
expect(findSubmit(tree.root).props.disabled).toBe(true)
})

it('enables the submit button when the shared form schema is valid', () => {
mockFormIsValid = true
it('enables the submit button once the title has content', () => {
useWatchMock.mockImplementation(({ name }: { name: string }) => {
switch (name) {
case 'title':
return 'Drink water'
case 'dueTime':
return ''
case 'reminderEnabled':
return false
case 'scheduledReminders':
return []
default:
return undefined
}
})
const tree = renderModal(<CreateHabitModal open onClose={vi.fn()} />)
expect(findSubmit(tree.root).props.disabled).toBe(false)
})
Expand Down
58 changes: 58 additions & 0 deletions apps/mobile/__tests__/components/habits/habit-form-fields.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,64 @@ describe('HabitFormFields (mobile)', () => {
expect(hasText('habits.form.tags')).toBe(true)
})

it('advances the frequency carousel to the next card when the next arrow is pressed', async () => {
const setFlexible = vi.fn()
const formHelpers = createMockFormHelpers(undefined, { setFlexible })
const tags = createMockTags()
let tree: any

await TestRenderer.act(async () => {
tree = TestRenderer.create(
<HabitFormFields
formHelpers={formHelpers}
tags={tags}
selectedGoalIds={[]}
atGoalLimit={false}
onToggleGoal={vi.fn()}
reminderTimes={[]}
onReminderTimesChange={vi.fn()}
/>,
)
})

const nextArrow = tree.root.findByProps({ accessibilityLabel: 'common.next' })

await TestRenderer.act(async () => {
nextArrow.props.onPress()
})

expect(setFlexible).toHaveBeenCalled()
})

it('moves the frequency carousel to the previous card when the previous arrow is pressed', async () => {
const setOneTime = vi.fn()
const formHelpers = createMockFormHelpers(undefined, { setOneTime })
const tags = createMockTags()
let tree: any

await TestRenderer.act(async () => {
tree = TestRenderer.create(
<HabitFormFields
formHelpers={formHelpers}
tags={tags}
selectedGoalIds={[]}
atGoalLimit={false}
onToggleGoal={vi.fn()}
reminderTimes={[]}
onReminderTimesChange={vi.fn()}
/>,
)
})

const previousArrow = tree.root.findByProps({ accessibilityLabel: 'common.previous' })

await TestRenderer.act(async () => {
previousArrow.props.onPress()
})

expect(setOneTime).toHaveBeenCalled()
})

it('commits the title to the form as it is typed so the shared schema gates submit', async () => {
const formHelpers = createMockFormHelpers({ title: '' })
const tags = createMockTags()
Expand Down
3 changes: 2 additions & 1 deletion apps/mobile/__tests__/screens/account-deletion.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,8 @@ describe('ProfileScreen account-deletion state machine', () => {
await Promise.resolve()
})

expect(screenTexts(tree)).toContain('boom')
expect(screenTexts(tree)).toContain('profile.deleteAccount.errorGeneric')
expect(screenTexts(tree)).not.toContain('boom')
expect(tree.root.findAllByType('TextInput')).toHaveLength(0)
})

Expand Down
40 changes: 35 additions & 5 deletions apps/mobile/__tests__/screens/intentional-offline-ux.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,13 @@ const mocks = vi.hoisted(() => {
useTrialExpired: vi.fn(() => false),
useTrialUrgent: vi.fn(() => false),
useRetrospective: vi.fn(() => ({
retrospective: null,
setRetrospective: vi.fn(),
data: null,
setData: vi.fn(),
isLoading: false,
error: null,
setError: vi.fn(),
noData: false,
setNoData: vi.fn(),
fromCache: false,
period: 'week',
setPeriod: vi.fn(),
Expand Down Expand Up @@ -314,6 +316,7 @@ vi.mock('lucide-react-native', () => {
Download: createIcon('Download'),
Flame: createIcon('Flame'),
Info: createIcon('Info'),
Lightbulb: createIcon('Lightbulb'),
Lock: createIcon('Lock'),
LogOut: createIcon('LogOut'),
MessageCircle: createIcon('MessageCircle'),
Expand All @@ -325,10 +328,12 @@ vi.mock('lucide-react-native', () => {
ShieldCheck: createIcon('ShieldCheck'),
Orbit: createIcon('Orbit'),
Sparkles: createIcon('Sparkles'),
Star: createIcon('Star'),
Play: createIcon('Play'),
Send: createIcon('Send'),
Tag: createIcon('Tag'),
Target: createIcon('Target'),
TrendingUp: createIcon('TrendingUp'),
TriangleAlert: createIcon('TriangleAlert'),
User: createIcon('User'),
UserX: createIcon('UserX'),
Expand Down Expand Up @@ -401,11 +406,13 @@ describe('intentional offline UX screens', () => {
mocks.useTrialExpired.mockReturnValue(false)
mocks.useTrialUrgent.mockReturnValue(false)
mocks.useRetrospective.mockReturnValue({
retrospective: null,
setRetrospective: vi.fn(),
data: null,
setData: vi.fn(),
isLoading: false,
error: null,
setError: vi.fn(),
noData: false,
setNoData: vi.fn(),
fromCache: false,
period: 'week',
setPeriod: vi.fn(),
Expand All @@ -431,7 +438,30 @@ describe('intentional offline UX screens', () => {
})

it('renders cached retrospective content offline when a cached read exists', async () => {
mocks.storage.set('orbit_retrospective_cache_week', 'Cached retrospective summary')
const cached = {
period: 'week',
fromCache: true,
metrics: {
completionRate: 88,
totalCompletions: 17,
totalScheduled: 20,
activeDays: 6,
periodDays: 7,
currentStreak: 3,
bestStreak: 8,
badHabitSlips: 1,
weeklyConsistency: [80, 100, 60, 100, 40, 0, 100],
topHabits: [],
needsAttention: [],
},
narrative: {
highlights: 'Cached retrospective summary',
missed: 'A couple of slips.',
trends: 'Mornings are strong.',
suggestion: 'Keep the streak alive.',
},
}
mocks.storage.set('orbit_retrospective_cache_week_v2', JSON.stringify(cached))

const tree = await renderScreen(<RetrospectiveScreen />)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,13 @@ vi.mock("@/hooks/use-gamification", () => ({

vi.mock("@/hooks/use-retrospective", () => ({
useRetrospective: () => ({
retrospective: null,
setRetrospective: vi.fn(),
data: null,
setData: vi.fn(),
isLoading: false,
error: "",
setError: vi.fn(),
noData: false,
setNoData: vi.fn(),
fromCache: false,
period: "month",
setPeriod: vi.fn(),
Expand Down
Loading
Loading