127 lines
3.7 KiB
TypeScript
127 lines
3.7 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import {
|
|
getTasksMRU,
|
|
deriveRunningApps,
|
|
getActiveTask,
|
|
getTaskTopActivity,
|
|
getActiveTopActivityId,
|
|
getActiveAppId,
|
|
} from '../os/taskUtils';
|
|
import type { OSState, Task } from '../os/types';
|
|
|
|
function mkTask(taskId: string, appId: string, lastActiveAt: number, stackSize = 1): Task {
|
|
const stack = Array.from({ length: stackSize }, (_, i) => ({
|
|
activityId: `${taskId}_act_${i}`,
|
|
appId,
|
|
initialRoute: '/',
|
|
}));
|
|
return { taskId, rootAppId: appId, stack, lastActiveAt };
|
|
}
|
|
|
|
function mkState(tasks: Task[], activeTaskId: string | null = null): OSState {
|
|
return {
|
|
tasks,
|
|
activeTaskId,
|
|
isLauncherVisible: !activeTaskId,
|
|
isRecentsVisible: false,
|
|
brightness: 60,
|
|
volume: 30,
|
|
};
|
|
}
|
|
|
|
describe('getTasksMRU', () => {
|
|
it('returns tasks sorted by lastActiveAt descending', () => {
|
|
const tasks = [mkTask('t1', 'a', 1), mkTask('t2', 'b', 3), mkTask('t3', 'c', 2)];
|
|
const sorted = getTasksMRU(tasks);
|
|
expect(sorted.map(t => t.taskId)).toEqual(['t2', 't3', 't1']);
|
|
});
|
|
|
|
it('returns empty array for empty input', () => {
|
|
expect(getTasksMRU([])).toEqual([]);
|
|
});
|
|
|
|
it('does not mutate the original array', () => {
|
|
const tasks = [mkTask('t1', 'a', 2), mkTask('t2', 'b', 1)];
|
|
const copy = [...tasks];
|
|
getTasksMRU(tasks);
|
|
expect(tasks).toEqual(copy);
|
|
});
|
|
});
|
|
|
|
describe('deriveRunningApps', () => {
|
|
it('returns unique app IDs in MRU order', () => {
|
|
const tasks = [
|
|
mkTask('t1', 'wechat', 1),
|
|
mkTask('t2', 'settings', 3),
|
|
mkTask('t3', 'wechat', 2),
|
|
];
|
|
expect(deriveRunningApps(tasks)).toEqual(['settings', 'wechat']);
|
|
});
|
|
|
|
it('returns empty for no tasks', () => {
|
|
expect(deriveRunningApps([])).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe('getActiveTask', () => {
|
|
it('returns the matching task when activeTaskId is set', () => {
|
|
const tasks = [mkTask('t1', 'a', 1), mkTask('t2', 'b', 2)];
|
|
const state = mkState(tasks, 't2');
|
|
expect(getActiveTask(state)?.taskId).toBe('t2');
|
|
});
|
|
|
|
it('returns null when activeTaskId is null', () => {
|
|
const state = mkState([mkTask('t1', 'a', 1)], null);
|
|
expect(getActiveTask(state)).toBeNull();
|
|
});
|
|
|
|
it('returns null when activeTaskId does not match any task', () => {
|
|
const state = mkState([mkTask('t1', 'a', 1)], 'nonexistent');
|
|
expect(getActiveTask(state)).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('getTaskTopActivity', () => {
|
|
it('returns the last activity in the stack', () => {
|
|
const task = mkTask('t1', 'a', 1, 3);
|
|
const top = getTaskTopActivity(task);
|
|
expect(top?.activityId).toBe('t1_act_2');
|
|
});
|
|
|
|
it('returns null for null/undefined task', () => {
|
|
expect(getTaskTopActivity(null)).toBeNull();
|
|
expect(getTaskTopActivity(undefined)).toBeNull();
|
|
});
|
|
|
|
it('returns null for task with empty stack', () => {
|
|
const task: Task = { taskId: 't1', rootAppId: 'a', stack: [], lastActiveAt: 0 };
|
|
expect(getTaskTopActivity(task)).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('getActiveTopActivityId', () => {
|
|
it('returns activity ID of the active task top', () => {
|
|
const task = mkTask('t1', 'a', 1, 2);
|
|
const state = mkState([task], 't1');
|
|
expect(getActiveTopActivityId(state)).toBe('t1_act_1');
|
|
});
|
|
|
|
it('returns null when no active task', () => {
|
|
const state = mkState([mkTask('t1', 'a', 1)], null);
|
|
expect(getActiveTopActivityId(state)).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('getActiveAppId', () => {
|
|
it('returns the appId of the top activity of the active task', () => {
|
|
const task = mkTask('t1', 'myapp', 1);
|
|
const state = mkState([task], 't1');
|
|
expect(getActiveAppId(state)).toBe('myapp');
|
|
});
|
|
|
|
it('returns null when no active task', () => {
|
|
const state = mkState([], null);
|
|
expect(getActiveAppId(state)).toBeNull();
|
|
});
|
|
});
|