test: initial end to end test with playwright
@@ -0,0 +1,36 @@
|
||||
import { Page, expect } from '@playwright/test';
|
||||
|
||||
export const TEST_ADMIN = {
|
||||
username: 'admin',
|
||||
email: 'testadmin@example.com',
|
||||
password: 'TestPassword1!',
|
||||
};
|
||||
|
||||
/**
|
||||
* Log in as the test admin and wait until the main app is ready.
|
||||
*/
|
||||
export async function loginAsAdmin(page: Page) {
|
||||
await goToLoginPage(page);
|
||||
await page.locator('#login-username').fill(TEST_ADMIN.username);
|
||||
await page.locator('#login-password').fill(TEST_ADMIN.password);
|
||||
await page.locator('#login-panel button[type="submit"]').click();
|
||||
await expect(page.locator('#sidebar')).toBeVisible({ timeout: 15_000 });
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to `/` and land on the login panel, handling the language selector
|
||||
* if it appears (fresh localStorage). The admin account is guaranteed to exist
|
||||
* because globalSetup created it before any test ran.
|
||||
*/
|
||||
export async function goToLoginPage(page: Page) {
|
||||
await page.goto('/');
|
||||
|
||||
// Both panels start with .hidden — wait for JS to reveal one.
|
||||
await page.waitForSelector('#language-panel:not(.hidden), #login-panel:not(.hidden)');
|
||||
|
||||
if (await page.locator('#language-panel').isVisible()) {
|
||||
await page.locator('#language-continue').click();
|
||||
}
|
||||
|
||||
await expect(page.locator('#login-panel')).toBeVisible();
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { goToLoginPage, loginAsAdmin, TEST_ADMIN } from './helpers';
|
||||
|
||||
test('has OxiCloud title', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
await expect(page).toHaveTitle(/OxiCloud/);
|
||||
});
|
||||
|
||||
test('language selector > choose EN > reach login page', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
|
||||
await expect(page.locator('#language-panel')).toBeVisible();
|
||||
await expect(page.getByText('Select your language to continue')).toBeVisible();
|
||||
await expect(page.locator('#lang-picker-name')).toHaveText('English');
|
||||
|
||||
await expect(page.locator('#language-panel')).toHaveScreenshot('language-selector.png');
|
||||
|
||||
await page.locator('#language-continue').click();
|
||||
|
||||
await expect(page.locator('#login-panel')).toBeVisible();
|
||||
await expect(page.locator('#language-panel')).toBeHidden();
|
||||
|
||||
await expect(page.locator('#login-panel')).toHaveScreenshot('login-panel.png');
|
||||
});
|
||||
|
||||
test('login with wrong password is rejected', async ({ page }) => {
|
||||
await goToLoginPage(page);
|
||||
|
||||
await page.locator('#login-username').fill(TEST_ADMIN.username);
|
||||
await page.locator('#login-password').fill('definitely-wrong-password');
|
||||
await page.locator('#login-panel button[type="submit"]').click();
|
||||
|
||||
const loginError = page.locator('#login-error');
|
||||
await expect(loginError).toBeVisible();
|
||||
await expect(loginError).toContainText('Authentication error (403): Forbidden');
|
||||
|
||||
await expect(page.locator('#login-panel')).toBeVisible();
|
||||
await expect(page.locator('#login-panel')).toHaveScreenshot('login-panel-error.png');
|
||||
});
|
||||
|
||||
test.describe('authenticated as admin', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await loginAsAdmin(page);
|
||||
});
|
||||
|
||||
test('home page shows files list', async ({ page }) => {
|
||||
await expect(page.locator('.files-container')).toBeVisible();
|
||||
await expect(page.locator('#user-menu-wrapper')).toBeVisible();
|
||||
await expect(page).toHaveScreenshot('home-files.png', {
|
||||
// ignore this div (max value may change)
|
||||
// FIXME: ensure same max capacity from server during test
|
||||
animations: 'disabled',
|
||||
mask: [page.locator('.storage-bar'), page.locator('.storage-info') ]
|
||||
});
|
||||
});
|
||||
|
||||
test('theme can be changed to dark', async ({ page }) => {
|
||||
await page.locator('#user-avatar-btn').click();
|
||||
await expect(page.locator('#user-menu')).toBeVisible();
|
||||
await page.locator('#theme-toggle-pill').click();
|
||||
|
||||
// html element must carry data-theme="dark"
|
||||
await expect(page.locator('html')).toHaveAttribute('data-theme', 'dark');
|
||||
|
||||
// localStorage must persist the choice
|
||||
const theme = await page.evaluate(() => localStorage.getItem('oxicloud_theme'));
|
||||
expect(theme).toBe('dark');
|
||||
|
||||
await expect(page).toHaveScreenshot('home-files-darktheme.png', {
|
||||
// ignore this div (max value may change)
|
||||
// FIXME: ensure same max capacity from server during test
|
||||
animations: 'disabled',
|
||||
mask: [page.locator('.storage-bar'), page.locator('.storage-info') ]
|
||||
});
|
||||
|
||||
// Toggle back to light
|
||||
await page.locator('#theme-toggle-pill').click();
|
||||
await expect(page.locator('html')).not.toHaveAttribute('data-theme', 'dark');
|
||||
|
||||
const themeAfter = await page.evaluate(() => localStorage.getItem('oxicloud_theme'));
|
||||
expect(themeAfter).toBe('light');
|
||||
|
||||
await expect(page).toHaveScreenshot('home-files-lightheme.png', {
|
||||
// ignore this div (max value may change)
|
||||
// FIXME: ensure same max capacity from server during test
|
||||
animations: 'disabled',
|
||||
mask: [page.locator('.storage-bar'), page.locator('.storage-info') ]
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 96 KiB |
|
After Width: | Height: | Size: 111 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 94 KiB |
|
After Width: | Height: | Size: 109 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 39 KiB |