Files
bsky-social-app/src/screens/Search/__tests__/searchParams.test.ts
T
2026-07-22 15:01:24 -07:00

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: {}})
})
})
})