Initial commit: ROA2WEB - FastAPI + Vue.js + Telegram Bot
Modern ERP Reports Application with microservices architecture Tech Stack: - Backend: FastAPI + python-oracledb (Oracle DB integration) - Frontend: Vue.js 3 + PrimeVue + Vite - Telegram Bot: python-telegram-bot + SQLite - Infrastructure: Shared database pool, JWT authentication, SSH tunnel Features: - FastAPI backend with async Oracle connection pool - Vue.js 3 responsive frontend with PrimeVue components - Telegram bot alternative interface - Microservices architecture with shared components - Complete deployment support (Linux Docker + Windows IIS) - Comprehensive testing (Playwright E2E + pytest) Repository Structure: - reports-app/ - Main application (backend, frontend, telegram-bot) - shared/ - Shared components (database pool, auth, utils) - deployment/ - Deployment scripts (Linux & Windows) - docs/ - Project documentation - security/ - Security scanning and git hooks
This commit is contained in:
237
reports-app/frontend/tests/e2e/responsive/breakpoints.spec.js
Normal file
237
reports-app/frontend/tests/e2e/responsive/breakpoints.spec.js
Normal file
@@ -0,0 +1,237 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { LoginPage } from '../../page-objects/LoginPage.js';
|
||||
import { DashboardPage } from '../../page-objects/DashboardPage.js';
|
||||
import { testCredentials } from '../../fixtures/auth.js';
|
||||
|
||||
test.describe('Responsive Design Tests', () => {
|
||||
let loginPage;
|
||||
let dashboardPage;
|
||||
|
||||
// Common setup for all responsive tests
|
||||
const setupMockAuth = async (page) => {
|
||||
await page.route('**/api/auth/login', async route => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({
|
||||
access_token: 'mock_access_token',
|
||||
refresh_token: 'mock_refresh_token',
|
||||
user: { id: 1, username: 'testuser', full_name: 'Test User' }
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
await page.route('**/api/companies', async route => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify([
|
||||
{ code: 'COMP1', name: 'Test Company' }
|
||||
]),
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
test.describe('Mobile Layout (320px)', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.setViewportSize({ width: 320, height: 568 });
|
||||
loginPage = new LoginPage(page);
|
||||
dashboardPage = new DashboardPage(page);
|
||||
await setupMockAuth(page);
|
||||
});
|
||||
|
||||
test('should display login form correctly on mobile', async ({ page }) => {
|
||||
await loginPage.navigate();
|
||||
|
||||
// Take screenshot for visual verification
|
||||
await page.screenshot({ path: 'mobile-login.png', fullPage: true });
|
||||
|
||||
// Login form should be visible and properly sized
|
||||
await expect(page.locator(loginPage.loginCard)).toBeVisible();
|
||||
await expect(page.locator(loginPage.usernameInput)).toBeVisible();
|
||||
await expect(page.locator(loginPage.passwordInput)).toBeVisible();
|
||||
|
||||
// Check that form takes appropriate width
|
||||
const cardWidth = await page.locator(loginPage.loginCard).boundingBox();
|
||||
expect(cardWidth.width).toBeLessThan(320); // Should fit in viewport
|
||||
});
|
||||
|
||||
test('should adapt dashboard layout for mobile', async ({ page }) => {
|
||||
// Login and navigate to dashboard
|
||||
await loginPage.navigate();
|
||||
await loginPage.login(testCredentials.valid.username, testCredentials.valid.password);
|
||||
await page.waitForURL('/dashboard');
|
||||
|
||||
// Take screenshot
|
||||
await page.screenshot({ path: 'mobile-dashboard.png', fullPage: true });
|
||||
|
||||
// Dashboard should be responsive
|
||||
await expect(page.locator(dashboardPage.pageTitle)).toBeVisible();
|
||||
|
||||
// Stats grid should stack vertically on mobile
|
||||
const statsGrid = page.locator(dashboardPage.statsGrid);
|
||||
if (await statsGrid.isVisible()) {
|
||||
const gridBox = await statsGrid.boundingBox();
|
||||
expect(gridBox.width).toBeLessThan(320);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Tablet Layout (768px)', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.setViewportSize({ width: 768, height: 1024 });
|
||||
loginPage = new LoginPage(page);
|
||||
dashboardPage = new DashboardPage(page);
|
||||
await setupMockAuth(page);
|
||||
});
|
||||
|
||||
test('should display login form appropriately on tablet', async ({ page }) => {
|
||||
await loginPage.navigate();
|
||||
|
||||
await page.screenshot({ path: 'tablet-login.png', fullPage: true });
|
||||
|
||||
// Login card should be centered and well-proportioned
|
||||
const loginCard = page.locator(loginPage.loginCard);
|
||||
await expect(loginCard).toBeVisible();
|
||||
|
||||
const cardBox = await loginCard.boundingBox();
|
||||
expect(cardBox.width).toBeGreaterThan(300);
|
||||
expect(cardBox.width).toBeLessThan(500);
|
||||
});
|
||||
|
||||
test('should show proper tablet dashboard layout', async ({ page }) => {
|
||||
await loginPage.navigate();
|
||||
await loginPage.login(testCredentials.valid.username, testCredentials.valid.password);
|
||||
await page.waitForURL('/dashboard');
|
||||
|
||||
await page.screenshot({ path: 'tablet-dashboard.png', fullPage: true });
|
||||
|
||||
// Dashboard elements should be properly spaced
|
||||
await expect(page.locator(dashboardPage.pageTitle)).toBeVisible();
|
||||
|
||||
// Stats should be arranged in appropriate grid
|
||||
const statsCards = page.locator('.stat-card');
|
||||
const cardCount = await statsCards.count();
|
||||
if (cardCount > 0) {
|
||||
// Cards should be visible and properly sized
|
||||
for (let i = 0; i < cardCount; i++) {
|
||||
await expect(statsCards.nth(i)).toBeVisible();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Desktop Layout (1024px+)', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.setViewportSize({ width: 1024, height: 768 });
|
||||
loginPage = new LoginPage(page);
|
||||
dashboardPage = new DashboardPage(page);
|
||||
await setupMockAuth(page);
|
||||
});
|
||||
|
||||
test('should display full desktop login layout', async ({ page }) => {
|
||||
await loginPage.navigate();
|
||||
|
||||
await page.screenshot({ path: 'desktop-login.png', fullPage: true });
|
||||
|
||||
// Login should be centered with appropriate sizing
|
||||
const loginCard = page.locator(loginPage.loginCard);
|
||||
await expect(loginCard).toBeVisible();
|
||||
|
||||
// Card should not take full width on desktop
|
||||
const cardBox = await loginCard.boundingBox();
|
||||
expect(cardBox.width).toBeLessThan(500);
|
||||
});
|
||||
|
||||
test('should show complete desktop dashboard layout', async ({ page }) => {
|
||||
await loginPage.navigate();
|
||||
await loginPage.login(testCredentials.valid.username, testCredentials.valid.password);
|
||||
await page.waitForURL('/dashboard');
|
||||
|
||||
await page.screenshot({ path: 'desktop-dashboard.png', fullPage: true });
|
||||
|
||||
// All dashboard elements should be visible
|
||||
await expect(page.locator(dashboardPage.pageTitle)).toBeVisible();
|
||||
await expect(page.locator(dashboardPage.pageSubtitle)).toBeVisible();
|
||||
|
||||
// Stats grid should use horizontal layout
|
||||
const statsGrid = page.locator(dashboardPage.statsGrid);
|
||||
if (await statsGrid.isVisible()) {
|
||||
const gridBox = await statsGrid.boundingBox();
|
||||
expect(gridBox.width).toBeGreaterThan(600);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Wide Screen Layout (1920px)', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.setViewportSize({ width: 1920, height: 1080 });
|
||||
loginPage = new LoginPage(page);
|
||||
dashboardPage = new DashboardPage(page);
|
||||
await setupMockAuth(page);
|
||||
});
|
||||
|
||||
test('should handle wide screen layouts appropriately', async ({ page }) => {
|
||||
await loginPage.navigate();
|
||||
await loginPage.login(testCredentials.valid.username, testCredentials.valid.password);
|
||||
await page.waitForURL('/dashboard');
|
||||
|
||||
await page.screenshot({ path: 'widescreen-dashboard.png', fullPage: true });
|
||||
|
||||
// Content should not stretch too wide
|
||||
const mainContent = page.locator('.dashboard-content');
|
||||
if (await mainContent.isVisible()) {
|
||||
const contentBox = await mainContent.boundingBox();
|
||||
// Content should have reasonable max-width
|
||||
expect(contentBox.width).toBeLessThan(1600);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Orientation Changes', () => {
|
||||
test('should handle portrait to landscape orientation', async ({ page }) => {
|
||||
// Start in mobile portrait
|
||||
await page.setViewportSize({ width: 375, height: 667 });
|
||||
loginPage = new LoginPage(page);
|
||||
await setupMockAuth(page);
|
||||
|
||||
await loginPage.navigate();
|
||||
await page.screenshot({ path: 'mobile-portrait.png' });
|
||||
|
||||
// Rotate to landscape
|
||||
await page.setViewportSize({ width: 667, height: 375 });
|
||||
await page.waitForTimeout(500); // Allow for reflow
|
||||
|
||||
await page.screenshot({ path: 'mobile-landscape.png' });
|
||||
|
||||
// Login form should still be usable
|
||||
await expect(page.locator(loginPage.loginCard)).toBeVisible();
|
||||
await expect(page.locator(loginPage.usernameInput)).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Touch Interactions', () => {
|
||||
test('should handle touch interactions on mobile', async ({ page }) => {
|
||||
await page.setViewportSize({ width: 375, height: 667 });
|
||||
loginPage = new LoginPage(page);
|
||||
await setupMockAuth(page);
|
||||
|
||||
await loginPage.navigate();
|
||||
|
||||
// Test touch interactions
|
||||
await page.tap(loginPage.usernameInput);
|
||||
await page.fill(loginPage.usernameInput, 'testuser');
|
||||
|
||||
await page.tap(loginPage.passwordInput);
|
||||
await page.fill(loginPage.passwordInput, 'testpass');
|
||||
|
||||
// Login button should be tappable
|
||||
const loginButton = page.locator(loginPage.loginButton);
|
||||
await expect(loginButton).toBeEnabled();
|
||||
|
||||
// Button should have appropriate touch target size (minimum 44px)
|
||||
const buttonBox = await loginButton.boundingBox();
|
||||
expect(buttonBox.height).toBeGreaterThanOrEqual(44);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user