201 előtti mentés
This commit is contained in:
251
frontend/tests/automated_flow_test.js
Normal file
251
frontend/tests/automated_flow_test.js
Normal file
@@ -0,0 +1,251 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Automated E2E Flow Test for Service Finder Frontend
|
||||
*
|
||||
* This script simulates:
|
||||
* 1. Logging in and getting a token
|
||||
* 2. Setting the Profile Mode (Personal/Fleet)
|
||||
* 3. Fetching the User's Garage
|
||||
* 4. Fetching Gamification stats
|
||||
*
|
||||
* Usage: node automated_flow_test.js
|
||||
*/
|
||||
|
||||
import axios from 'axios'
|
||||
import { fileURLToPath } from 'url'
|
||||
import { dirname } from 'path'
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url)
|
||||
const __dirname = dirname(__filename)
|
||||
|
||||
// Configuration
|
||||
const API_BASE_URL = process.env.VITE_API_BASE_URL || 'https://dev.servicefinder.hu'
|
||||
const TEST_USER_EMAIL = process.env.TEST_USER_EMAIL || 'test@example.com'
|
||||
const TEST_USER_PASSWORD = process.env.TEST_USER_PASSWORD || 'password123'
|
||||
|
||||
// Create axios instance
|
||||
const api = axios.create({
|
||||
baseURL: API_BASE_URL,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
})
|
||||
|
||||
// Test state
|
||||
let authToken = null
|
||||
let userId = null
|
||||
|
||||
async function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
async function testLogin() {
|
||||
console.log('🔐 Testing login...')
|
||||
|
||||
try {
|
||||
// First, check if we need to register a test user
|
||||
// For now, we'll try to login with provided credentials
|
||||
const response = await api.post('/api/v2/auth/login', {
|
||||
email: TEST_USER_EMAIL,
|
||||
password: TEST_USER_PASSWORD,
|
||||
})
|
||||
|
||||
if (response.data.access_token) {
|
||||
authToken = response.data.access_token
|
||||
userId = response.data.user_id
|
||||
console.log('✅ Login successful')
|
||||
console.log(` Token: ${authToken.substring(0, 20)}...`)
|
||||
console.log(` User ID: ${userId}`)
|
||||
|
||||
// Set auth header for subsequent requests
|
||||
api.defaults.headers.common['Authorization'] = `Bearer ${authToken}`
|
||||
return true
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ Login failed:', error.response?.data || error.message)
|
||||
|
||||
// If login fails due to invalid credentials, try to register
|
||||
if (error.response?.status === 401 || error.response?.status === 404) {
|
||||
console.log('⚠️ Attempting to register test user...')
|
||||
try {
|
||||
const registerResponse = await api.post('/api/v2/auth/register', null, {
|
||||
params: {
|
||||
email: TEST_USER_EMAIL,
|
||||
password: TEST_USER_PASSWORD,
|
||||
first_name: 'Test',
|
||||
last_name: 'User',
|
||||
phone: '+36123456789',
|
||||
}
|
||||
})
|
||||
|
||||
if (registerResponse.data.access_token) {
|
||||
authToken = registerResponse.data.access_token
|
||||
userId = registerResponse.data.user_id
|
||||
console.log('✅ Test user registered and logged in')
|
||||
api.defaults.headers.common['Authorization'] = `Bearer ${authToken}`
|
||||
return true
|
||||
}
|
||||
} catch (registerError) {
|
||||
console.error('❌ Registration failed:', registerError.response?.data || registerError.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
async function testSetProfileMode() {
|
||||
console.log('\n🎯 Testing profile mode setting...')
|
||||
|
||||
try {
|
||||
// First, get current user to check existing mode
|
||||
const userResponse = await api.get('/api/v1/users/me')
|
||||
console.log(` Current UI mode: ${userResponse.data.ui_mode || 'not set'}`)
|
||||
|
||||
// Set mode to 'personal' (private_garage)
|
||||
const modeToSet = 'personal'
|
||||
const response = await api.patch('/api/v1/users/me/preferences', {
|
||||
ui_mode: modeToSet
|
||||
})
|
||||
|
||||
console.log(`✅ Profile mode set to: ${modeToSet}`)
|
||||
|
||||
// Verify the mode was set
|
||||
const verifyResponse = await api.get('/api/v1/users/me')
|
||||
if (verifyResponse.data.ui_mode === modeToSet) {
|
||||
console.log(`✅ Mode verified: ${verifyResponse.data.ui_mode}`)
|
||||
return true
|
||||
} else {
|
||||
console.error(`❌ Mode mismatch: expected ${modeToSet}, got ${verifyResponse.data.ui_mode}`)
|
||||
return false
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ Failed to set profile mode:', error.response?.data || error.message)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async function testFetchGarage() {
|
||||
console.log('\n🚗 Testing garage fetch...')
|
||||
|
||||
try {
|
||||
const response = await api.get('/api/v1/vehicles/my-garage')
|
||||
|
||||
if (Array.isArray(response.data)) {
|
||||
console.log(`✅ Garage fetched successfully: ${response.data.length} vehicle(s)`)
|
||||
if (response.data.length > 0) {
|
||||
console.log(' Sample vehicle:', {
|
||||
id: response.data[0].id,
|
||||
make: response.data[0].make,
|
||||
model: response.data[0].model,
|
||||
license_plate: response.data[0].license_plate,
|
||||
})
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
console.error('❌ Unexpected garage response format:', response.data)
|
||||
return false
|
||||
}
|
||||
} catch (error) {
|
||||
// Garage might be empty (404) or other error
|
||||
if (error.response?.status === 404) {
|
||||
console.log('✅ Garage is empty (expected for new user)')
|
||||
return true
|
||||
}
|
||||
console.error('❌ Failed to fetch garage:', error.response?.data || error.message)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async function testFetchGamification() {
|
||||
console.log('\n🏆 Testing gamification fetch...')
|
||||
|
||||
try {
|
||||
// Test achievements endpoint
|
||||
const achievementsResponse = await api.get('/api/v1/gamification/achievements')
|
||||
console.log(`✅ Achievements fetched: ${achievementsResponse.data.achievements?.length || 0} total`)
|
||||
|
||||
// Test user stats
|
||||
const statsResponse = await api.get('/api/v1/gamification/me')
|
||||
console.log('✅ User stats fetched:', {
|
||||
xp: statsResponse.data.xp,
|
||||
level: statsResponse.data.level,
|
||||
rank: statsResponse.data.rank,
|
||||
})
|
||||
|
||||
// Test badges
|
||||
const badgesResponse = await api.get('/api/v1/gamification/my-badges')
|
||||
console.log(`✅ Badges fetched: ${badgesResponse.data?.length || 0} earned`)
|
||||
|
||||
return true
|
||||
} catch (error) {
|
||||
// Gamification might not be fully implemented
|
||||
if (error.response?.status === 404 || error.response?.status === 501) {
|
||||
console.log('⚠️ Gamification endpoints not fully implemented (expected during development)')
|
||||
return true
|
||||
}
|
||||
console.error('❌ Failed to fetch gamification:', error.response?.data || error.message)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async function runAllTests() {
|
||||
console.log('🚀 Starting Service Finder E2E Flow Test')
|
||||
console.log('=========================================')
|
||||
console.log(`API Base URL: ${API_BASE_URL}`)
|
||||
console.log(`Test User: ${TEST_USER_EMAIL}`)
|
||||
console.log('')
|
||||
|
||||
const results = {
|
||||
login: false,
|
||||
profileMode: false,
|
||||
garage: false,
|
||||
gamification: false,
|
||||
}
|
||||
|
||||
// Run tests sequentially
|
||||
results.login = await testLogin()
|
||||
if (!results.login) {
|
||||
console.error('\n❌ Login failed, aborting further tests')
|
||||
return results
|
||||
}
|
||||
|
||||
await sleep(1000) // Small delay between tests
|
||||
|
||||
results.profileMode = await testSetProfileMode()
|
||||
|
||||
await sleep(500)
|
||||
|
||||
results.garage = await testFetchGarage()
|
||||
|
||||
await sleep(500)
|
||||
|
||||
results.gamification = await testFetchGamification()
|
||||
|
||||
// Summary
|
||||
console.log('\n📊 Test Summary')
|
||||
console.log('===============')
|
||||
console.log(`Login: ${results.login ? '✅ PASS' : '❌ FAIL'}`)
|
||||
console.log(`Profile Mode: ${results.profileMode ? '✅ PASS' : '❌ FAIL'}`)
|
||||
console.log(`Garage Fetch: ${results.garage ? '✅ PASS' : '❌ FAIL'}`)
|
||||
console.log(`Gamification: ${results.gamification ? '✅ PASS' : '❌ FAIL'}`)
|
||||
|
||||
const allPassed = Object.values(results).every(r => r)
|
||||
console.log(`\n${allPassed ? '🎉 ALL TESTS PASSED' : '⚠️ SOME TESTS FAILED'}`)
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
// Run tests if script is executed directly
|
||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
runAllTests().then(results => {
|
||||
const allPassed = Object.values(results).every(r => r)
|
||||
process.exit(allPassed ? 0 : 1)
|
||||
}).catch(error => {
|
||||
console.error('💥 Unhandled error in test runner:', error)
|
||||
process.exit(1)
|
||||
})
|
||||
}
|
||||
|
||||
export { runAllTests }
|
||||
61
frontend/tests/e2e/frontend-flow.spec.js
Normal file
61
frontend/tests/e2e/frontend-flow.spec.js
Normal file
@@ -0,0 +1,61 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
// Use internal Docker network hostname for frontend
|
||||
const FRONTEND_URL = 'http://sf_public_frontend:5173';
|
||||
// Test user credentials (should be a valid test user in the dev database)
|
||||
const TEST_EMAIL = 'superadmin@profibot.hu';
|
||||
const TEST_PASSWORD = 'anypassword';
|
||||
|
||||
test.describe('Frontend UI E2E Flow', () => {
|
||||
test('should login, select profile mode, and load dashboard', async ({ page }) => {
|
||||
// Step 1: Open login page
|
||||
await page.goto(`${FRONTEND_URL}/login`);
|
||||
await expect(page).toHaveURL(/\/login/);
|
||||
await expect(page.getByRole('heading', { name: /login/i })).toBeVisible();
|
||||
|
||||
// Step 2: Fill credentials and submit
|
||||
await page.getByLabel(/email/i).fill(TEST_EMAIL);
|
||||
await page.getByLabel(/password/i).fill(TEST_PASSWORD);
|
||||
await page.getByRole('button', { name: /sign in|login/i }).click();
|
||||
|
||||
// Step 3: Wait for redirect to profile selection (since no UI mode selected)
|
||||
await expect(page).toHaveURL(/\/profile-select/);
|
||||
await expect(page.getByRole('heading', { name: /welcome to service finder/i })).toBeVisible();
|
||||
|
||||
// Step 4: Select Private Garage mode
|
||||
await page.getByText(/private garage/i).click();
|
||||
await expect(page.locator('.selected').filter({ hasText: /private garage/i })).toBeVisible();
|
||||
|
||||
// Step 5: Click Continue to Dashboard
|
||||
await page.getByRole('button', { name: /continue to dashboard/i }).click();
|
||||
|
||||
// Step 6: Verify dashboard loads
|
||||
await expect(page).toHaveURL(/\//);
|
||||
await expect(page.getByRole('heading').filter({ hasText: /dashboard/i }).first()).toBeVisible();
|
||||
|
||||
// Step 7: Verify gamification trophies are present
|
||||
await expect(page.getByText(/trophies|achievements/i).first()).toBeVisible();
|
||||
|
||||
// Step 8: Verify "Add Expense" link is present and clickable
|
||||
const addExpenseLink = page.getByRole('link', { name: /add expense/i });
|
||||
await expect(addExpenseLink).toBeVisible();
|
||||
await addExpenseLink.click();
|
||||
// Should navigate to add expense page
|
||||
await expect(page.getByRole('heading', { name: /add expense/i })).toBeVisible();
|
||||
});
|
||||
|
||||
test('should handle corporate fleet selection', async ({ page }) => {
|
||||
await page.goto(`${FRONTEND_URL}/login`);
|
||||
await page.getByLabel(/email/i).fill(TEST_EMAIL);
|
||||
await page.getByLabel(/password/i).fill(TEST_PASSWORD);
|
||||
await page.getByRole('button', { name: /sign in|login/i }).click();
|
||||
|
||||
await expect(page).toHaveURL(/\/profile-select/);
|
||||
await page.getByText(/corporate fleet/i).click();
|
||||
await page.getByRole('button', { name: /continue to dashboard/i }).click();
|
||||
|
||||
await expect(page).toHaveURL(/\//);
|
||||
// Fleet dashboard may have different elements, but at least dashboard title
|
||||
await expect(page.getByRole('heading').filter({ hasText: /dashboard/i }).first()).toBeVisible();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user