Files
TREK/client/src/native/shareLink.test.ts
T
Maurice 6835fd7bf3 feat(mobile): page transitions, pull-to-refresh, share sheet and haptics
Page transitions: on the phone, pages move like a native navigation stack,
built on the View Transitions API (mobile/routeTransition.ts). A page opened
from another slides in from the trailing edge, back slides it away, and
switching between the dock's sections cross-fades while the dock stands
still. Lazy routes do not flash their skeleton: the swap waits, behind the
frozen snapshot, until the new page rendered. The edge-swipe back gesture
already moves the page and skips the transition. RTL mirrors the motion,
reduced motion and desktop get none.

Pull-to-refresh on the mobile dashboard: the page follows the finger with
resistance, ticks once past the threshold and re-reads trips and
notifications quietly, without a loading state.

Share: trip share and invite links and the journey link get a share button
next to copy, opening the system share sheet (Capacitor Share in the apps,
Web Share in browsers that have it).

Haptics: a selection click for switches and segmented controls, a tick when
a sheet is flung closed, and a firmer bump for the chat's long press.

Theme lint: this branch's new screens use the type and z-index tokens, the
Vacay company tint is a token, and the baseline is lowered to the new
counts.
2026-11-06 15:02:55 +01:00

41 lines
1.8 KiB
TypeScript

import { Share } from '@capacitor/share';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { isNativeApp } from './platform';
import { reportNativeFailure } from './reportFailure';
import { canShareLink, shareLink } from './shareLink';
vi.mock('@capacitor/share', () => ({ Share: { share: vi.fn() } }));
vi.mock('./platform', () => ({ isNativeApp: vi.fn() }));
vi.mock('./reportFailure', () => ({ reportNativeFailure: vi.fn() }));
vi.mock('../utils/fileShare', () => ({ canShareText: vi.fn(() => false) }));
beforeEach(() => {
vi.mocked(Share.share).mockReset().mockResolvedValue({ activityType: undefined });
vi.mocked(reportNativeFailure).mockClear();
vi.mocked(isNativeApp).mockReturnValue(true);
});
describe('shareLink', () => {
it('uses the native sheet inside the app', async () => {
expect(canShareLink()).toBe(true);
expect(await shareLink('https://trek.example/s/abc', 'Trip')).toBe(true);
expect(Share.share).toHaveBeenCalledWith({ url: 'https://trek.example/s/abc', title: 'Trip', dialogTitle: 'Trip' });
});
it('treats a closed sheet as no share, without a failure report', async () => {
vi.mocked(Share.share).mockRejectedValue(new Error('Share canceled'));
expect(await shareLink('https://trek.example/s/abc')).toBe(false);
expect(reportNativeFailure).not.toHaveBeenCalled();
});
it('falls back to the Web Share API in a browser, and offers nothing without one', async () => {
vi.mocked(isNativeApp).mockReturnValue(false);
expect(canShareLink()).toBe(false);
const share = vi.fn(async () => {});
vi.stubGlobal('navigator', { share });
expect(await shareLink('https://trek.example/s/abc')).toBe(true);
expect(share).toHaveBeenCalledWith({ url: 'https://trek.example/s/abc', title: undefined });
vi.unstubAllGlobals();
});
});