mirror of
https://github.com/Freika/dawarich.git
synced 2026-01-06 05:09:40 -06:00
Update tests
This commit is contained in:
@@ -15,14 +15,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
- Taiwan flag is now shown on its own instead of in combination with China flag.
|
||||
- On the registration page and other user forms, if something goes wrong, error messages are now shown to the user.
|
||||
- Leaving family, deleting family and cancelling invitations now prompt confirmation dialog to prevent accidental actions.
|
||||
- Each pending family invitation now also contain a link to share with the invitee.
|
||||
- Each pending family invitation now also contains a link to share with the invitee.
|
||||
|
||||
## Changed
|
||||
|
||||
- Removed useless system tests and cover map functionality with Playwright e2e tests instead.
|
||||
- S3 storage now can be used in self-hosted instances as well. Set STORAGE_BACKEND environment variable to `s3` and provide `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_REGION`, `AWS_BUCKET` and `AWS_ENDPOINT_URL` environment variables to configure it.
|
||||
- Number of family members on self-hosted instances is no longer limited. #1918
|
||||
- Export to GPX now adds adds speed and course to each point if they are available.
|
||||
- Export to GPX now adds speed and course to each point if they are available.
|
||||
- `docker-compose.yml` file updated to provide sensible defaults for self-hosted production environment.
|
||||
- `.env.example` file added with default environment variables.
|
||||
- Single Dockerfile introduced so Dawarich could be run in self-hosted mode in production environment.
|
||||
|
||||
@@ -398,18 +398,15 @@ export class VisitsManager {
|
||||
* Adds a cancel button to the drawer to clear the selection
|
||||
*/
|
||||
addSelectionCancelButton() {
|
||||
console.log('addSelectionCancelButton: Called');
|
||||
const container = document.getElementById('visits-list');
|
||||
if (!container) {
|
||||
console.error('addSelectionCancelButton: visits-list container not found');
|
||||
return;
|
||||
}
|
||||
console.log('addSelectionCancelButton: Container found');
|
||||
|
||||
// Remove any existing button container first to avoid duplicates
|
||||
const existingButtonContainer = document.getElementById('selection-button-container');
|
||||
if (existingButtonContainer) {
|
||||
console.log('addSelectionCancelButton: Removing existing button container');
|
||||
existingButtonContainer.remove();
|
||||
}
|
||||
|
||||
@@ -438,9 +435,6 @@ export class VisitsManager {
|
||||
badge.className = 'badge badge-sm ml-1';
|
||||
badge.textContent = this.selectedPoints.length;
|
||||
deleteButton.appendChild(badge);
|
||||
console.log(`addSelectionCancelButton: Added badge with ${this.selectedPoints.length} points`);
|
||||
} else {
|
||||
console.warn('addSelectionCancelButton: No selected points, selectedPoints =', this.selectedPoints);
|
||||
}
|
||||
|
||||
buttonContainer.appendChild(cancelButton);
|
||||
@@ -448,15 +442,6 @@ export class VisitsManager {
|
||||
|
||||
// Insert at the beginning of the container
|
||||
container.insertBefore(buttonContainer, container.firstChild);
|
||||
console.log('addSelectionCancelButton: Buttons inserted into DOM');
|
||||
|
||||
// Verify buttons are in DOM
|
||||
setTimeout(() => {
|
||||
const verifyDelete = document.getElementById('delete-selection-button');
|
||||
const verifyCancel = document.getElementById('cancel-selection-button');
|
||||
console.log('addSelectionCancelButton: Verification - Delete button exists:', !!verifyDelete);
|
||||
console.log('addSelectionCancelButton: Verification - Cancel button exists:', !!verifyCancel);
|
||||
}, 100);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -596,12 +581,21 @@ export class VisitsManager {
|
||||
const controlsLayer = {
|
||||
Points: this.mapsController.markersLayer || L.layerGroup(),
|
||||
Routes: this.mapsController.polylinesLayer || L.layerGroup(),
|
||||
Tracks: this.mapsController.tracksLayer || L.layerGroup(),
|
||||
Heatmap: this.mapsController.heatmapLayer || L.layerGroup(),
|
||||
"Fog of War": this.mapsController.fogOverlay,
|
||||
"Scratch map": this.mapsController.scratchLayerManager?.getLayer() || L.layerGroup(),
|
||||
Areas: this.mapsController.areasLayer || L.layerGroup(),
|
||||
Photos: this.mapsController.photoMarkers || L.layerGroup()
|
||||
Photos: this.mapsController.photoMarkers || L.layerGroup(),
|
||||
"Suggested Visits": this.getVisitCirclesLayer(),
|
||||
"Confirmed Visits": this.getConfirmedVisitCirclesLayer()
|
||||
};
|
||||
|
||||
// Include Family Members layer if available
|
||||
if (window.familyMembersController?.familyMarkersLayer) {
|
||||
controlsLayer['Family Members'] = window.familyMembersController.familyMarkersLayer;
|
||||
}
|
||||
|
||||
this.mapsController.layerControl = L.control.layers(
|
||||
this.mapsController.baseMaps(),
|
||||
controlsLayer
|
||||
|
||||
@@ -161,6 +161,14 @@ test.describe('Side Panel', () => {
|
||||
test('should display visit details in panel', async ({ page }) => {
|
||||
await selectAreaWithVisits(page);
|
||||
|
||||
// Open the visits collapsible section
|
||||
const visitsSection = page.locator('#visits-section-collapse');
|
||||
await expect(visitsSection).toBeVisible();
|
||||
|
||||
const visitsSummary = visitsSection.locator('summary');
|
||||
await visitsSummary.click();
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// Check if we have any visits
|
||||
const visitCount = await page.locator('.visit-item').count();
|
||||
|
||||
@@ -185,7 +193,7 @@ test.describe('Side Panel', () => {
|
||||
await expect(timeInfo).toBeVisible();
|
||||
|
||||
// Check if this is a suggested visit (has confirm/decline buttons)
|
||||
const hasSuggestedButtons = await firstVisit.locator('.confirm-visit').count() > 0;
|
||||
const hasSuggestedButtons = (await firstVisit.locator('.confirm-visit').count()) > 0;
|
||||
|
||||
if (hasSuggestedButtons) {
|
||||
// For suggested visits, verify action buttons are present
|
||||
@@ -202,6 +210,14 @@ test.describe('Side Panel', () => {
|
||||
test('should confirm individual suggested visit from panel', async ({ page }) => {
|
||||
await selectAreaWithVisits(page);
|
||||
|
||||
// Open the visits collapsible section
|
||||
const visitsSection = page.locator('#visits-section-collapse');
|
||||
await expect(visitsSection).toBeVisible();
|
||||
|
||||
const visitsSummary = visitsSection.locator('summary');
|
||||
await visitsSummary.click();
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// Find a suggested visit (one with confirm/decline buttons)
|
||||
const suggestedVisit = page.locator('.visit-item').filter({ has: page.locator('.confirm-visit') }).first();
|
||||
|
||||
@@ -245,6 +261,14 @@ test.describe('Side Panel', () => {
|
||||
test('should decline individual suggested visit from panel', async ({ page }) => {
|
||||
await selectAreaWithVisits(page);
|
||||
|
||||
// Open the visits collapsible section
|
||||
const visitsSection = page.locator('#visits-section-collapse');
|
||||
await expect(visitsSection).toBeVisible();
|
||||
|
||||
const visitsSummary = visitsSection.locator('summary');
|
||||
await visitsSummary.click();
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// Find a suggested visit
|
||||
const suggestedVisit = page.locator('.visit-item').filter({ has: page.locator('.decline-visit') }).first();
|
||||
|
||||
@@ -280,6 +304,14 @@ test.describe('Side Panel', () => {
|
||||
test('should show checkboxes on hover for mass selection', async ({ page }) => {
|
||||
await selectAreaWithVisits(page);
|
||||
|
||||
// Open the visits collapsible section
|
||||
const visitsSection = page.locator('#visits-section-collapse');
|
||||
await expect(visitsSection).toBeVisible();
|
||||
|
||||
const visitsSummary = visitsSection.locator('summary');
|
||||
await visitsSummary.click();
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// Check if we have any visits
|
||||
const visitCount = await page.locator('.visit-item').count();
|
||||
|
||||
@@ -313,6 +345,14 @@ test.describe('Side Panel', () => {
|
||||
test('should select multiple visits and show bulk action buttons', async ({ page }) => {
|
||||
await selectAreaWithVisits(page);
|
||||
|
||||
// Open the visits collapsible section
|
||||
const visitsSection = page.locator('#visits-section-collapse');
|
||||
await expect(visitsSection).toBeVisible();
|
||||
|
||||
const visitsSummary = visitsSection.locator('summary');
|
||||
await visitsSummary.click();
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// Verify we have at least 2 visits
|
||||
const visitCount = await page.locator('.visit-item').count();
|
||||
if (visitCount < 2) {
|
||||
@@ -365,6 +405,14 @@ test.describe('Side Panel', () => {
|
||||
test('should cancel mass selection', async ({ page }) => {
|
||||
await selectAreaWithVisits(page);
|
||||
|
||||
// Open the visits collapsible section
|
||||
const visitsSection = page.locator('#visits-section-collapse');
|
||||
await expect(visitsSection).toBeVisible();
|
||||
|
||||
const visitsSummary = visitsSection.locator('summary');
|
||||
await visitsSummary.click();
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
const visitCount = await page.locator('.visit-item').count();
|
||||
if (visitCount < 2) {
|
||||
console.log('Test skipped: Need at least 2 visits');
|
||||
@@ -405,6 +453,14 @@ test.describe('Side Panel', () => {
|
||||
test('should mass confirm multiple visits', async ({ page }) => {
|
||||
await selectAreaWithVisits(page);
|
||||
|
||||
// Open the visits collapsible section
|
||||
const visitsSection = page.locator('#visits-section-collapse');
|
||||
await expect(visitsSection).toBeVisible();
|
||||
|
||||
const visitsSummary = visitsSection.locator('summary');
|
||||
await visitsSummary.click();
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// Find suggested visits (those with confirm buttons)
|
||||
const suggestedVisits = page.locator('.visit-item').filter({ has: page.locator('.confirm-visit') });
|
||||
const suggestedCount = await suggestedVisits.count();
|
||||
@@ -452,6 +508,14 @@ test.describe('Side Panel', () => {
|
||||
test('should mass decline multiple visits', async ({ page }) => {
|
||||
await selectAreaWithVisits(page);
|
||||
|
||||
// Open the visits collapsible section
|
||||
const visitsSection = page.locator('#visits-section-collapse');
|
||||
await expect(visitsSection).toBeVisible();
|
||||
|
||||
const visitsSummary = visitsSection.locator('summary');
|
||||
await visitsSummary.click();
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
const suggestedVisits = page.locator('.visit-item').filter({ has: page.locator('.decline-visit') });
|
||||
const suggestedCount = await suggestedVisits.count();
|
||||
|
||||
@@ -497,6 +561,14 @@ test.describe('Side Panel', () => {
|
||||
test('should mass merge multiple visits', async ({ page }) => {
|
||||
await selectAreaWithVisits(page);
|
||||
|
||||
// Open the visits collapsible section
|
||||
const visitsSection = page.locator('#visits-section-collapse');
|
||||
await expect(visitsSection).toBeVisible();
|
||||
|
||||
const visitsSummary = visitsSection.locator('summary');
|
||||
await visitsSummary.click();
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
const visitCount = await page.locator('.visit-item').count();
|
||||
if (visitCount < 2) {
|
||||
console.log('Test skipped: Need at least 2 visits');
|
||||
@@ -535,35 +607,38 @@ test.describe('Side Panel', () => {
|
||||
expect(finalVisitCount).toBeLessThan(visitCount);
|
||||
});
|
||||
|
||||
test('should shift controls when panel opens and shift back when closed', async ({ page }) => {
|
||||
// Get initial position of a control element (layer control)
|
||||
test('should open and close panel without shifting controls', async ({ page }) => {
|
||||
// Get the layer control element
|
||||
const layerControl = page.locator('.leaflet-control-layers');
|
||||
await expect(layerControl).toBeVisible();
|
||||
|
||||
// Check if controls have the shifted class initially (should not)
|
||||
const initiallyShifted = await layerControl.evaluate(el =>
|
||||
el.classList.contains('controls-shifted')
|
||||
);
|
||||
expect(initiallyShifted).toBe(false);
|
||||
// Get initial position of the control
|
||||
const initialBox = await layerControl.boundingBox();
|
||||
|
||||
// Open the drawer
|
||||
await clickDrawerButton(page);
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// Verify controls now have the shifted class
|
||||
const shiftedAfterOpen = await layerControl.evaluate(el =>
|
||||
el.classList.contains('controls-shifted')
|
||||
);
|
||||
expect(shiftedAfterOpen).toBe(true);
|
||||
// Verify drawer is open
|
||||
const drawerOpen = await isDrawerOpen(page);
|
||||
expect(drawerOpen).toBe(true);
|
||||
|
||||
// Get position after opening - should be the same (no shifting)
|
||||
const afterOpenBox = await layerControl.boundingBox();
|
||||
expect(afterOpenBox.x).toBe(initialBox.x);
|
||||
expect(afterOpenBox.y).toBe(initialBox.y);
|
||||
|
||||
// Close the drawer
|
||||
await clickDrawerButton(page);
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// Verify controls no longer have the shifted class
|
||||
const shiftedAfterClose = await layerControl.evaluate(el =>
|
||||
el.classList.contains('controls-shifted')
|
||||
);
|
||||
expect(shiftedAfterClose).toBe(false);
|
||||
// Verify drawer is closed
|
||||
const drawerClosed = await isDrawerOpen(page);
|
||||
expect(drawerClosed).toBe(false);
|
||||
|
||||
// Get final position - should still be the same
|
||||
const afterCloseBox = await layerControl.boundingBox();
|
||||
expect(afterCloseBox.x).toBe(initialBox.x);
|
||||
expect(afterCloseBox.y).toBe(initialBox.y);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -132,10 +132,15 @@ test.describe('Visit Interactions', () => {
|
||||
await saveButton.click();
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// Verify success message or popup closes
|
||||
const popupStillVisible = await page.locator('.leaflet-popup').isVisible().catch(() => false);
|
||||
// Either popup closes or stays open with updated content
|
||||
expect(popupStillVisible === false || popupStillVisible === true).toBe(true);
|
||||
// Verify popup closes after successful save
|
||||
const popupVisible = await page.locator('.leaflet-popup').isVisible().catch(() => false);
|
||||
expect(popupVisible).toBe(false);
|
||||
|
||||
// Verify success flash message appears
|
||||
const flashMessage = page.locator('#flash-messages [role="alert"]');
|
||||
await expect(flashMessage).toBeVisible({ timeout: 2000 });
|
||||
const messageText = await flashMessage.textContent();
|
||||
expect(messageText).toContain('Visit updated successfully');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -173,9 +178,15 @@ test.describe('Visit Interactions', () => {
|
||||
await saveButton.click();
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// Verify flash message or popup closes
|
||||
const flashOrClose = await page.locator('#flash-messages [role="alert"]').isVisible({ timeout: 2000 }).catch(() => false);
|
||||
expect(flashOrClose === true || flashOrClose === false).toBe(true);
|
||||
// Verify popup closes after successful save
|
||||
const popupVisible = await page.locator('.leaflet-popup').isVisible().catch(() => false);
|
||||
expect(popupVisible).toBe(false);
|
||||
|
||||
// Verify success flash message appears
|
||||
const flashMessage = page.locator('#flash-messages [role="alert"]');
|
||||
await expect(flashMessage).toBeVisible({ timeout: 2000 });
|
||||
const messageText = await flashMessage.textContent();
|
||||
expect(messageText).toContain('Visit updated successfully');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -27,8 +27,7 @@ RSpec.describe Families::Invite do
|
||||
end
|
||||
|
||||
it 'sends invitation email' do
|
||||
expect(FamilyMailer).to receive(:invitation).and_call_original
|
||||
expect_any_instance_of(ActionMailer::MessageDelivery).to receive(:deliver_later)
|
||||
expect(Family::Invitations::SendingJob).to receive(:perform_later).and_call_original
|
||||
service.call
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user