mirror of
https://github.com/utopia-os/utopia-ui.git
synced 2026-04-06 01:25:33 +00:00
fix(other): improve e2e test reliability with API mocking and race condition fixes (#621)
This commit is contained in:
parent
a4d8bbeae8
commit
b9bab44274
1
.gitignore
vendored
1
.gitignore
vendored
@ -5,3 +5,4 @@ cypress/node_modules/
|
|||||||
cypress/results/
|
cypress/results/
|
||||||
cypress/runner-results/
|
cypress/runner-results/
|
||||||
cypress/screenshots/
|
cypress/screenshots/
|
||||||
|
|
||||||
|
|||||||
99
backend/prepare-seed.js
Normal file
99
backend/prepare-seed.js
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepares seed data by updating event dates relative to the current date.
|
||||||
|
*
|
||||||
|
* This script modifies items.json in-place, updating all items with
|
||||||
|
* layer "layer-events" to have realistic start/end dates that ensure
|
||||||
|
* events are visible in the app (end dates in the future).
|
||||||
|
*
|
||||||
|
* Date strategy:
|
||||||
|
* - Event 1 (item-event-1): Long-running, started 30 days ago, ends in 365 days
|
||||||
|
* - Event 2 (item-event-2): Upcoming single-day event in 7 days
|
||||||
|
* - Event 3 (item-event-3): Ongoing event, started yesterday, ends tomorrow
|
||||||
|
* - Event 4 (item-event-4): Upcoming multi-day conference in 30 days
|
||||||
|
*/
|
||||||
|
|
||||||
|
const fs = require('fs')
|
||||||
|
const path = require('path')
|
||||||
|
|
||||||
|
const seedPath = path.join(__dirname, 'directus-config/development/seed/items.json')
|
||||||
|
|
||||||
|
function addDays(date, days) {
|
||||||
|
const result = new Date(date)
|
||||||
|
result.setDate(result.getDate() + days)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
function addHours(date, hours) {
|
||||||
|
const result = new Date(date)
|
||||||
|
result.setHours(result.getHours() + hours)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatDateTime(date) {
|
||||||
|
return date.toISOString().slice(0, 19)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate dynamic dates for each event based on current time
|
||||||
|
*/
|
||||||
|
function getEventDates(syncId, now) {
|
||||||
|
const dateConfigs = {
|
||||||
|
// "Some Event" - long-running, started in past, ends far in future
|
||||||
|
'item-event-1': {
|
||||||
|
start: addDays(now, -30),
|
||||||
|
end: addDays(now, 365)
|
||||||
|
},
|
||||||
|
// "Tech Meetup Munich" - upcoming single-day event (the one used in search tests)
|
||||||
|
'item-event-2': {
|
||||||
|
start: addHours(addDays(now, 7), 18),
|
||||||
|
end: addHours(addDays(now, 7), 21)
|
||||||
|
},
|
||||||
|
// "Sustainability Workshop NYC" - ongoing event (started yesterday, ends tomorrow)
|
||||||
|
'item-event-3': {
|
||||||
|
start: addHours(addDays(now, -1), 14),
|
||||||
|
end: addHours(addDays(now, 1), 17)
|
||||||
|
},
|
||||||
|
// "Open Source Conference" - upcoming multi-day conference
|
||||||
|
'item-event-4': {
|
||||||
|
start: addHours(addDays(now, 30), 9),
|
||||||
|
end: addHours(addDays(now, 32), 18)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dateConfigs[syncId] || null
|
||||||
|
}
|
||||||
|
|
||||||
|
function prepareSeedData() {
|
||||||
|
// Read the current items.json
|
||||||
|
const content = fs.readFileSync(seedPath, 'utf8')
|
||||||
|
const seedData = JSON.parse(content)
|
||||||
|
|
||||||
|
const now = new Date()
|
||||||
|
let updatedCount = 0
|
||||||
|
|
||||||
|
// Update event items with dynamic dates
|
||||||
|
for (const item of seedData.data) {
|
||||||
|
if (item.layer === 'layer-events' && item._sync_id) {
|
||||||
|
const dates = getEventDates(item._sync_id, now)
|
||||||
|
if (dates) {
|
||||||
|
item.start = formatDateTime(dates.start)
|
||||||
|
item.end = formatDateTime(dates.end)
|
||||||
|
console.log(` ${item._sync_id} (${item.name}):`)
|
||||||
|
console.log(` start: ${item.start}`)
|
||||||
|
console.log(` end: ${item.end}`)
|
||||||
|
updatedCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write back to items.json
|
||||||
|
fs.writeFileSync(seedPath, JSON.stringify(seedData, null, 4))
|
||||||
|
|
||||||
|
console.log(`\nUpdated ${updatedCount} event(s) with dynamic dates.`)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Preparing seed data with dynamic dates...\n')
|
||||||
|
prepareSeedData()
|
||||||
|
|
||||||
@ -15,6 +15,9 @@ PGDATABASE="${PGDATABASE:-'directus'}"
|
|||||||
PROJECT_NAME="${PROJECT:-development}"
|
PROJECT_NAME="${PROJECT:-development}"
|
||||||
PROJECT_FOLDER=$SCRIPT_DIR/directus-config/$PROJECT_NAME
|
PROJECT_FOLDER=$SCRIPT_DIR/directus-config/$PROJECT_NAME
|
||||||
|
|
||||||
|
echo "Preparing seed data with dynamic dates"
|
||||||
|
node $SCRIPT_DIR/prepare-seed.js || exit 1
|
||||||
|
|
||||||
echo "Seed data"
|
echo "Seed data"
|
||||||
npx directus-sync@3.4.0 seed push \
|
npx directus-sync@3.4.0 seed push \
|
||||||
--seed-path $PROJECT_FOLDER/seed \
|
--seed-path $PROJECT_FOLDER/seed \
|
||||||
|
|||||||
@ -74,7 +74,7 @@ describe('Utopia Map Search', () => {
|
|||||||
cy.contains('Wat Arun').first().click()
|
cy.contains('Wat Arun').first().click()
|
||||||
})
|
})
|
||||||
|
|
||||||
cy.get('.leaflet-popup').should('be.visible')
|
cy.get('.leaflet-popup', { timeout: 15000 }).should('exist')
|
||||||
cy.get('.leaflet-popup-content').should('contain', 'Wat Arun')
|
cy.get('.leaflet-popup-content').should('contain', 'Wat Arun')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -76,6 +76,12 @@ Cypress.Commands.add('searchFor', (query: string) => {
|
|||||||
Cypress.Commands.add('waitForMapReady', () => {
|
Cypress.Commands.add('waitForMapReady', () => {
|
||||||
cy.get('[data-cy="search-input"]', { timeout: 10000 }).should('be.visible')
|
cy.get('[data-cy="search-input"]', { timeout: 10000 }).should('be.visible')
|
||||||
cy.get('.leaflet-container', { timeout: 10000 }).should('be.visible')
|
cy.get('.leaflet-container', { timeout: 10000 }).should('be.visible')
|
||||||
|
cy.wait('@getLayers', { timeout: 15000 }).then((interception) => {
|
||||||
|
const layerCount = interception.response?.body?.data?.length || 3
|
||||||
|
for (let i = 0; i < layerCount; i++) {
|
||||||
|
cy.wait('@getLayerItems', { timeout: 15000 })
|
||||||
|
}
|
||||||
|
})
|
||||||
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)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -5,6 +5,71 @@ import './commands'
|
|||||||
// for screenshot embedding
|
// for screenshot embedding
|
||||||
import addContext from 'mochawesome/addContext'
|
import addContext from 'mochawesome/addContext'
|
||||||
|
|
||||||
|
const photonMockData: Record<string, object> = {
|
||||||
|
berlin: {
|
||||||
|
type: 'FeatureCollection',
|
||||||
|
features: [
|
||||||
|
{
|
||||||
|
type: 'Feature',
|
||||||
|
properties: {
|
||||||
|
osm_type: 'R',
|
||||||
|
osm_id: 62422,
|
||||||
|
osm_key: 'place',
|
||||||
|
osm_value: 'city',
|
||||||
|
type: 'city',
|
||||||
|
countrycode: 'DE',
|
||||||
|
name: 'Berlin',
|
||||||
|
country: 'Germany',
|
||||||
|
state: 'Berlin',
|
||||||
|
extent: [13.088345, 52.6755087, 13.7611609, 52.3382448],
|
||||||
|
},
|
||||||
|
geometry: { type: 'Point', coordinates: [13.3951309, 52.5173885] },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'wat arun': {
|
||||||
|
type: 'FeatureCollection',
|
||||||
|
features: [
|
||||||
|
{
|
||||||
|
type: 'Feature',
|
||||||
|
properties: {
|
||||||
|
osm_type: 'W',
|
||||||
|
osm_id: 25867629,
|
||||||
|
osm_key: 'tourism',
|
||||||
|
osm_value: 'attraction',
|
||||||
|
type: 'attraction',
|
||||||
|
countrycode: 'TH',
|
||||||
|
name: 'Wat Arun',
|
||||||
|
country: 'Thailand',
|
||||||
|
city: 'Bangkok',
|
||||||
|
extent: [100.4882, 13.7437, 100.4912, 13.7407],
|
||||||
|
},
|
||||||
|
geometry: { type: 'Point', coordinates: [100.4897, 13.7437] },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.intercept('GET', 'https://photon.komoot.io/api/*', (req) => {
|
||||||
|
const url = new URL(req.url)
|
||||||
|
const query = (url.searchParams.get('q') || '').toLowerCase()
|
||||||
|
|
||||||
|
const mockKey = Object.keys(photonMockData).find((key) =>
|
||||||
|
query.includes(key.toLowerCase()),
|
||||||
|
)
|
||||||
|
|
||||||
|
if (mockKey) {
|
||||||
|
req.reply(photonMockData[mockKey])
|
||||||
|
} else {
|
||||||
|
req.reply({ type: 'FeatureCollection', features: [] })
|
||||||
|
}
|
||||||
|
}).as('photonApi')
|
||||||
|
|
||||||
|
cy.intercept('GET', '**/items/layers*').as('getLayers')
|
||||||
|
cy.intercept('GET', '**/items/items*').as('getLayerItems')
|
||||||
|
})
|
||||||
|
|
||||||
// Global exception handler
|
// Global exception handler
|
||||||
Cypress.on('uncaught:exception', (err) => {
|
Cypress.on('uncaught:exception', (err) => {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user