mattermost-mobile/app/products/agents/utils.test.ts
Christopher Speller 0c675ec2db
Agents posts on mobile (#9317)
* Agents streaming

* Fix bug and lint

* Add reasoning summaries to mobile agents

* Add tool call approval UI for mobile agents

* Add citations and annotations support for mobile agents

Implements Phase 4 from the agents mobile plan:
- WebSocket event handling for annotations control signal
- CitationsList component displaying sources below agent responses
- Collapsible sources section with source count
- Each citation shows title, domain, and opens URL on tap
- Citations persisted in post.props.annotations
- Touch-optimized UI with 44pt minimum tap targets

* Add stop and regenerate controls for mobile agents

* Add tests

* Fix tool approval buttons not updating after accept/reject

- Remove optimistic WebSocket event approach that didn't work
- Clear component streaming state on ENDED event to force switch to persisted data
- Remove delays in streaming store cleanup (500ms and 100ms)
- POST_EDITED event now properly updates UI for both accept and reject
- Remove debug console.log statements
- Fix ESLint issues (unused vars, nested callbacks, nested ternary)

* Refactor agents code: remove unused client mixin, fix bugs, and simplify logic

* Fixes

* Add CLAUDE.md

* Review feedback.

* Review feedback inline functions

* Learnings

* Address review feedback: StyleSheet.create, parent mount checks, utils

* Move to observables

* Last style fix

* Style tweaks
2025-12-09 03:56:12 -08:00

134 lines
4.7 KiB
TypeScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {AGENT_POST_TYPES} from '@agents/constants';
import TestHelper from '@test/test_helper';
import {isAgentPost, isPostRequester} from './utils';
describe('isAgentPost', () => {
describe('with Post objects', () => {
it('returns true when post type is custom_llmbot', () => {
const post = TestHelper.fakePost({type: AGENT_POST_TYPES.LLMBOT});
expect(isAgentPost(post)).toBe(true);
});
it('returns true when post type is custom_llm_postback', () => {
const post = TestHelper.fakePost({type: AGENT_POST_TYPES.LLM_POSTBACK});
expect(isAgentPost(post)).toBe(true);
});
it('returns false for non-agent post types', () => {
const post = TestHelper.fakePost({type: ''});
expect(isAgentPost(post)).toBe(false);
const systemPost = TestHelper.fakePost({type: 'system_join_channel'});
expect(isAgentPost(systemPost)).toBe(false);
});
});
describe('with PostModel objects', () => {
it('returns true when post type is custom_llmbot', () => {
const postModel = TestHelper.fakePostModel({type: AGENT_POST_TYPES.LLMBOT});
expect(isAgentPost(postModel)).toBe(true);
});
it('returns true when post type is custom_llm_postback', () => {
const postModel = TestHelper.fakePostModel({type: AGENT_POST_TYPES.LLM_POSTBACK});
expect(isAgentPost(postModel)).toBe(true);
});
it('returns false for non-agent post types', () => {
const postModel = TestHelper.fakePostModel({type: ''});
expect(isAgentPost(postModel)).toBe(false);
});
});
});
describe('isPostRequester', () => {
const currentUserId = 'user123';
describe('with Post objects', () => {
it('returns true when llm_requester_user_id matches current user ID', () => {
const post = TestHelper.fakePost({
props: {
llm_requester_user_id: currentUserId,
},
});
expect(isPostRequester(post, currentUserId)).toBe(true);
});
it('returns false when llm_requester_user_id does not match', () => {
const post = TestHelper.fakePost({
props: {
llm_requester_user_id: 'different_user',
},
});
expect(isPostRequester(post, currentUserId)).toBe(false);
});
it('returns false when props is undefined', () => {
const post = TestHelper.fakePost({
props: undefined,
});
expect(isPostRequester(post, currentUserId)).toBe(false);
});
it('returns false when props is empty object', () => {
const post = TestHelper.fakePost({
props: {},
});
expect(isPostRequester(post, currentUserId)).toBe(false);
});
it('returns false when llm_requester_user_id is missing', () => {
const post = TestHelper.fakePost({
props: {
some_other_prop: 'value',
},
});
expect(isPostRequester(post, currentUserId)).toBe(false);
});
});
describe('with PostModel objects', () => {
it('returns true when llm_requester_user_id matches current user ID', () => {
const postModel = TestHelper.fakePostModel({
props: {
llm_requester_user_id: currentUserId,
},
});
expect(isPostRequester(postModel, currentUserId)).toBe(true);
});
it('returns false when llm_requester_user_id does not match', () => {
const postModel = TestHelper.fakePostModel({
props: {
llm_requester_user_id: 'different_user',
},
});
expect(isPostRequester(postModel, currentUserId)).toBe(false);
});
it('returns false when props is empty object', () => {
const postModel = TestHelper.fakePostModel({
props: {},
});
expect(isPostRequester(postModel, currentUserId)).toBe(false);
});
});
describe('error handling', () => {
it('handles exceptions gracefully and returns false', () => {
// Create a post object that throws when accessing props
const faultyPost = {
get props() {
throw new Error('Access denied');
},
} as any;
expect(isPostRequester(faultyPost, currentUserId)).toBe(false);
});
});
});