Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions assets/js/SearchTagInputHelper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export function normalizeSearchTagInput(rawValue: string) {
if (!rawValue) {
return ''
}

let value = rawValue.trim()

// Collapse one or more OR tokens (case-insensitive) into a comma — the OR-group delimiter
value = value.replace(/\s*(?:\bor\b(?:\s+)?)+\s*/gi, ',')

// Replace remaining spaces (within individual tag names) with underscores
value = value.replace(/\s/g, '_')

return value
}
Comment on lines +1 to +15
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Find the file and examine it
find . -name "SearchTagInputHelper.ts" -o -name "*SearchTagInput*"

Repository: Rule-34/App

Length of output: 90


🏁 Script executed:

# Search for usages of normalizeSearchTagInput
rg "normalizeSearchTagInput" --type ts --type tsx

Repository: Rule-34/App

Length of output: 82


🏁 Script executed:

# Look for test files related to this function
fd -e spec.ts -e test.ts -e __tests__ | head -20

Repository: Rule-34/App

Length of output: 319


🏁 Script executed:

# Check git history to understand context
git log --oneline -10 -- assets/js/SearchTagInputHelper.ts 2>/dev/null || echo "Git history not available"

Repository: Rule-34/App

Length of output: 123


🏁 Script executed:

# Read the test file to see all test cases
cat -n test/assets/search-tag-input-helper.test.ts

Repository: Rule-34/App

Length of output: 1588


🏁 Script executed:

# Find where normalizeSearchTagInput is actually used in the application
rg "normalizeSearchTagInput" --type ts -A 2 -B 2

Repository: Rule-34/App

Length of output: 2896


🏁 Script executed:

# Check the full file to understand the implementation
cat -n assets/js/SearchTagInputHelper.ts

Repository: Rule-34/App

Length of output: 573


🏁 Script executed:

# Search for where normalizeSearchTagInput is actually called in the application
rg "normalizeSearchTagInput" -B 3 -A 3 --type ts | grep -v "test/"

Repository: Rule-34/App

Length of output: 290


🏁 Script executed:

# Look for any validation or processing of the output value
rg "normalizeSearchTagInput\(" -B 5 -A 5 --type ts

Repository: Rule-34/App

Length of output: 3033


🏁 Script executed:

# Search more broadly for imports of this helper
rg "SearchTagInputHelper" --type ts

Repository: Rule-34/App

Length of output: 177


🏁 Script executed:

# Search for where normalizeSearchTagInput might be used without explicit import
rg "normalizeSearchTagInput" --type js --type ts --type tsx --type jsx 2>/dev/null | head -30

Repository: Rule-34/App

Length of output: 37


🏁 Script executed:

# Look for any page or component that might handle search tag input
find . -name "*search*" -type f | grep -E "\.(ts|tsx|js|jsx)$" | head -20

Repository: Rule-34/App

Length of output: 100


🏁 Script executed:

# Check pages directory for search functionality
ls -la pages/ 2>/dev/null | head -20

Repository: Rule-34/App

Length of output: 811


🏁 Script executed:

# Search in Vue files for normalizeSearchTagInput
rg "normalizeSearchTagInput" --type-add 'vue:*.vue' --type vue 2>/dev/null || rg "normalizeSearchTagInput" pages/

Repository: Rule-34/App

Length of output: 508


🏁 Script executed:

# Check if the function might be called differently or if it's a new addition
git log --all --oneline -- assets/js/SearchTagInputHelper.ts | head -5

Repository: Rule-34/App

Length of output: 123


🏁 Script executed:

# Look at the index.vue file to see if it uses search tag input
grep -n "normalizeSearchTagInput\|SearchTagInput" pages/index.vue

Repository: Rule-34/App

Length of output: 37


🏁 Script executed:

# Check if there are any JavaScript files using it
find . -name "*.js" -type f | xargs grep -l "normalizeSearchTagInput" 2>/dev/null

Repository: Rule-34/App

Length of output: 39


🏁 Script executed:

# Get the recent commits to see if this is a new function or if usage was removed
git log --all --format="%H %s" | head -20

Repository: Rule-34/App

Length of output: 2044


🏁 Script executed:

# Examine the SimpleSearch.vue component to see how searchQuery is used
cat -n components/pages/home/SimpleSearch.vue | head -50

Repository: Rule-34/App

Length of output: 1637


🏁 Script executed:

# Look for where searchQuery is consumed after normalization
grep -n "searchQuery" components/pages/home/SimpleSearch.vue | head -20

Repository: Rule-34/App

Length of output: 273


🏁 Script executed:

# Check the SearchMenu.vue component
cat -n components/pages/posts/navigation/search/SearchMenu.vue | head -50

Repository: Rule-34/App

Length of output: 1941


🏁 Script executed:

# Look for the actual API call or query building to understand how the normalized value is used
rg "searchQuery" components/pages/home/SimpleSearch.vue -A 5 -B 5

Repository: Rule-34/App

Length of output: 721


🏁 Script executed:

# Let's trace through the actual behavior with a specific edge case
# Check if there are any integration tests that might test "OR cat" scenario
rg "OR cat\|cat OR" test/ -t ts

Repository: Rule-34/App

Length of output: 37


🏁 Script executed:

# Let's check the complete onSearchChange and customTagFromQuery logic
cat -n components/pages/home/SimpleSearch.vue | sed -n '33,65p'

Repository: Rule-34/App

Length of output: 863


🏁 Script executed:

# Look for any query building logic that might process the tag further
rg "emit.*searchTag\|emit.*submit" components/pages/home/SimpleSearch.vue -A 5 -B 5

Repository: Rule-34/App

Length of output: 37


🏁 Script executed:

# Check if there's any validation or normalization of the tags when passed to parent
rg "searchTag" components/ -A 3 -B 3 | head -40

Repository: Rule-34/App

Length of output: 1815


🏁 Script executed:

# Check if there's any further processing of the emitted tag value
# Look for parent components that listen to 'searchTag' event
rg "@(submit|searchTag)=" components/ -A 2 -B 2

Repository: Rule-34/App

Length of output: 37


🏁 Script executed:

# Let's also verify the actual regex behavior with a test
python3 << 'EOF'
import re

def normalizeSearchTagInput(rawValue: str) -> str:
    if not rawValue:
        return ''
    
    value = rawValue.strip()
    # Collapse one or more OR tokens (case-insensitive) into a comma
    value = re.sub(r'\s*(?:\bor\b(?:\s+)?)+\s*', ',', value, flags=re.IGNORECASE)
    # Replace remaining spaces with underscores
    value = re.sub(r'\s', '_', value)
    
    return value

# Test edge cases
test_cases = [
    "OR cat",
    "cat OR",
    "OR",
    "  OR cat  ",
    "cat OR dog",
    "OR OR cat",
]

for test in test_cases:
    result = normalizeSearchTagInput(test)
    print(f"'{test}' -> '{result}'")
EOF

Repository: Rule-34/App

Length of output: 175


🏁 Script executed:

# Check if there's any parent component that listens to the searchTag event and processes it
cat -n components/pages/home/SimpleSearch.vue | tail -20

Repository: Rule-34/App

Length of output: 785


🏁 Script executed:

# Look at the full SearchMenu component to see how it handles the searchTag event
grep -n "normalizeSearchTagInput\|searchQuery\|onSearchChange" components/pages/posts/navigation/search/SearchMenu.vue | head -20

Repository: Rule-34/App

Length of output: 397


🏁 Script executed:

# Check how the search results are actually built/used in the application
rg "searchTag" pages/ -A 5 | head -30

Repository: Rule-34/App

Length of output: 920


🏁 Script executed:

# Let's look at the complete onSearchChange function in SearchMenu.vue to see if there's any additional handling
sed -n '112,130p' components/pages/posts/navigation/search/SearchMenu.vue

Repository: Rule-34/App

Length of output: 480


🏁 Script executed:

# Check the complete flow - see what the parent component does with the emitted value
grep -n "onSearchTag\|onSearchSubmit" pages/index.vue -A 8 | head -30

Repository: Rule-34/App

Length of output: 1039


🏁 Script executed:

# Let's verify what happens when the tag is used in navigation
grep -n "tag" pages/posts/\[domain\]/ -A 3 -B 3 2>/dev/null | head -40

Repository: Rule-34/App

Length of output: 37


🏁 Script executed:

# Search for actual post search/filter logic
find . -name "*posts*" -type f -name "*.vue" | head -10

Repository: Rule-34/App

Length of output: 37


Add test coverage and fix leading/trailing comma edge case.

The edge cases with leading/trailing OR tokens are real. Inputs like "OR cat", "cat OR", or "OR" produce ",cat", "cat,", and "," respectively. While the existing tests cover trimming, repeated OR collapsing, and space-to-underscore conversion, they don't cover these leading/trailing scenarios.

The normalized value is passed directly to downstream API calls without additional sanitization, so these malformed delimiters should be filtered out:

Recommended fix
   // Replace remaining spaces (within individual tag names) with underscores
   value = value.replace(/\s/g, '_')

+  // Remove any leading or trailing commas from edge OR tokens
+  value = value.replace(/^,+|,+$/g, '')
+
   return value
 }

Add test cases covering the edge scenarios:

  • normalizeSearchTagInput('OR cat') should return 'cat'
  • normalizeSearchTagInput('cat OR') should return 'cat'
  • normalizeSearchTagInput('OR') should return ''
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function normalizeSearchTagInput(rawValue: string) {
if (!rawValue) {
return ''
}
let value = rawValue.trim()
// Collapse one or more OR tokens (case-insensitive) into a comma — the OR-group delimiter
value = value.replace(/\s*(?:\bor\b(?:\s+)?)+\s*/gi, ',')
// Replace remaining spaces (within individual tag names) with underscores
value = value.replace(/\s/g, '_')
return value
}
export function normalizeSearchTagInput(rawValue: string) {
if (!rawValue) {
return ''
}
let value = rawValue.trim()
// Collapse one or more OR tokens (case-insensitive) into a comma — the OR-group delimiter
value = value.replace(/\s*(?:\bor\b(?:\s+)?)+\s*/gi, ',')
// Replace remaining spaces (within individual tag names) with underscores
value = value.replace(/\s/g, '_')
// Remove any leading or trailing commas from edge OR tokens
value = value.replace(/^,+|,+$/g, '')
return value
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@assets/js/SearchTagInputHelper.ts` around lines 1 - 15, The
normalizeSearchTagInput function produces leading/trailing or empty commas for
inputs like "OR cat", "cat OR", or "OR"; update normalizeSearchTagInput to
sanitize the comma-delimited result by splitting on ',', trimming each token,
filtering out empty tokens, and rejoining with ',' (or simply remove
leading/trailing commas and collapse consecutive commas) so inputs return '' for
'OR' and 'cat' for 'OR cat'/'cat OR'; add unit tests asserting
normalizeSearchTagInput('OR cat') === 'cat', normalizeSearchTagInput('cat OR')
=== 'cat', and normalizeSearchTagInput('OR') === '' to cover these edge cases.

10 changes: 3 additions & 7 deletions components/pages/home/SimpleSearch.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { CheckIcon, MagnifyingGlassIcon } from '@heroicons/vue/20/solid'
import { watchDebounced } from '@vueuse/core'
import { abbreviateNumber } from 'js-abbreviation-number'
import { normalizeSearchTagInput } from '~/assets/js/SearchTagInputHelper'
import Tag from '~/assets/js/tag.dto'

const props = defineProps<{
Expand All @@ -22,14 +23,9 @@

// Change event
function onComboboxInputChange(event: InputEvent) {
let value = (event.target as HTMLInputElement).value
const value = (event.target as HTMLInputElement).value

value = value.trim()

// Replace empty spaces with underscores
value = value.replace(/\s/g, '_')

searchQuery.value = value
searchQuery.value = normalizeSearchTagInput(value)
}

watchDebounced(searchQuery, (value) => onSearchChange(value), { debounce: 350 })
Expand Down
10 changes: 3 additions & 7 deletions components/pages/posts/navigation/search/SearchMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import { watchDebounced } from '@vueuse/core'
import { abbreviateNumber } from 'js-abbreviation-number'
import { cloneDeep, unionWith } from 'es-toolkit'
import { normalizeSearchTagInput } from '~/assets/js/SearchTagInputHelper'
import Tag from '~/assets/js/tag.dto'
import SearchSelect from './SearchSelect.vue'

Expand Down Expand Up @@ -60,14 +61,9 @@

// Change event
function onComboboxInputChange(event: InputEvent) {
let value = (event.target as HTMLInputElement).value
const value = (event.target as HTMLInputElement).value

value = value.trim()

// Replace empty spaces with underscores
value = value.replace(/\s/g, '_')

searchQuery.value = value
searchQuery.value = normalizeSearchTagInput(value)
}

watchDebounced(searchQuery, (value) => onSearchChange(value), { debounce: 350 })
Expand Down
30 changes: 30 additions & 0 deletions test/assets/search-tag-input-helper.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { describe, expect, it } from 'vitest'
import { normalizeSearchTagInput } from '../../assets/js/SearchTagInputHelper'

describe('normalizeSearchTagInput', () => {
it('maps typed OR separators to commas case-insensitively', () => {
expect(normalizeSearchTagInput('cat OR dog')).toBe('cat,dog')
expect(normalizeSearchTagInput('cat or dog')).toBe('cat,dog')
expect(normalizeSearchTagInput('cat Or dog')).toBe('cat,dog')
expect(normalizeSearchTagInput('cat oR dog')).toBe('cat,dog')
})

it('maps multiple OR separators to commas', () => {
expect(normalizeSearchTagInput('cat OR dog or fish')).toBe('cat,dog,fish')
})

it('preserves existing behavior of converting spaces in tags to underscores', () => {
expect(normalizeSearchTagInput('black hair')).toBe('black_hair')
expect(normalizeSearchTagInput('black hair OR blue eyes')).toBe('black_hair,blue_eyes')
})

it('trims surrounding whitespace', () => {
expect(normalizeSearchTagInput(' cat OR dog ')).toBe('cat,dog')
})

it('collapses adjacent/repeated OR separators into a single comma', () => {
expect(normalizeSearchTagInput('cat OR OR dog')).toBe('cat,dog')
expect(normalizeSearchTagInput('cat or OR or dog')).toBe('cat,dog')
expect(normalizeSearchTagInput('cat OR OR OR dog')).toBe('cat,dog')
})
Comment on lines +4 to +29
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Good test coverage — consider adding edge case tests.

The tests thoroughly cover the main scenarios and address the prior review feedback about adjacent OR tokens. Consider adding tests for:

  1. Boundary OR tokens: "OR cat", "cat OR", "OR" alone — to document whether leading/trailing commas are expected or should be stripped.
  2. Empty/falsy inputs: "", null, undefined — to verify the guard clause at lines 2-4 of the helper.
📝 Suggested additional test cases
it('handles empty and falsy inputs', () => {
  expect(normalizeSearchTagInput('')).toBe('')
  expect(normalizeSearchTagInput(null as unknown as string)).toBe('')
  expect(normalizeSearchTagInput(undefined as unknown as string)).toBe('')
})

it('handles leading/trailing OR tokens', () => {
  // Document current behavior (produces leading/trailing commas)
  // or update if the helper is changed to strip them
  expect(normalizeSearchTagInput('OR cat')).toBe(',cat')
  expect(normalizeSearchTagInput('cat OR')).toBe('cat,')
  expect(normalizeSearchTagInput('OR')).toBe(',')
})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/assets/search-tag-input-helper.test.ts` around lines 4 - 29, Add unit
tests for edge cases to document and assert the current guard behavior of
normalizeSearchTagInput: add a test that passes empty and falsy inputs ('' ,
null as unknown as string, undefined as unknown as string) and asserts they
return '' to verify the early-return/guard clause; and add a test for boundary
OR tokens ('OR cat', 'cat OR', 'OR') that documents current behavior (expect
',cat', 'cat,', ',' respectively) or updates expectations if you change the
helper to strip leading/trailing separators — reference the
normalizeSearchTagInput function in the test suite to locate where to add these
cases.

})