[READ-ONLY] a fast, modern browser for the npm registry
at main 187 lines 6.0 kB view raw
1import { describe, expect, it } from 'vitest' 2import { hasSearchOperators, parseSearchOperators } from '~/composables/useStructuredFilters' 3 4describe('parseSearchOperators', () => { 5 describe('basic operator parsing', () => { 6 it('parses name: operator', () => { 7 const result = parseSearchOperators('name:react') 8 expect(result).toEqual({ name: ['react'] }) 9 }) 10 11 it('parses desc: operator', () => { 12 const result = parseSearchOperators('desc:framework') 13 expect(result).toEqual({ description: ['framework'] }) 14 }) 15 16 it('parses description: operator (long form)', () => { 17 const result = parseSearchOperators('description:framework') 18 expect(result).toEqual({ description: ['framework'] }) 19 }) 20 21 it('parses kw: operator', () => { 22 const result = parseSearchOperators('kw:typescript') 23 expect(result).toEqual({ keywords: ['typescript'] }) 24 }) 25 26 it('parses keyword: operator (long form)', () => { 27 const result = parseSearchOperators('keyword:typescript') 28 expect(result).toEqual({ keywords: ['typescript'] }) 29 }) 30 }) 31 32 describe('comma-separated values', () => { 33 it('parses multiple keywords with comma', () => { 34 const result = parseSearchOperators('kw:typescript,react,hooks') 35 expect(result).toEqual({ keywords: ['typescript', 'react', 'hooks'] }) 36 }) 37 38 it('parses multiple names with comma', () => { 39 const result = parseSearchOperators('name:react,vue,angular') 40 expect(result).toEqual({ name: ['react', 'vue', 'angular'] }) 41 }) 42 43 it('handles empty values between commas', () => { 44 const result = parseSearchOperators('kw:foo,,bar') 45 expect(result).toEqual({ keywords: ['foo', 'bar'] }) 46 }) 47 }) 48 49 describe('multiple operators', () => { 50 it('parses name and kw operators together', () => { 51 const result = parseSearchOperators('name:react kw:typescript') 52 expect(result).toEqual({ 53 name: ['react'], 54 keywords: ['typescript'], 55 }) 56 }) 57 58 it('parses all three operator types', () => { 59 const result = parseSearchOperators('name:react desc:framework kw:typescript') 60 expect(result).toEqual({ 61 name: ['react'], 62 description: ['framework'], 63 keywords: ['typescript'], 64 }) 65 }) 66 67 it('merges multiple instances of same operator', () => { 68 const result = parseSearchOperators('kw:react kw:typescript') 69 expect(result).toEqual({ 70 keywords: ['react', 'typescript'], 71 }) 72 }) 73 }) 74 75 describe('remaining text', () => { 76 it('captures text without operators', () => { 77 const result = parseSearchOperators('some search text') 78 expect(result).toEqual({ text: 'some search text' }) 79 }) 80 81 it('captures remaining text after operators', () => { 82 const result = parseSearchOperators('name:react some text') 83 expect(result).toEqual({ 84 name: ['react'], 85 text: 'some text', 86 }) 87 }) 88 89 it('captures remaining text before operators', () => { 90 const result = parseSearchOperators('some text name:react') 91 expect(result).toEqual({ 92 name: ['react'], 93 text: 'some text', 94 }) 95 }) 96 97 it('captures text mixed with operators', () => { 98 const result = parseSearchOperators('hello name:react world kw:hooks foo') 99 expect(result).toEqual({ 100 name: ['react'], 101 keywords: ['hooks'], 102 text: 'hello world foo', 103 }) 104 }) 105 106 it('collapses multiple spaces in remaining text', () => { 107 const result = parseSearchOperators('name:react lots of spaces') 108 expect(result).toEqual({ 109 name: ['react'], 110 text: 'lots of spaces', 111 }) 112 }) 113 }) 114 115 describe('case insensitivity', () => { 116 it('handles uppercase operator names', () => { 117 const result = parseSearchOperators('NAME:react') 118 expect(result).toEqual({ name: ['react'] }) 119 }) 120 121 it('handles mixed case operator names', () => { 122 const result = parseSearchOperators('NaMe:react KW:typescript') 123 expect(result).toEqual({ 124 name: ['react'], 125 keywords: ['typescript'], 126 }) 127 }) 128 }) 129 130 describe('edge cases', () => { 131 it('returns empty object for empty string', () => { 132 const result = parseSearchOperators('') 133 expect(result).toEqual({}) 134 }) 135 136 it('returns empty object for whitespace only', () => { 137 const result = parseSearchOperators(' ') 138 expect(result).toEqual({}) 139 }) 140 141 it('handles operator with no value', () => { 142 // "name:" followed by space - the regex won't match empty values 143 const result = parseSearchOperators('name: react') 144 expect(result).toEqual({ text: 'name: react' }) 145 }) 146 147 it('handles special characters in values', () => { 148 const result = parseSearchOperators('name:@scope/package') 149 expect(result).toEqual({ name: ['@scope/package'] }) 150 }) 151 152 it('handles hyphenated values', () => { 153 const result = parseSearchOperators('kw:gatsby-plugin') 154 expect(result).toEqual({ keywords: ['gatsby-plugin'] }) 155 }) 156 }) 157}) 158 159describe('hasSearchOperators', () => { 160 it('returns true when name is present', () => { 161 expect(hasSearchOperators({ name: ['react'] })).toBe(true) 162 }) 163 164 it('returns true when description is present', () => { 165 expect(hasSearchOperators({ description: ['framework'] })).toBe(true) 166 }) 167 168 it('returns true when keywords is present', () => { 169 expect(hasSearchOperators({ keywords: ['typescript'] })).toBe(true) 170 }) 171 172 it('returns false when only text is present', () => { 173 expect(hasSearchOperators({ text: 'search query' })).toBe(false) 174 }) 175 176 it('returns false for empty object', () => { 177 expect(hasSearchOperators({})).toBe(false) 178 }) 179 180 it('returns true when operators and text are present', () => { 181 expect(hasSearchOperators({ name: ['react'], text: 'query' })).toBe(true) 182 }) 183 184 it('returns false for empty arrays', () => { 185 expect(hasSearchOperators({ name: [], keywords: [] })).toBe(false) 186 }) 187})