mirror of
https://github.com/utopia-os/utopia-ui.git
synced 2026-04-06 01:25:33 +00:00
add itemsearch tests to cypress search testing
This commit is contained in:
parent
81349e299b
commit
eeee4b9d01
50
cypress/e2e/search/item-search.cy.ts
Normal file
50
cypress/e2e/search/item-search.cy.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/// <reference types="cypress" />
|
||||||
|
|
||||||
|
describe('Utopia Map Item Search', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.clearCookies()
|
||||||
|
cy.clearLocalStorage()
|
||||||
|
cy.window().then((win) => {
|
||||||
|
win.sessionStorage.clear()
|
||||||
|
})
|
||||||
|
|
||||||
|
cy.visit('/')
|
||||||
|
cy.waitForMapReady()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should find items by exact name match', () => {
|
||||||
|
cy.searchFor('Tech Meetup Munich')
|
||||||
|
cy.get('[data-cy="search-suggestions"]').should('contain', 'Tech Meetup Munich')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should find items by partial name match (case insensitive)', () => {
|
||||||
|
cy.searchFor('café collaboration')
|
||||||
|
cy.get('[data-cy="search-suggestions"]').should('contain', 'Café Collaboration London')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should find items by text content', () => {
|
||||||
|
cy.searchFor('sustainability')
|
||||||
|
cy.get('[data-cy="search-suggestions"]').should('contain', 'Alex Entrepreneur')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should navigate to item profile when clicking search result', () => {
|
||||||
|
cy.intercept('GET', '**/items*').as('getItems')
|
||||||
|
|
||||||
|
cy.visit('/')
|
||||||
|
cy.waitForMapReady()
|
||||||
|
|
||||||
|
cy.searchFor('makerspace')
|
||||||
|
cy.get('[data-cy="search-suggestions"]').within(() => {
|
||||||
|
cy.get('[data-cy="search-item-result"]').contains('Makerspace Tokyo').click()
|
||||||
|
})
|
||||||
|
|
||||||
|
cy.url().should('match', /\/(item\/|[a-f0-9-]+)/)
|
||||||
|
cy.get('body').should('contain', 'Makerspace Tokyo')
|
||||||
|
|
||||||
|
// Verify search uses local filtering (items already loaded)
|
||||||
|
cy.get('@getItems.all').then((calls) => {
|
||||||
|
expect(calls.length).to.be.greaterThan(0)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
@ -87,6 +87,7 @@ export default tseslint.config(
|
|||||||
'cypress/downloads/**',
|
'cypress/downloads/**',
|
||||||
'cypress/screenshots/**',
|
'cypress/screenshots/**',
|
||||||
'cypress/videos/**',
|
'cypress/videos/**',
|
||||||
|
'results/**',
|
||||||
'dist/**',
|
'dist/**',
|
||||||
'build/**',
|
'build/**',
|
||||||
'*.min.js'
|
'*.min.js'
|
||||||
|
|||||||
@ -22,6 +22,43 @@ declare global {
|
|||||||
* @example cy.waitForMapReady()
|
* @example cy.waitForMapReady()
|
||||||
*/
|
*/
|
||||||
waitForMapReady(): Chainable<Element>
|
waitForMapReady(): Chainable<Element>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Click on a map marker
|
||||||
|
* @example cy.clickMarker() // clicks first marker
|
||||||
|
*/
|
||||||
|
clickMarker(): Chainable<Element>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for a popup to appear on the map
|
||||||
|
* @example cy.waitForPopup()
|
||||||
|
*/
|
||||||
|
waitForPopup(): Chainable<Element>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the currently open popup
|
||||||
|
* @example cy.closePopup()
|
||||||
|
*/
|
||||||
|
closePopup(): Chainable<Element>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle a layer's visibility in the layer control
|
||||||
|
* @param layerName - Name of the layer to toggle
|
||||||
|
* @example cy.toggleLayer('places')
|
||||||
|
*/
|
||||||
|
toggleLayer(layerName: string): Chainable<Element>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open the layer control panel
|
||||||
|
* @example cy.openLayerControl()
|
||||||
|
*/
|
||||||
|
openLayerControl(): Chainable<Element>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the layer control panel
|
||||||
|
* @example cy.closeLayerControl()
|
||||||
|
*/
|
||||||
|
closeLayerControl(): Chainable<Element>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -42,4 +79,30 @@ Cypress.Commands.add('waitForMapReady', () => {
|
|||||||
cy.get('.leaflet-marker-icon', { timeout: 15000 }).should('have.length.at.least', 1)
|
cy.get('.leaflet-marker-icon', { timeout: 15000 }).should('have.length.at.least', 1)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Cypress.Commands.add('clickMarker', () => {
|
||||||
|
// For now, always use force click since markers might be clustered or outside viewport
|
||||||
|
cy.get('.leaflet-marker-icon').first().click({ force: true })
|
||||||
|
})
|
||||||
|
|
||||||
|
Cypress.Commands.add('waitForPopup', () => {
|
||||||
|
cy.get('[data-cy="item-popup"]', { timeout: 10000 }).should('be.visible')
|
||||||
|
})
|
||||||
|
|
||||||
|
Cypress.Commands.add('closePopup', () => {
|
||||||
|
cy.get('.leaflet-popup-close-button').click()
|
||||||
|
})
|
||||||
|
|
||||||
|
Cypress.Commands.add('toggleLayer', (layerName: string) => {
|
||||||
|
cy.get(`[data-cy="layer-checkbox-${layerName}"]`).click()
|
||||||
|
})
|
||||||
|
|
||||||
|
Cypress.Commands.add('openLayerControl', () => {
|
||||||
|
cy.get('[data-cy="layer-control-button"]').click()
|
||||||
|
cy.get('[data-cy="layer-control-panel"]', { timeout: 5000 }).should('be.visible')
|
||||||
|
})
|
||||||
|
|
||||||
|
Cypress.Commands.add('closeLayerControl', () => {
|
||||||
|
cy.get('[data-cy="layer-control-close"]').click()
|
||||||
|
})
|
||||||
|
|
||||||
export {}
|
export {}
|
||||||
|
|||||||
@ -113,6 +113,7 @@ export const SearchControl = () => {
|
|||||||
autoComplete='off'
|
autoComplete='off'
|
||||||
value={value}
|
value={value}
|
||||||
className='tw:input tw:input-bordered tw:h-12 tw:grow tw:shadow-xl tw:rounded-box tw:pr-12 tw:w-full'
|
className='tw:input tw:input-bordered tw:h-12 tw:grow tw:shadow-xl tw:rounded-box tw:pr-12 tw:w-full'
|
||||||
|
data-cy='search-input'
|
||||||
ref={searchInput}
|
ref={searchInput}
|
||||||
onChange={(e) => setValue(e.target.value)}
|
onChange={(e) => setValue(e.target.value)}
|
||||||
onFocus={() => {
|
onFocus={() => {
|
||||||
@ -124,6 +125,7 @@ export const SearchControl = () => {
|
|||||||
{value.length > 0 && (
|
{value.length > 0 && (
|
||||||
<button
|
<button
|
||||||
className='tw:btn tw:btn-sm tw:btn-circle tw:absolute tw:right-2 tw:top-2'
|
className='tw:btn tw:btn-sm tw:btn-circle tw:absolute tw:right-2 tw:top-2'
|
||||||
|
data-cy='search-clear-button'
|
||||||
onClick={() => setValue('')}
|
onClick={() => setValue('')}
|
||||||
>
|
>
|
||||||
✕
|
✕
|
||||||
@ -140,13 +142,14 @@ export const SearchControl = () => {
|
|||||||
value.length === 0 ? (
|
value.length === 0 ? (
|
||||||
''
|
''
|
||||||
) : (
|
) : (
|
||||||
<div className='tw:card tw:card-body tw:bg-base-100 tw:p-4 tw:mt-2 tw:shadow-xl tw:overflow-y-auto tw:max-h-[calc(100dvh-152px)] tw:absolute tw:z-3000 tw:w-83'>
|
<div className='tw:card tw:card-body tw:bg-base-100 tw:p-4 tw:mt-2 tw:shadow-xl tw:overflow-y-auto tw:max-h-[calc(100dvh-152px)] tw:absolute tw:z-3000 tw:w-83' data-cy='search-suggestions'>
|
||||||
{tagsResults.length > 0 && (
|
{tagsResults.length > 0 && (
|
||||||
<div className='tw:flex tw:flex-wrap'>
|
<div className='tw:flex tw:flex-wrap'>
|
||||||
{tagsResults.slice(0, 3).map((tag) => (
|
{tagsResults.slice(0, 3).map((tag) => (
|
||||||
<div
|
<div
|
||||||
key={tag.name}
|
key={tag.name}
|
||||||
className='tw:rounded-2xl tw:text-white tw:p-1 tw:px-4 tw:shadow-md tw:card tw:mr-2 tw:mb-2 tw:cursor-pointer'
|
className='tw:rounded-2xl tw:text-white tw:p-1 tw:px-4 tw:shadow-md tw:card tw:mr-2 tw:mb-2 tw:cursor-pointer'
|
||||||
|
data-cy='search-tag-result'
|
||||||
style={{ backgroundColor: tag.color }}
|
style={{ backgroundColor: tag.color }}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
addFilterTag(tag)
|
addFilterTag(tag)
|
||||||
@ -165,6 +168,7 @@ export const SearchControl = () => {
|
|||||||
<div
|
<div
|
||||||
key={item.id}
|
key={item.id}
|
||||||
className='tw:cursor-pointer tw:hover:font-bold tw:flex tw:flex-row'
|
className='tw:cursor-pointer tw:hover:font-bold tw:flex tw:flex-row'
|
||||||
|
data-cy='search-item-result'
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const marker = Object.entries(leafletRefs).find((r) => r[1].item === item)?.[1]
|
const marker = Object.entries(leafletRefs).find((r) => r[1].item === item)?.[1]
|
||||||
.marker
|
.marker
|
||||||
@ -178,7 +182,7 @@ export const SearchControl = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{item.layer?.markerIcon.image ? (
|
{item.layer?.markerIcon.image ? (
|
||||||
<div className='tw:w-7 tw:h-full tw:flex tw:justify-center tw:items-center'>
|
<div className='tw:w-7 tw:h-full tw:flex tw:justify-center tw:items-center' data-cy='search-item-icon'>
|
||||||
<SVG
|
<SVG
|
||||||
src={appState.assetsApi.url + item.layer.markerIcon.image}
|
src={appState.assetsApi.url + item.layer.markerIcon.image}
|
||||||
className='tw:text-current tw:mr-2 tw:mt-0'
|
className='tw:text-current tw:mr-2 tw:mt-0'
|
||||||
@ -191,7 +195,7 @@ export const SearchControl = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className='tw:w-7' />
|
<div className='tw:w-7' data-cy='search-item-icon-placeholder' />
|
||||||
)}
|
)}
|
||||||
<div>
|
<div>
|
||||||
<div className='tw:text-sm tw:overflow-hidden tw:text-ellipsis tw:whitespace-nowrap tw:max-w-[17rem]'>
|
<div className='tw:text-sm tw:overflow-hidden tw:text-ellipsis tw:whitespace-nowrap tw:max-w-[17rem]'>
|
||||||
@ -207,6 +211,7 @@ export const SearchControl = () => {
|
|||||||
{Array.from(geoResults).map((geo) => (
|
{Array.from(geoResults).map((geo) => (
|
||||||
<div
|
<div
|
||||||
className='tw:flex tw:flex-row tw:hover:font-bold tw:cursor-pointer'
|
className='tw:flex tw:flex-row tw:hover:font-bold tw:cursor-pointer'
|
||||||
|
data-cy='search-geo-result'
|
||||||
key={Math.random()}
|
key={Math.random()}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
searchInput.current?.blur()
|
searchInput.current?.blur()
|
||||||
@ -262,6 +267,7 @@ export const SearchControl = () => {
|
|||||||
{isGeoCoordinate(value) && (
|
{isGeoCoordinate(value) && (
|
||||||
<div
|
<div
|
||||||
className='tw:flex tw:flex-row tw:hover:font-bold tw:cursor-pointer'
|
className='tw:flex tw:flex-row tw:hover:font-bold tw:cursor-pointer'
|
||||||
|
data-cy='search-coordinate-result'
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
marker(
|
marker(
|
||||||
new LatLng(extractCoordinates(value)![0], extractCoordinates(value)![1]),
|
new LatLng(extractCoordinates(value)![0], extractCoordinates(value)![1]),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user