feat: rebrand Hemmelig to paste.es for cloudhost.es
- Set Spanish as default language with ephemeral/encrypted privacy focus - Translate all user-facing strings and legal pages to Spanish - Replace Norwegian flag with Spanish flag in footer - Remove Hemmelig/terces.cloud links, add cloudhost.es sponsorship - Rewrite PrivacyPage: zero data collection, ephemeral design emphasis - Rewrite TermsPage: Spanish law, RGPD, paste.es/CloudHost.es references - Update PWA manifest, HTML meta tags, package.json branding - Rename webhook headers to X-Paste-Event / X-Paste-Signature - Update API docs title and contact to paste.es / cloudhost.es Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
83
tests/e2e/auth.spec.ts
Normal file
83
tests/e2e/auth.spec.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
|
||||
const TEST_USER = {
|
||||
email: 'e2etest@hemmelig.local',
|
||||
username: 'e2etestuser',
|
||||
password: 'TestPassword123!',
|
||||
name: 'E2E Test User',
|
||||
};
|
||||
|
||||
test.describe('Authentication', () => {
|
||||
test('should complete initial setup if needed', async ({ page, request }) => {
|
||||
// Check if setup is needed
|
||||
const statusResponse = await request.get('/api/setup/status');
|
||||
const statusData = await statusResponse.json();
|
||||
|
||||
if (statusData.needsSetup) {
|
||||
await page.goto('/setup');
|
||||
|
||||
// Fill in the setup form
|
||||
await page.getByPlaceholder(/email/i).fill(TEST_USER.email);
|
||||
await page.getByPlaceholder(/username/i).fill(TEST_USER.username);
|
||||
await page.getByPlaceholder(/name/i).first().fill(TEST_USER.name);
|
||||
await page.getByPlaceholder(/create.*password/i).fill(TEST_USER.password);
|
||||
|
||||
// Submit setup
|
||||
await page.getByRole('button', { name: /create|setup|submit/i }).click();
|
||||
|
||||
// Should redirect to home or login
|
||||
await expect(page).toHaveURL(/^\/$|\/login/, { timeout: 10000 });
|
||||
} else {
|
||||
// Setup already done, just verify we can access the app
|
||||
await page.goto('/');
|
||||
// Either we see the home page or login page
|
||||
const url = page.url();
|
||||
expect(url.includes('/') || url.includes('/login')).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
test('should allow user registration when enabled', async ({ page, request }) => {
|
||||
// Check if registration is allowed
|
||||
const settingsResponse = await request.get('/api/instance/settings/public');
|
||||
const settings = await settingsResponse.json();
|
||||
|
||||
if (!settings.allowRegistration) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
await page.goto('/register');
|
||||
|
||||
// Check that registration form is visible
|
||||
await expect(page.getByPlaceholder(/email/i)).toBeVisible();
|
||||
await expect(page.getByPlaceholder(/username/i)).toBeVisible();
|
||||
await expect(page.getByPlaceholder(/create.*password/i)).toBeVisible();
|
||||
});
|
||||
|
||||
test('should show login page', async ({ page }) => {
|
||||
await page.goto('/login');
|
||||
|
||||
// Check that login form is visible
|
||||
await expect(page.getByPlaceholder(/username/i)).toBeVisible();
|
||||
await expect(page.getByPlaceholder(/password/i)).toBeVisible();
|
||||
await expect(page.getByRole('button', { name: /sign in/i })).toBeVisible();
|
||||
});
|
||||
|
||||
test('should show error for invalid credentials', async ({ page }) => {
|
||||
await page.goto('/login');
|
||||
|
||||
await page.getByPlaceholder(/username/i).fill('nonexistent@test.com');
|
||||
await page.getByPlaceholder(/password/i).fill('wrongpassword');
|
||||
await page.getByRole('button', { name: /sign in/i }).click();
|
||||
|
||||
// Should stay on login page or show error
|
||||
await page.waitForTimeout(2000);
|
||||
const isStillOnLogin = page.url().includes('/login');
|
||||
const hasError = await page
|
||||
.getByText(/invalid|error|incorrect|failed/i)
|
||||
.isVisible()
|
||||
.catch(() => false);
|
||||
|
||||
expect(isStillOnLogin || hasError).toBe(true);
|
||||
});
|
||||
});
|
||||
36
tests/e2e/fixtures.ts
Normal file
36
tests/e2e/fixtures.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { test as base, expect, Page } from '@playwright/test';
|
||||
import { TEST_USER } from './global-setup';
|
||||
|
||||
async function loginUser(page: Page, email: string, password: string): Promise<boolean> {
|
||||
await page.goto('/login');
|
||||
|
||||
// Fill login form
|
||||
await page.getByPlaceholder(/username/i).fill(email);
|
||||
await page.getByPlaceholder(/password/i).fill(password);
|
||||
await page.getByRole('button', { name: /sign in/i }).click();
|
||||
|
||||
// Wait for navigation - could be home, dashboard, or 2FA
|
||||
try {
|
||||
await page.waitForURL(/^\/$|\/dashboard|\/verify-2fa/, { timeout: 10000 });
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Extend the base test with auth fixture
|
||||
export const test = base.extend<{ authenticatedPage: Page }>({
|
||||
authenticatedPage: async ({ page }, use) => {
|
||||
// Check if authentication is required
|
||||
await page.goto('/');
|
||||
|
||||
if (page.url().includes('/login')) {
|
||||
// Login with test user (created in global setup)
|
||||
await loginUser(page, TEST_USER.email, TEST_USER.password);
|
||||
}
|
||||
|
||||
await use(page);
|
||||
},
|
||||
});
|
||||
|
||||
export { expect, TEST_USER };
|
||||
71
tests/e2e/global-setup.ts
Normal file
71
tests/e2e/global-setup.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { FullConfig } from '@playwright/test';
|
||||
import { execSync } from 'child_process';
|
||||
import { existsSync, unlinkSync } from 'fs';
|
||||
|
||||
const TEST_DB_PATH = './database/hemmelig-test.db';
|
||||
|
||||
export const TEST_USER = {
|
||||
email: 'e2e-test@hemmelig.local',
|
||||
username: 'e2etestuser',
|
||||
password: 'E2ETestPassword123!',
|
||||
name: 'E2E Test User',
|
||||
};
|
||||
|
||||
async function globalSetup(config: FullConfig) {
|
||||
const baseURL = config.projects[0].use.baseURL || 'http://localhost:5173';
|
||||
|
||||
// Delete existing test database to start fresh
|
||||
if (existsSync(TEST_DB_PATH)) {
|
||||
unlinkSync(TEST_DB_PATH);
|
||||
console.log('Deleted existing test database');
|
||||
}
|
||||
|
||||
// Run migrations on test database
|
||||
console.log('Running migrations on test database...');
|
||||
execSync('npx prisma migrate deploy', {
|
||||
env: {
|
||||
...process.env,
|
||||
DATABASE_URL: `file:${TEST_DB_PATH}`,
|
||||
},
|
||||
stdio: 'inherit',
|
||||
});
|
||||
|
||||
// Wait for server to be ready
|
||||
let attempts = 0;
|
||||
while (attempts < 30) {
|
||||
try {
|
||||
const response = await fetch(`${baseURL}/api/setup/status`);
|
||||
if (response.ok) break;
|
||||
} catch {
|
||||
// Server not ready yet
|
||||
}
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
attempts++;
|
||||
}
|
||||
|
||||
// Check if setup is needed (should always be true with fresh DB)
|
||||
const statusResponse = await fetch(`${baseURL}/api/setup/status`);
|
||||
const statusData = await statusResponse.json();
|
||||
|
||||
if (statusData.needsSetup) {
|
||||
console.log('Creating test user...');
|
||||
const setupResponse = await fetch(`${baseURL}/api/setup/complete`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
email: TEST_USER.email,
|
||||
password: TEST_USER.password,
|
||||
username: TEST_USER.username,
|
||||
name: TEST_USER.name,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!setupResponse.ok) {
|
||||
console.error('Failed to complete setup:', await setupResponse.text());
|
||||
} else {
|
||||
console.log('Test user created successfully');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default globalSetup;
|
||||
19
tests/e2e/global-teardown.ts
Normal file
19
tests/e2e/global-teardown.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { existsSync, unlinkSync } from 'fs';
|
||||
|
||||
const TEST_DB_PATH = './database/hemmelig-test.db';
|
||||
|
||||
async function globalTeardown() {
|
||||
// Delete test database after tests complete
|
||||
if (existsSync(TEST_DB_PATH)) {
|
||||
unlinkSync(TEST_DB_PATH);
|
||||
console.log('Cleaned up test database');
|
||||
}
|
||||
|
||||
// Also clean up journal file if it exists
|
||||
const journalPath = `${TEST_DB_PATH}-journal`;
|
||||
if (existsSync(journalPath)) {
|
||||
unlinkSync(journalPath);
|
||||
}
|
||||
}
|
||||
|
||||
export default globalTeardown;
|
||||
37
tests/e2e/home.spec.ts
Normal file
37
tests/e2e/home.spec.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { expect, test } from './fixtures';
|
||||
|
||||
test.describe('Home Page', () => {
|
||||
test('should display the secret creation form', async ({ authenticatedPage }) => {
|
||||
await authenticatedPage.goto('/');
|
||||
|
||||
// Check that the editor is present
|
||||
await expect(authenticatedPage.locator('.ProseMirror')).toBeVisible();
|
||||
|
||||
// Check that the create button exists (there are two, use first())
|
||||
await expect(
|
||||
authenticatedPage.getByRole('button', { name: /create/i }).first()
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test('should have working dark/light mode toggle', async ({ authenticatedPage }) => {
|
||||
await authenticatedPage.goto('/');
|
||||
|
||||
// Check initial theme (could be light or dark based on system preference)
|
||||
const html = authenticatedPage.locator('html');
|
||||
|
||||
// Find and click theme toggle button
|
||||
const themeToggle = authenticatedPage
|
||||
.locator(
|
||||
'button[aria-label*="theme"], button:has([class*="Moon"]), button:has([class*="Sun"])'
|
||||
)
|
||||
.first();
|
||||
|
||||
if (await themeToggle.isVisible()) {
|
||||
const initialClass = await html.getAttribute('class');
|
||||
await themeToggle.click();
|
||||
|
||||
// Theme should have changed
|
||||
await expect(html).not.toHaveClass(initialClass || '');
|
||||
}
|
||||
});
|
||||
});
|
||||
33
tests/e2e/navigation.spec.ts
Normal file
33
tests/e2e/navigation.spec.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { expect, test } from './fixtures';
|
||||
|
||||
test.describe('Navigation', () => {
|
||||
test('should navigate to home page', async ({ authenticatedPage: page }) => {
|
||||
await page.goto('/');
|
||||
|
||||
// Check that we're on the home page with the secret form
|
||||
await expect(page.locator('.ProseMirror')).toBeVisible();
|
||||
});
|
||||
|
||||
test('should have working logo link', async ({ authenticatedPage: page }) => {
|
||||
await page.goto('/');
|
||||
|
||||
// Check for logo/brand link that goes to home
|
||||
const logoLink = page.locator('a[href="/"]').first();
|
||||
await expect(logoLink).toBeVisible();
|
||||
await logoLink.click();
|
||||
await expect(page).toHaveURL('/');
|
||||
});
|
||||
|
||||
test('should handle 404 pages gracefully', async ({ authenticatedPage: page }) => {
|
||||
await page.goto('/nonexistent-page-12345');
|
||||
|
||||
// Should show 404 page or similar error
|
||||
const pageContent = await page.content();
|
||||
const is404 =
|
||||
pageContent.toLowerCase().includes('not found') ||
|
||||
pageContent.toLowerCase().includes('404') ||
|
||||
page.url().includes('/nonexistent-page');
|
||||
|
||||
expect(is404).toBe(true);
|
||||
});
|
||||
});
|
||||
124
tests/e2e/password-change.spec.ts
Normal file
124
tests/e2e/password-change.spec.ts
Normal file
@@ -0,0 +1,124 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
import { TEST_USER } from './global-setup';
|
||||
|
||||
const WEAK_PASSWORD_USER = {
|
||||
email: 'weakpass@hemmelig.local',
|
||||
username: 'weakpassuser',
|
||||
password: 'pass',
|
||||
name: 'Weak Password User',
|
||||
};
|
||||
|
||||
const NEW_STRONG_PASSWORD = 'NewStrongPass123!';
|
||||
|
||||
/**
|
||||
* Helper: sign in via the better-auth API and return session cookies.
|
||||
*/
|
||||
async function signInViaAPI(
|
||||
baseURL: string,
|
||||
email: string,
|
||||
password: string
|
||||
): Promise<{ cookie: string }> {
|
||||
const res = await fetch(`${baseURL}/api/auth/sign-in/email`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Origin: baseURL,
|
||||
},
|
||||
body: JSON.stringify({ email, password }),
|
||||
redirect: 'manual',
|
||||
});
|
||||
|
||||
const setCookieHeaders = res.headers.getSetCookie();
|
||||
const cookie = setCookieHeaders
|
||||
.map((c) => c.split(';')[0])
|
||||
.filter(Boolean)
|
||||
.join('; ');
|
||||
|
||||
return { cookie };
|
||||
}
|
||||
|
||||
test.describe('Password Change', () => {
|
||||
test.beforeAll(async ({}, testInfo) => {
|
||||
const baseURL = testInfo.project.use.baseURL || 'http://localhost:5173';
|
||||
|
||||
// Sign in as admin to get session cookies
|
||||
const { cookie: adminCookie } = await signInViaAPI(
|
||||
baseURL,
|
||||
TEST_USER.email,
|
||||
TEST_USER.password
|
||||
);
|
||||
|
||||
// Use better-auth admin endpoint to create a user with a weak password.
|
||||
// This bypasses the sign-up hook (which only runs on /sign-up/email),
|
||||
// and since minPasswordLength is 1, better-auth accepts it.
|
||||
const createRes = await fetch(`${baseURL}/api/auth/admin/create-user`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Origin: baseURL,
|
||||
Cookie: adminCookie,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email: WEAK_PASSWORD_USER.email,
|
||||
password: WEAK_PASSWORD_USER.password,
|
||||
name: WEAK_PASSWORD_USER.name,
|
||||
role: 'user',
|
||||
data: {
|
||||
username: WEAK_PASSWORD_USER.username,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
if (!createRes.ok) {
|
||||
const body = await createRes.text();
|
||||
// Ignore if user already exists (e.g. from a previous test run)
|
||||
if (!body.includes('already') && !body.includes('exists')) {
|
||||
throw new Error(`Failed to create weak-password user: ${createRes.status} ${body}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('should allow a user with a weak password to change their password and log in with the new one', async ({
|
||||
page,
|
||||
}) => {
|
||||
// Log in as the weak-password user
|
||||
await page.goto('/login');
|
||||
await page.getByPlaceholder(/username/i).fill(WEAK_PASSWORD_USER.username);
|
||||
await page.getByPlaceholder(/password/i).fill(WEAK_PASSWORD_USER.password);
|
||||
await page.getByRole('button', { name: /sign in/i }).click();
|
||||
|
||||
// Wait for login to complete
|
||||
await page.waitForURL(/^\/$|\/dashboard/, { timeout: 10000 });
|
||||
|
||||
// Navigate to account settings
|
||||
await page.goto('/dashboard/account');
|
||||
|
||||
// Click on the Security tab
|
||||
await page.getByRole('button', { name: /security/i }).click();
|
||||
|
||||
// Fill in the password change form
|
||||
await page.getByPlaceholder(/enter current password/i).fill(WEAK_PASSWORD_USER.password);
|
||||
await page.getByPlaceholder(/enter new password/i).fill(NEW_STRONG_PASSWORD);
|
||||
await page.getByPlaceholder(/confirm new password/i).fill(NEW_STRONG_PASSWORD);
|
||||
|
||||
// Submit the password change
|
||||
await page.getByRole('button', { name: /change password/i }).click();
|
||||
|
||||
// Verify success message appears
|
||||
await expect(page.getByText(/password changed successfully/i)).toBeVisible({
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
// Sign out by clearing cookies and navigating to login
|
||||
await page.context().clearCookies();
|
||||
await page.goto('/login');
|
||||
|
||||
// Log in with the new password
|
||||
await page.getByPlaceholder(/username/i).fill(WEAK_PASSWORD_USER.username);
|
||||
await page.getByPlaceholder(/password/i).fill(NEW_STRONG_PASSWORD);
|
||||
await page.getByRole('button', { name: /sign in/i }).click();
|
||||
|
||||
// Verify login succeeds
|
||||
await page.waitForURL(/^\/$|\/dashboard/, { timeout: 10000 });
|
||||
});
|
||||
});
|
||||
189
tests/e2e/secret.spec.ts
Normal file
189
tests/e2e/secret.spec.ts
Normal file
@@ -0,0 +1,189 @@
|
||||
import { expect, test } from './fixtures';
|
||||
|
||||
test.describe('Secret Creation and Viewing', () => {
|
||||
test('should create a secret and display the secret URL', async ({
|
||||
authenticatedPage: page,
|
||||
}) => {
|
||||
await page.goto('/');
|
||||
|
||||
// Type secret content in the editor
|
||||
const editor = page.locator('.ProseMirror');
|
||||
await editor.click();
|
||||
await editor.fill('This is my test secret message');
|
||||
|
||||
// Click create button
|
||||
await page
|
||||
.getByRole('button', { name: /create/i })
|
||||
.first()
|
||||
.click();
|
||||
|
||||
// Wait for success state - check for the success icon or URL field
|
||||
await expect(page.getByText(/secret.*created/i)).toBeVisible({ timeout: 10000 });
|
||||
|
||||
// Verify the secret URL is displayed
|
||||
const urlInput = page.locator('input[readonly]').first();
|
||||
await expect(urlInput).toBeVisible();
|
||||
|
||||
const secretUrl = await urlInput.inputValue();
|
||||
expect(secretUrl).toContain('/secret/');
|
||||
expect(secretUrl).toContain('#decryptionKey=');
|
||||
});
|
||||
|
||||
test('should create a secret with a title', async ({ authenticatedPage: page }) => {
|
||||
await page.goto('/');
|
||||
|
||||
// Type secret content
|
||||
const editor = page.locator('.ProseMirror');
|
||||
await editor.click();
|
||||
await editor.fill('Secret with a title');
|
||||
|
||||
// Add a title
|
||||
const titleInput = page.getByPlaceholder(/title/i);
|
||||
if (await titleInput.isVisible()) {
|
||||
await titleInput.fill('My Secret Title');
|
||||
}
|
||||
|
||||
// Create the secret
|
||||
await page
|
||||
.getByRole('button', { name: /create/i })
|
||||
.first()
|
||||
.click();
|
||||
|
||||
// Verify success
|
||||
await expect(page.getByText(/secret.*created/i)).toBeVisible({ timeout: 10000 });
|
||||
});
|
||||
|
||||
test('should create and view a secret end-to-end', async ({ authenticatedPage: page }) => {
|
||||
await page.goto('/');
|
||||
|
||||
const secretText = `Test secret created at ${Date.now()}`;
|
||||
|
||||
// Create a secret
|
||||
const editor = page.locator('.ProseMirror');
|
||||
await editor.click();
|
||||
await editor.fill(secretText);
|
||||
|
||||
await page
|
||||
.getByRole('button', { name: /create/i })
|
||||
.first()
|
||||
.click();
|
||||
|
||||
// Wait for the URL to appear
|
||||
await expect(page.getByText(/secret.*created/i)).toBeVisible({ timeout: 10000 });
|
||||
|
||||
// Get the secret URL
|
||||
const urlInput = page.locator('input[readonly]').first();
|
||||
const secretUrl = await urlInput.inputValue();
|
||||
|
||||
// Navigate to the secret URL
|
||||
await page.goto(secretUrl);
|
||||
|
||||
// Click the unlock/view button
|
||||
const unlockButton = page.getByRole('button', { name: /unlock|view/i });
|
||||
await expect(unlockButton).toBeVisible({ timeout: 5000 });
|
||||
await unlockButton.click();
|
||||
|
||||
// Verify the secret content is displayed
|
||||
await expect(page.locator('.ProseMirror')).toContainText(secretText, { timeout: 10000 });
|
||||
});
|
||||
|
||||
test('should create a password-protected secret', async ({ authenticatedPage: page }) => {
|
||||
await page.goto('/');
|
||||
|
||||
const secretText = 'This is a password protected secret';
|
||||
const password = 'mysecretpassword123';
|
||||
|
||||
// Type secret content
|
||||
const editor = page.locator('.ProseMirror');
|
||||
await editor.click();
|
||||
await editor.fill(secretText);
|
||||
|
||||
// Look for password protection toggle in security settings section
|
||||
// The toggle might be in a section that needs to be scrolled to
|
||||
const passwordSection = page.locator('text=Password Protection');
|
||||
|
||||
if (await passwordSection.isVisible({ timeout: 2000 }).catch(() => false)) {
|
||||
// Click the toggle switch near "Password Protection"
|
||||
const toggleSwitch = passwordSection
|
||||
.locator('xpath=..')
|
||||
.locator('button[role="switch"], input[type="checkbox"]');
|
||||
if (await toggleSwitch.isVisible()) {
|
||||
await toggleSwitch.click();
|
||||
|
||||
// Wait for password input to appear
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// Find the password input that appeared
|
||||
const passwordInput = page
|
||||
.locator('input[placeholder*="password" i], input[type="password"]')
|
||||
.first();
|
||||
if (await passwordInput.isVisible()) {
|
||||
await passwordInput.fill(password);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Password protection not available, skip this assertion
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the secret
|
||||
await page
|
||||
.getByRole('button', { name: /create/i })
|
||||
.first()
|
||||
.click();
|
||||
|
||||
// Wait for success
|
||||
await expect(page.getByText(/secret.*created/i)).toBeVisible({ timeout: 10000 });
|
||||
|
||||
// Get the secret URL
|
||||
const urlInput = page.locator('input[readonly]').first();
|
||||
const secretUrl = await urlInput.inputValue();
|
||||
|
||||
// Navigate to the secret
|
||||
await page.goto(secretUrl);
|
||||
|
||||
// If password was set, the secret page should prompt for password
|
||||
// (URL might still have decryptionKey depending on implementation)
|
||||
const unlockButton = page.getByRole('button', { name: /unlock|view/i });
|
||||
await expect(unlockButton).toBeVisible({ timeout: 5000 });
|
||||
await unlockButton.click();
|
||||
|
||||
// Verify content is visible (secret should decrypt with the key in URL)
|
||||
await expect(page.locator('.ProseMirror')).toContainText(secretText, { timeout: 10000 });
|
||||
});
|
||||
|
||||
test('should burn a secret after viewing', async ({ authenticatedPage: page }) => {
|
||||
await page.goto('/');
|
||||
|
||||
// Create a secret
|
||||
const editor = page.locator('.ProseMirror');
|
||||
await editor.click();
|
||||
await editor.fill('Secret to be burned');
|
||||
|
||||
await page
|
||||
.getByRole('button', { name: /create/i })
|
||||
.first()
|
||||
.click();
|
||||
|
||||
// Wait for success and get URL
|
||||
await expect(page.getByText(/secret.*created/i)).toBeVisible({ timeout: 10000 });
|
||||
const urlInput = page.locator('input[readonly]').first();
|
||||
const secretUrl = await urlInput.inputValue();
|
||||
|
||||
// View the secret
|
||||
await page.goto(secretUrl);
|
||||
await page.getByRole('button', { name: /unlock|view/i }).click();
|
||||
await expect(page.locator('.ProseMirror')).toBeVisible({ timeout: 10000 });
|
||||
|
||||
// Delete the secret
|
||||
await page.getByRole('button', { name: /delete/i }).click();
|
||||
|
||||
// Confirm deletion in modal
|
||||
const confirmButton = page.getByRole('button', { name: /delete|confirm/i }).last();
|
||||
await confirmButton.click();
|
||||
|
||||
// Should redirect to home
|
||||
await expect(page).toHaveURL('/');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user