add a test

This commit is contained in:
Eric Bailey
2023-10-10 18:43:16 -05:00
parent b3bae78e30
commit 1dc38a8fd9
4 changed files with 142 additions and 46 deletions
@@ -0,0 +1,80 @@
import {describe, it, expect} from '@jest/globals'
import {findSuggestionMatch, parsePunctuationFromTag} from '../utils'
describe('findSuggestionMatch', () => {
it(`finds tag`, () => {
const match = findSuggestionMatch({
text: 'a #tag',
cursorPosition: 6,
})
expect(match).toEqual({
range: {
from: 2,
to: 6,
},
query: 'tag',
text: ' #tag',
})
})
it(`validates tag length`, () => {
expect(
findSuggestionMatch({
text: '#xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
cursorPosition: 65,
}),
).toEqual({
range: {
from: 0,
to: 65,
},
query: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
text: '#xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
})
expect(
findSuggestionMatch({
text: '#xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxo',
cursorPosition: 66,
}),
).toEqual(null)
})
it(`reports tag with trailing punctuation`, () => {
const match = findSuggestionMatch({
text: '#tag!!!',
cursorPosition: 7,
})
expect(match).toEqual({
range: {
from: 0,
to: 7,
},
query: 'tag!!!',
text: '#tag!!!',
})
})
})
describe('parsePunctuationFromTag', () => {
it(`parses with punctuation`, () => {
expect(parsePunctuationFromTag('tag!')).toEqual({
tag: 'tag',
punctuation: '!',
})
expect(parsePunctuationFromTag('tag!!!')).toEqual({
tag: 'tag',
punctuation: '!!!',
})
})
it(`parses without punctuation`, () => {
expect(parsePunctuationFromTag('tag')).toEqual({
tag: 'tag',
punctuation: '',
})
})
})
@@ -5,6 +5,8 @@ import {Node as ProseMirrorNode} from '@tiptap/pm/model'
import {PluginKey} from '@tiptap/pm/state'
import Suggestion, {SuggestionOptions} from '@estrattonbailey/tiptap-suggestion'
import {findSuggestionMatch} from './utils'
export type TagOptions = {
HTMLAttributes: Record<string, any>
renderLabel: (props: {options: TagOptions; node: ProseMirrorNode}) => string
@@ -64,55 +66,13 @@ export const Tags = Node.create<TagOptions>({
},
findSuggestionMatch({$position}) {
const text = $position.nodeBefore?.isText && $position.nodeBefore.text
const cursorPosition = $position.pos
if (!text) {
return null
}
const regex = /(?:^|\s)(#[^\d\s]\S*)(?=\s)?/g
const puncRegex = /\p{P}+$/gu
const match = Array.from(text.matchAll(regex)).pop()
if (
!match ||
match.input === undefined ||
match.index === undefined
) {
return null
}
const cursorPosition = $position.pos
const startIndex = cursorPosition - text.length
let [matchedString, tag] = match
const sanitized = tag.replace(puncRegex, '').replace(/^#/, '')
// one of our hashtag spec rules
if (sanitized.length > 64) return null
const from = startIndex + match.index + matchedString.indexOf(tag)
const to = from + tag.length
if (from < cursorPosition && to >= cursorPosition) {
return {
range: {
from,
to,
},
/**
* This is passed to the `items({ query })` method configured in
* `createTagsAutocomplete`.
*
* We parse out the punctuation later, but we don't want to pass
* the # to the search query.
*/
query: tag.replace(/^#/, ''),
// raw text string
text: matchedString,
}
}
return null
return findSuggestionMatch({text, cursorPosition})
},
},
}
@@ -0,0 +1,55 @@
export function parsePunctuationFromTag(value: string) {
const reg = /(\p{P}+)$/gu
const tag = value.replace(reg, '')
const punctuation = value.match(reg)?.[0] || ''
return {tag, punctuation}
}
export function findSuggestionMatch({
text,
cursorPosition,
}: {
text: string
cursorPosition: number
}) {
const regex = /(?:^|\s)(#[^\d\s]\S*)(?=\s)?/g
const puncRegex = /\p{P}+$/gu
const match = Array.from(text.matchAll(regex)).pop()
if (!match || match.input === undefined || match.index === undefined) {
return null
}
const startIndex = cursorPosition - text.length
let [matchedString, tag] = match
const sanitized = tag.replace(puncRegex, '').replace(/^#/, '')
// one of our hashtag spec rules
if (sanitized.length > 64) return null
const from = startIndex + match.index + matchedString.indexOf(tag)
const to = from + tag.length
if (from < cursorPosition && to >= cursorPosition) {
return {
range: {
from,
to,
},
/**
* This is passed to the `items({ query })` method configured in
* `createTagsAutocomplete`.
*
* We parse out the punctuation later, but we don't want to pass
* the # to the search query.
*/
query: tag.replace(/^#/, ''),
// raw text string
text: matchedString,
}
}
return null
}
@@ -12,6 +12,8 @@ import {TagsAutocompleteModel} from 'state/models/ui/tags-autocomplete'
import {usePalette} from 'lib/hooks/usePalette'
import {Text} from 'view/com/util/text/Text'
import {parsePunctuationFromTag} from './utils'
type AutocompleteResult = string
type ListProps = SuggestionProps<AutocompleteResult> & {
autocompleteModel: TagsAutocompleteModel
@@ -99,8 +101,7 @@ const Autocomplete = forwardRef<AutocompleteRef, ListProps>(
const commit = React.useCallback(
(query: string) => {
const tag = query.replace(/(\p{P}+)$/gu, '')
const punctuation = query.match(/(\p{P}+)$/gu)?.[0] || ''
const {tag, punctuation} = parsePunctuationFromTag(query)
/*
* This values here are passed directly to the `command` method
* configured in the `Tags` plugin.