diff --git a/packages/message-parser/src/definitions.ts b/packages/message-parser/src/definitions.ts index 4beac565ecc2d..0a7e35ab07f1f 100644 --- a/packages/message-parser/src/definitions.ts +++ b/packages/message-parser/src/definitions.ts @@ -198,29 +198,13 @@ export type Types = { LIST_ITEM: ListItem; IMAGE: Image; LINE_BREAK: LineBreak; + KATEX: KaTeX; + INLINE_KATEX: InlineKaTeX; + TIMESTAMP: Timestamp; SPOILER_BLOCK: SpoilerBlock; }; -export type ASTNode = - | BigEmoji - | Bold - | Spoiler - | Paragraph - | Plain - | Italic - | Strike - | Code - | CodeLine - | InlineCode - | Heading - | Quote - | SpoilerBlock - | Link - | UserMention - | ChannelMention - | Emoji - | Color - | Tasks; +export type ASTNode = Types[keyof Types]; export type TypesKeys = keyof Types; diff --git a/packages/message-parser/src/guards.ts b/packages/message-parser/src/guards.ts index 8dcabca2f85f1..cf835ed12ca9f 100644 --- a/packages/message-parser/src/guards.ts +++ b/packages/message-parser/src/guards.ts @@ -1,4 +1,4 @@ import type { ASTNode } from './definitions'; -export const isNodeOfType = (value: unknown, type: N['type']): value is N => +export const isNodeOfType = (value: unknown, type: T): value is Extract => typeof value === 'object' && value !== null && 'type' in value && (value as { type: unknown }).type === type; diff --git a/packages/message-parser/tests/guards.test.ts b/packages/message-parser/tests/guards.test.ts new file mode 100644 index 0000000000000..5e2d29106e254 --- /dev/null +++ b/packages/message-parser/tests/guards.test.ts @@ -0,0 +1,62 @@ +import { isNodeOfType, parse } from '../src'; +import type { Image, OrderedList, Timestamp } from '../src/definitions'; +import { timestamp } from '../src/utils'; + +describe('isNodeOfType', () => { + it('narrows timestamp nodes', () => { + const value: unknown = timestamp('1708551317'); + + expect(isNodeOfType(value, 'TIMESTAMP')).toBe(true); + + if (!isNodeOfType(value, 'TIMESTAMP')) { + throw new Error('expected TIMESTAMP node'); + } + + const narrowed: Timestamp = value; + expect(value.value.timestamp).toBe('1708551317'); + expect(value.value.format).toBe('t'); + expect(narrowed.value.format).toBe('t'); + }); + + it('narrows ordered list nodes from parser output', () => { + const value: unknown = parse('1. one')[0]; + + expect(isNodeOfType(value, 'ORDERED_LIST')).toBe(true); + + if (!isNodeOfType(value, 'ORDERED_LIST')) { + throw new Error('expected ORDERED_LIST node'); + } + + const narrowed: OrderedList = value; + expect(value.value[0].number).toBe(1); + expect(narrowed.value[0].number).toBe(1); + }); + + it('narrows image nodes from parser output', () => { + const paragraph: unknown = parse('![logo](https://rocket.chat/logo.png)')[0]; + + expect(isNodeOfType(paragraph, 'PARAGRAPH')).toBe(true); + + if (!isNodeOfType(paragraph, 'PARAGRAPH')) { + throw new Error('expected PARAGRAPH node'); + } + + const imageNode: unknown = paragraph.value[0]; + + expect(isNodeOfType(imageNode, 'IMAGE')).toBe(true); + + if (!isNodeOfType(imageNode, 'IMAGE')) { + throw new Error('expected IMAGE node'); + } + + const narrowed: Image = imageNode; + expect(imageNode.value.src.value).toBe('https://rocket.chat/logo.png'); + expect(narrowed.value.src.value).toBe('https://rocket.chat/logo.png'); + }); + + it('returns false for mismatched types', () => { + const value: unknown = timestamp('1708551317'); + + expect(isNodeOfType(value, 'ORDERED_LIST')).toBe(false); + }); +});