203 lines
5.7 KiB
TypeScript
203 lines
5.7 KiB
TypeScript
import {describe, expect, it} from '@jest/globals'
|
|
|
|
import {
|
|
countActiveFilters,
|
|
definedFilterParams,
|
|
filtersToApiParams,
|
|
hasActiveFilters,
|
|
hasPostOnlyFilters,
|
|
parseHistoryEntry,
|
|
readSearchFilters,
|
|
serializeHistoryEntry,
|
|
withoutFilterParams,
|
|
} from '#/screens/Search/searchParams'
|
|
|
|
describe(`searchParams`, () => {
|
|
describe(`readSearchFilters`, () => {
|
|
it(`reads present string filters`, () => {
|
|
expect(
|
|
readSearchFilters({q: 'cats', author: 'alice', domain: 'bsky.app'}),
|
|
).toEqual({author: 'alice', domain: 'bsky.app'})
|
|
})
|
|
|
|
it(`ignores the literal string "undefined"`, () => {
|
|
expect(
|
|
readSearchFilters({
|
|
q: 'cats',
|
|
author: 'alice',
|
|
mentions: 'undefined',
|
|
domain: 'undefined',
|
|
}),
|
|
).toEqual({author: 'alice'})
|
|
})
|
|
|
|
it(`ignores empty and non-string values`, () => {
|
|
expect(readSearchFilters({author: '', tag: undefined})).toEqual({})
|
|
})
|
|
})
|
|
|
|
describe(`hasPostOnlyFilters`, () => {
|
|
it(`returns false for a lang-only filter (people/feeds tabs stay)`, () => {
|
|
expect(hasPostOnlyFilters({lang: 'en'})).toBe(false)
|
|
})
|
|
|
|
it(`returns false for no filters`, () => {
|
|
expect(hasPostOnlyFilters({})).toBe(false)
|
|
})
|
|
|
|
it(`returns true for a post-restricting filter`, () => {
|
|
expect(hasPostOnlyFilters({author: 'alice'})).toBe(true)
|
|
expect(hasPostOnlyFilters({media: 'true'})).toBe(true)
|
|
expect(hasPostOnlyFilters({excludeTag: 'spam'})).toBe(true)
|
|
})
|
|
|
|
it(`returns true when lang is combined with a post-only filter`, () => {
|
|
expect(hasPostOnlyFilters({lang: 'en', author: 'alice'})).toBe(true)
|
|
})
|
|
})
|
|
|
|
describe(`hasActiveFilters`, () => {
|
|
it(`includes the structured Me author filter`, () => {
|
|
expect(hasActiveFilters({})).toBe(false)
|
|
expect(hasActiveFilters({from: 'me'})).toBe(true)
|
|
expect(hasActiveFilters({author: 'alice'})).toBe(true)
|
|
})
|
|
})
|
|
|
|
describe(`definedFilterParams`, () => {
|
|
it(`omits absent keys entirely`, () => {
|
|
expect(definedFilterParams({author: 'alice'})).toEqual({author: 'alice'})
|
|
})
|
|
})
|
|
|
|
describe(`withoutFilterParams`, () => {
|
|
it(`strips filter keys but keeps q/tab/name`, () => {
|
|
expect(
|
|
withoutFilterParams({
|
|
q: 'cats',
|
|
tab: 'latest',
|
|
name: 'alice',
|
|
author: 'alice',
|
|
domain: 'undefined',
|
|
}),
|
|
).toEqual({q: 'cats', tab: 'latest', name: 'alice'})
|
|
})
|
|
})
|
|
|
|
describe(`filtersToApiParams`, () => {
|
|
it(`splits list fields into arrays and maps v2-only filters`, () => {
|
|
expect(
|
|
filtersToApiParams({
|
|
author: 'alice bob',
|
|
domain: 'bsky.app',
|
|
tag: 'atproto bluesky',
|
|
lang: 'en',
|
|
media: 'true',
|
|
replies: 'none',
|
|
}),
|
|
).toEqual({
|
|
authors: ['alice', 'bob'],
|
|
domains: ['bsky.app'],
|
|
hashtags: ['atproto', 'bluesky'],
|
|
language: 'en',
|
|
hasMedia: true,
|
|
excludeReplies: true,
|
|
})
|
|
})
|
|
|
|
it(`maps video/following and repliesOnly`, () => {
|
|
expect(
|
|
filtersToApiParams({
|
|
video: 'true',
|
|
following: 'true',
|
|
replies: 'only',
|
|
}),
|
|
).toEqual({
|
|
hasVideo: true,
|
|
following: true,
|
|
repliesOnly: true,
|
|
})
|
|
})
|
|
|
|
it(`maps exclude* keys to v2 exclude* arrays`, () => {
|
|
expect(
|
|
filtersToApiParams({
|
|
excludeAuthor: 'alice bob',
|
|
excludeMentions: 'carol',
|
|
excludeDomain: 'spam.com',
|
|
excludeUrl: 'spam.com/x',
|
|
excludeTag: 'nsfw promo',
|
|
}),
|
|
).toEqual({
|
|
excludeAuthors: ['alice', 'bob'],
|
|
excludeMentions: ['carol'],
|
|
excludeDomains: ['spam.com'],
|
|
excludeUrls: ['spam.com/x'],
|
|
excludeHashtags: ['nsfw', 'promo'],
|
|
})
|
|
})
|
|
})
|
|
|
|
describe(`countActiveFilters`, () => {
|
|
it(`counts each structured filter key once`, () => {
|
|
expect(countActiveFilters({})).toBe(0)
|
|
expect(countActiveFilters({from: 'me'})).toBe(1)
|
|
expect(
|
|
countActiveFilters({author: 'alice bob', domain: 'bsky.app'}),
|
|
).toBe(2)
|
|
})
|
|
})
|
|
|
|
describe(`search history serialize/parse`, () => {
|
|
it(`stores a filter-less search as a plain string`, () => {
|
|
expect(serializeHistoryEntry('cats', {})).toBe('cats')
|
|
})
|
|
|
|
it(`stores a filtered search as JSON`, () => {
|
|
const stored = serializeHistoryEntry('cats', {author: 'alice'})
|
|
expect(stored).not.toBe('cats')
|
|
expect(parseHistoryEntry(stored)).toEqual({
|
|
q: 'cats',
|
|
filters: {author: 'alice'},
|
|
})
|
|
})
|
|
|
|
it(`round-trips a promoted Me-only search`, () => {
|
|
const stored = serializeHistoryEntry('', {from: 'me'})
|
|
expect(parseHistoryEntry(stored)).toEqual({
|
|
q: '',
|
|
filters: {from: 'me'},
|
|
})
|
|
})
|
|
|
|
it(`round-trips query + filters`, () => {
|
|
const filters = {
|
|
author: 'alice',
|
|
tag: 'black orange',
|
|
since: '2024-01-01',
|
|
}
|
|
const stored = serializeHistoryEntry('cats', filters)
|
|
expect(parseHistoryEntry(stored)).toEqual({q: 'cats', filters})
|
|
})
|
|
|
|
it(`reads a legacy plain-string entry as a query with no filters`, () => {
|
|
expect(parseHistoryEntry('plain old search')).toEqual({
|
|
q: 'plain old search',
|
|
filters: {},
|
|
})
|
|
})
|
|
|
|
it(`treats malformed JSON as a plain query without throwing`, () => {
|
|
expect(parseHistoryEntry('{not valid json')).toEqual({
|
|
q: '{not valid json',
|
|
filters: {},
|
|
})
|
|
})
|
|
|
|
it(`treats a JSON value lacking a string q as a plain query`, () => {
|
|
const weird = JSON.stringify({foo: 'bar'})
|
|
expect(parseHistoryEntry(weird)).toEqual({q: weird, filters: {}})
|
|
})
|
|
})
|
|
})
|