mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-11-14 00:01:03 +00:00
b7951baf23
- RTL: the measured pills and centred controls use logical forms or a marked line - i18n: the two native widget numbers are positions, not counts (NOT_PLURAL) - Types: styleOverride on the lazy GL map, the location and shared-file payloads, the capabilities fallback, the transport sheet's stay metadata - ESLint: latest-value refs move to layout effects, usePresence keeps the last open state in state, the booking import opens clean with a shared document kept in the list (the reset effect used to clear it again) - Layers: the phone import sheet lives under mobile/ and reads through tripRepo; the offline host check moves to utils/tileUrl - Sizes: plugin layer geometry, hardNavigate and the exit animations get modules of their own - Tests follow the status radios, the stay chip overview, the dock + button for the first list and the field token; store registry lists the new stores
64 lines
2.5 KiB
TypeScript
64 lines
2.5 KiB
TypeScript
import { App } from '@capacitor/app';
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
import { act, fireEvent, render, screen, waitFor } from '../../../tests/helpers/render';
|
|
import { isAppLockEnabled, unlockApp } from '../../native/appLock';
|
|
import AppLockGate from './AppLockGate';
|
|
|
|
vi.mock('@capacitor/app', () => ({ App: { addListener: vi.fn(() => Promise.resolve({ remove: vi.fn() })) } }));
|
|
vi.mock('../../native/appLock', () => ({
|
|
RELOCK_AFTER_MS: 30_000,
|
|
isAppLockEnabled: vi.fn(),
|
|
unlockApp: vi.fn(),
|
|
}));
|
|
|
|
function stateListener(): (state: { isActive: boolean }) => void {
|
|
const call = vi.mocked(App.addListener).mock.calls.find(([name]) => (name as string) === 'appStateChange');
|
|
if (!call) throw new Error('no appStateChange listener');
|
|
return call[1] as unknown as (state: { isActive: boolean }) => void;
|
|
}
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
vi.mocked(isAppLockEnabled).mockReturnValue(true);
|
|
vi.mocked(unlockApp).mockResolvedValue(false);
|
|
});
|
|
|
|
describe('AppLockGate', () => {
|
|
it('covers the app at launch and asks at once; a successful unlock lifts it', async () => {
|
|
render(<AppLockGate />);
|
|
expect(screen.getByRole('dialog')).toBeInTheDocument();
|
|
await waitFor(() => expect(unlockApp).toHaveBeenCalledTimes(1));
|
|
|
|
vi.mocked(unlockApp).mockResolvedValue(true);
|
|
fireEvent.click(screen.getByRole('button'));
|
|
await waitFor(() => expect(screen.queryByRole('dialog')).not.toBeInTheDocument());
|
|
});
|
|
|
|
it('locks again after half a minute in the background, not after a glance away', async () => {
|
|
vi.mocked(unlockApp).mockResolvedValue(true);
|
|
const now = vi.spyOn(Date, 'now');
|
|
render(<AppLockGate />);
|
|
await waitFor(() => expect(screen.queryByRole('dialog')).not.toBeInTheDocument());
|
|
|
|
vi.mocked(unlockApp).mockResolvedValue(false);
|
|
now.mockReturnValue(1_000);
|
|
act(() => stateListener()({ isActive: false }));
|
|
now.mockReturnValue(10_000);
|
|
act(() => stateListener()({ isActive: true }));
|
|
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
|
|
|
|
act(() => stateListener()({ isActive: false }));
|
|
now.mockReturnValue(60_000);
|
|
act(() => stateListener()({ isActive: true }));
|
|
expect(screen.getByRole('dialog')).toBeInTheDocument();
|
|
now.mockRestore();
|
|
});
|
|
|
|
it('is nothing while the lock is off', () => {
|
|
vi.mocked(isAppLockEnabled).mockReturnValue(false);
|
|
const { container } = render(<AppLockGate />);
|
|
expect(container).toBeEmptyDOMElement();
|
|
expect(unlockApp).not.toHaveBeenCalled();
|
|
});
|
|
});
|