Skip to content
Merged
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
2 changes: 1 addition & 1 deletion frontend/e2e/accessibility.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const pages: {
path: './#/sightings/3',
ready: (page) =>
expect(
page.getByRole('heading', { level: 1, name: 'Oslo, NO' }),
page.getByRole('heading', { level: 1, name: 'Oslo, Norway' }),
).toBeVisible(),
},
{
Expand Down
2 changes: 1 addition & 1 deletion frontend/e2e/submit-sighting.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ test('fill in and submit the form with the keyboard', async ({ page }) => {
await tabTo(page, page.getByRole('link', { name: 'View your sighting' }))
await page.keyboard.press('Enter')
await expect(
page.getByRole('heading', { level: 1, name: 'Trondheim, NO' }),
page.getByRole('heading', { level: 1, name: 'Trondheim, Norway' }),
).toBeVisible()
await expect(page.getByText('User submitted')).toBeVisible()
await expect(
Expand Down
15 changes: 3 additions & 12 deletions frontend/src/components/SearchFilters.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,9 @@
import { useQuery } from '@apollo/client/react'
import { FILTER_OPTIONS_QUERY } from '../graphql/queries'
import { createRetry } from '../utils/create-retry'
import { formatCountry } from '../utils/format-sighting'
import './SearchFilters.css'

const countryNames = new Intl.DisplayNames(['en'], { type: 'region' })

function countryName(code: string) {
try {
return countryNames.of(code.toUpperCase()) ?? code
} catch {
return code
}
}

export interface SearchFiltersValue {
shape: string
country: string
Expand All @@ -38,7 +29,7 @@ export default function SearchFilters({
const countries = [
...new Set([...(data?.countries ?? []), filters.country].filter(Boolean)),
]
.map((code) => ({ code, name: countryName(code) }))
.map((code) => ({ code, name: formatCountry(code) }))
.sort((left, right) => left.name.localeCompare(right.name, 'en'))
const chips: { key: keyof SearchFiltersValue; label: string }[] = [
{
Expand All @@ -47,7 +38,7 @@ export default function SearchFilters({
},
{
key: 'country',
label: filters.country && `Country: ${countryName(filters.country)}`,
label: filters.country && `Country: ${formatCountry(filters.country)}`,
},
{ key: 'from', label: filters.from && `From: ${filters.from}` },
{ key: 'to', label: filters.to && `To: ${filters.to}` },
Expand Down
10 changes: 6 additions & 4 deletions frontend/src/tests/SearchPage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,10 @@ describe('SearchPage', () => {
target: { value: 'nz' },
})
expect(await screen.findByText('1 result')).toBeInTheDocument()
expect(await screen.findByText('Gisborne, NZ')).toBeInTheDocument()
expect(screen.queryByText('San Marcos, TX, US')).not.toBeInTheDocument()
expect(await screen.findByText('Gisborne, New Zealand')).toBeInTheDocument()
expect(
screen.queryByText('San Marcos, TX, United States'),
).not.toBeInTheDocument()
expect(requests.at(-1)?.filter).toEqual({ country: 'nz' })
expect(screen.getByLabelText('Current URL')).toHaveTextContent('country=nz')
})
Expand Down Expand Up @@ -314,7 +316,7 @@ describe('SearchPage', () => {

expect(screen.getByRole('searchbox')).toHaveValue('oslo')
expect(
await screen.findByRole('link', { name: 'Oslo, NO' }),
await screen.findByRole('link', { name: 'Oslo, Norway' }),
).toBeInTheDocument()
expect(requests).toEqual([{ search: 'oslo', sort: 'DATE_DESC', first: 20 }])
})
Expand Down Expand Up @@ -385,7 +387,7 @@ describe('SearchPage', () => {
fireEvent.click(screen.getByRole('button', { name: 'Try again' }))

expect(
await screen.findByRole('link', { name: 'Oslo, NO' }),
await screen.findByRole('link', { name: 'Oslo, Norway' }),
).toBeInTheDocument()
expect(screen.queryByRole('alert')).not.toBeInTheDocument()
expect(requests).toHaveLength(2)
Expand Down
24 changes: 16 additions & 8 deletions frontend/src/tests/SightingDetailPage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,13 @@ describe('SightingDetailPage', () => {
screen.getByRole('heading', { name: 'Loading sighting…' }),
).toBeInTheDocument()
expect(
await screen.findByRole('heading', { name: 'San Marcos, TX, US' }),
await screen.findByRole('heading', {
name: 'San Marcos, TX, United States',
}),
).toBeInTheDocument()
expect(container.querySelectorAll('[aria-live]')).toHaveLength(1)
expect(container.querySelector('[aria-live]')).toBe(announcement)
expect(announcement).toHaveTextContent('San Marcos, TX, US')
expect(announcement).toHaveTextContent('San Marcos, TX, United States')
expect(screen.getByRole('article')).toBe(content)
expect(content).toHaveAttribute('aria-busy', 'false')
expect(screen.getByText('45 min')).toBeInTheDocument()
Expand All @@ -92,7 +94,7 @@ describe('SightingDetailPage', () => {
'true',
)
expect(screen.getByTestId('detail-map')).toHaveAttribute('data-zoom', '11')
expect(document.title).toBe('San Marcos, TX, US · UFOund it')
expect(document.title).toBe('San Marcos, TX, United States · UFOund it')
})

it('shows fallbacks for a missing shape and hides missing comments', async () => {
Expand All @@ -107,7 +109,9 @@ describe('SightingDetailPage', () => {
])

expect(
await screen.findByRole('heading', { name: 'San Marcos, TX, US' }),
await screen.findByRole('heading', {
name: 'San Marcos, TX, United States',
}),
).toBeInTheDocument()
expect(screen.getByText('Unknown')).toBeInTheDocument()
expect(screen.queryByText(/This event took place/)).not.toBeInTheDocument()
Expand Down Expand Up @@ -159,7 +163,7 @@ describe('SightingDetailPage', () => {
expect(await screen.findByText('User submitted')).toBeInTheDocument()
// Outside the heading, so the page title is still just the location.
expect(
screen.getByRole('heading', { name: 'San Marcos, TX, US' }),
screen.getByRole('heading', { name: 'San Marcos, TX, United States' }),
).toBeInTheDocument()
})

Expand All @@ -168,7 +172,9 @@ describe('SightingDetailPage', () => {
renderDetailPage('/sightings/1', [found])

expect(
await screen.findByRole('heading', { name: 'San Marcos, TX, US' }),
await screen.findByRole('heading', {
name: 'San Marcos, TX, United States',
}),
).toBeInTheDocument()
expect(screen.queryByText('User submitted')).not.toBeInTheDocument()
})
Expand Down Expand Up @@ -241,11 +247,13 @@ describe('SightingDetailPage', () => {
expect(screen.queryByRole('alert')).not.toBeInTheDocument()

expect(
await screen.findByRole('heading', { name: 'San Marcos, TX, US' }),
await screen.findByRole('heading', {
name: 'San Marcos, TX, United States',
}),
).toBeInTheDocument()
expect(container.querySelectorAll('[aria-live]')).toHaveLength(1)
expect(container.querySelector('[aria-live]')).toBe(announcement)
expect(announcement).toHaveTextContent('San Marcos, TX, US')
expect(announcement).toHaveTextContent('San Marcos, TX, United States')
expect(screen.getByRole('article')).toHaveAttribute('aria-busy', 'false')
})
})
8 changes: 5 additions & 3 deletions frontend/src/tests/SightingList.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ describe('SightingList', () => {

expect(screen.getByText('1 result')).toBeInTheDocument()
expect(
screen.getByRole('link', { name: 'San Marcos, TX, US' }),
screen.getByRole('link', { name: 'San Marcos, TX, United States' }),
).toHaveAttribute('href', '/sightings/1')
expect(screen.getByText('cylinder')).toBeInTheDocument()
expect(screen.getByText('45 min')).toBeInTheDocument()
Expand All @@ -67,7 +67,9 @@ describe('SightingList', () => {

expect(screen.getAllByText('User submitted')).toHaveLength(1)
// The badge is not part of the link, so its name stays the location.
const link = screen.getByRole('link', { name: 'San Marcos, TX, US' })
const link = screen.getByRole('link', {
name: 'San Marcos, TX, United States',
})
expect(link.closest('li')).toHaveTextContent('User submitted')
})

Expand Down Expand Up @@ -184,7 +186,7 @@ describe('SightingList', () => {

expect(screen.getByRole('list')).toHaveAttribute('aria-busy', 'true')
expect(
screen.getByRole('link', { name: 'San Marcos, TX, US' }),
screen.getByRole('link', { name: 'San Marcos, TX, United States' }),
).toBeInTheDocument()
})

Expand Down
6 changes: 4 additions & 2 deletions frontend/src/tests/SightingMap.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,9 @@ describe('SightingMap', () => {
'data-position',
`${sighting.latitude},${sighting.longitude}`,
)
expect(screen.getByText('San Marcos, TX, US')).toBeInTheDocument()
expect(
screen.getByText('San Marcos, TX, United States'),
).toBeInTheDocument()
})

it('shows a placeholder instead of a map when coordinates are missing', () => {
Expand Down Expand Up @@ -126,7 +128,7 @@ describe('SightingMap', () => {
expect(leafletContainer).toHaveAttribute('role', 'region')
expect(leafletContainer).toHaveAttribute(
'aria-label',
'Map showing San Marcos, TX, US',
'Map showing San Marcos, TX, United States',
)
})
})
2 changes: 1 addition & 1 deletion frontend/src/tests/SubmitSightingPage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -532,7 +532,7 @@ describe('SubmitSightingPage', () => {
expect(screen.getByText('13 results')).toBeInTheDocument(),
)
expect(
screen.getByRole('link', { name: 'trondheim, NO' }),
screen.getByRole('link', { name: 'trondheim, Norway' }),
).toBeInTheDocument()
})
})
Expand Down
30 changes: 25 additions & 5 deletions frontend/src/tests/format-sighting.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { describe, expect, it } from 'vitest'
import type { UfoSighting } from '../types/ufo-sighting'
import {
formatCoordinates,
formatCountry,
formatDate,
formatDuration,
formatLocation,
Expand Down Expand Up @@ -30,10 +31,29 @@ describe('sighting formatters', () => {
expect(formatDuration(125)).toBe('2 min 5 sec')
})

it('formats a location with available region codes in uppercase', () => {
expect(formatLocation(sighting)).toBe('Oslo, OS, NO')
expect(formatLocation({ ...sighting, state: '' })).toBe('Oslo, NO')
expect(formatLocation({ ...sighting, state: '', country: '' })).toBe('Oslo')
it('names a known country code in either case', () => {
expect(formatCountry('no')).toBe('Norway')
expect(formatCountry('GB')).toBe('United Kingdom')
})

it('shows a code that is not a country as it is, in uppercase', () => {
expect(formatCountry('eu')).toBe('EU')
expect(formatCountry('xx')).toBe('XX')
})

it('says when the country is missing', () => {
expect(formatCountry(null)).toBe('Unknown country')
expect(formatCountry('')).toBe('Unknown country')
expect(formatCountry(' ')).toBe('Unknown country')
})

it('formats a location with the state code and the country name', () => {
expect(formatLocation(sighting)).toBe('Oslo, OS, Norway')
expect(formatLocation({ ...sighting, state: '' })).toBe('Oslo, Norway')
expect(formatLocation({ ...sighting, country: 'xx' })).toBe('Oslo, OS, XX')
expect(formatLocation({ ...sighting, state: null, country: null })).toBe(
'Oslo, Unknown country',
)
})

it('falls back to readable text when the API returns null', () => {
Expand All @@ -42,7 +62,7 @@ describe('sighting formatters', () => {
expect(formatDuration(null)).toBe('Unknown')
expect(formatShape(null)).toBe('Unknown')
expect(formatLocation({ ...sighting, city: null })).toBe(
'Unknown location, OS, NO',
'Unknown location, OS, Norway',
)
expect(
formatLocation({ ...sighting, city: null, state: null, country: null }),
Expand Down
24 changes: 19 additions & 5 deletions frontend/src/utils/format-sighting.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { UfoSighting } from '../types/ufo-sighting'
import { countryNameFromCode } from './countries'

export function formatDate(date: string | null) {
if (!date) return 'Unknown date'
Expand All @@ -21,14 +22,27 @@ export function formatDuration(seconds: number | null) {
return `${minutes} min ${remainingSeconds} sec`
}

/**
* Names a country code, e.g. "no" as "Norway". A code that is not an ISO
* country stays visible in uppercase instead of being hidden behind a
* guessed name, and a missing one says so.
*/
export function formatCountry(code: string | null) {
if (!code?.trim()) return 'Unknown country'
return countryNameFromCode(code.trim()) ?? code.trim().toUpperCase()
}

export function formatLocation(sighting: UfoSighting) {
const city = sighting.city ?? 'Unknown location'
const region = [sighting.state, sighting.country]
const { city, state, country } = sighting
if (!city && !state && !country) return 'Unknown location'

return [
city ?? 'Unknown location',
state?.toUpperCase(),
formatCountry(country),
]
.filter(Boolean)
.join(', ')
.toUpperCase()

return region ? `${city}, ${region}` : city
}

export function formatShape(shape: string | null) {
Expand Down
Loading