diff --git a/README.md b/README.md
index 9898fb5..f4f9f73 100644
--- a/README.md
+++ b/README.md
@@ -183,7 +183,12 @@ Un socle de tests E2E est fourni pour valider les parcours critiques :
```bash
cd shaarli-pro
npm run test:e2e:install # installer les navigateurs Playwright (une fois)
+
+# Linux / macOS
SHAARLI_URL=http://localhost:8080 npm run test:e2e
+
+# Windows (PowerShell)
+$env:SHAARLI_URL="http://localhost:8080"; npm run test:e2e
```
Les tests couvrent actuellement :
diff --git a/shaarli-pro/linklist.paging.html b/shaarli-pro/linklist.paging.html
index b0303d5..42d4b15 100644
--- a/shaarli-pro/linklist.paging.html
+++ b/shaarli-pro/linklist.paging.html
@@ -14,9 +14,9 @@
{$from=($page_current - 1) * $links_per_page + 1}
{$to=min($total, ($page_current - 1) * $links_per_page + $links_per_page)}
{if="!empty($search_tags) && stripos($search_tags, 'note') !== false"}
-{$paging_label="{'Notes'|t}"}
+{$paging_label=t('Notes')}
{else}
-{$paging_label="{'Bookmarks'|t}"}
+{$paging_label=t('Bookmarks')}
{/if}
{if="$page_max > 1"}
diff --git a/shaarli-pro/tests/smoke.spec.js b/shaarli-pro/tests/smoke.spec.js
index 3603367..081f1d5 100644
--- a/shaarli-pro/tests/smoke.spec.js
+++ b/shaarli-pro/tests/smoke.spec.js
@@ -3,36 +3,76 @@
*
* These tests require a running Shaarli instance with the shaarli-pro theme
* activated. Set SHAARLI_URL before running.
+ *
+ * Example:
+ * SHAARLI_URL=http://localhost:8080 npx playwright test
*/
'use strict';
const { test, expect } = require('@playwright/test');
+/**
+ * Verify the current page resolves to a Shaarli instance.
+ * Returns true if Shaarli elements are found, skips the test otherwise.
+ */
+async function ensureShaarli(page, testInfo) {
+ // Check for Shaarli-specific markers (linklist container, sidebar, or the search overlay)
+ const shaarliMarkers = page.locator('#linklist, .shaarli-sidebar, #search-overlay, .search-overlay, meta[name="generator"][content*="Shaarli"]');
+ await shaarliMarkers.first().waitFor({ state: 'attached', timeout: 3000 }).catch(() => {});
+ const count = await shaarliMarkers.count();
+ if (count === 0) {
+ // Diagnose: what's actually running on this URL?
+ const title = await page.title();
+ const bodyText = await page.locator('body').textContent({ timeout: 2000 }).catch(() => '(timeout)');
+ const snippet = (bodyText || '').substring(0, 200);
+ throw new Error(
+ `SHAARLI_URL (${testInfo.project.use.baseURL}) does not appear to be a Shaarli instance.\n` +
+ `Page title: "${title}"\n` +
+ `Page content: "${snippet}"\n\n` +
+ `Make sure:\n` +
+ ` 1. Shaarli is running and accessible at this URL\n` +
+ ` 2. The "shaarli-pro" theme is activated in Shaarli settings\n` +
+ ` 3. No other application is using this port`
+ );
+ }
+ return true;
+}
+
test.describe('Shaarli Pro theme smoke tests', () => {
- test('homepage loads and shows the main layout', async ({ page }) => {
+ test('homepage loads and shows the main layout', async ({ page }, testInfo) => {
await page.goto('/');
await expect(page.locator('body')).toBeVisible();
- // Sidebar or header should be present.
- await expect(
- page.locator('.shaarli-sidebar, .sidebar, header, .header')
- ).toHaveCount(1);
+ await ensureShaarli(page, testInfo);
+
+ // Header should be present (the theme uses )
+ const header = page.locator('header.header-main');
+ await expect(header).toBeVisible({ timeout: 5000 });
+
+ // Sidebar should be present
+ const sidebar = page.locator('.sidebar');
+ await expect(sidebar).toBeVisible();
});
- test('search overlay opens with the S shortcut', async ({ page }) => {
+ test('search overlay opens with the S shortcut', async ({ page }, testInfo) => {
await page.goto('/');
+ await ensureShaarli(page, testInfo);
+
const search = page.locator('.search-overlay, #search-overlay');
await expect(search).not.toBeVisible();
await page.keyboard.press('s');
- await expect(search).toBeVisible();
+ await expect(search).toBeVisible({ timeout: 3000 });
await page.keyboard.press('Escape');
- await expect(search).not.toBeVisible();
+ await expect(search).not.toBeVisible({ timeout: 2000 });
});
- test('theme toggle changes data-theme attribute', async ({ page }) => {
+ test('theme toggle changes data-theme attribute', async ({ page }, testInfo) => {
await page.goto('/');
+ await ensureShaarli(page, testInfo);
+
const toggle = page.locator('[data-theme-toggle], .theme-toggle, #theme-toggle');
if (await toggle.count() === 0) {
test.skip('No theme toggle found on this page');
+ return;
}
const initialTheme = await page.locator('html').getAttribute('data-theme');
await toggle.click();
@@ -41,16 +81,22 @@ test.describe('Shaarli Pro theme smoke tests', () => {
expect(newTheme).not.toEqual(initialTheme);
});
- test('skip-link is present for keyboard users', async ({ page }) => {
+ test('skip-link is present for keyboard users', async ({ page }, testInfo) => {
await page.goto('/');
+ await ensureShaarli(page, testInfo);
+
+ await expect(page.locator('.skip-link')).toBeVisible();
await expect(page.locator('.skip-link')).toHaveAttribute('href');
});
- test('bulk selection button is present on link list', async ({ page }) => {
+ test('bulk selection button is present on link list', async ({ page }, testInfo) => {
await page.goto('/');
+ await ensureShaarli(page, testInfo);
+
const bulkButton = page.locator('[data-bulk-select], .bulk-select-toggle, #bulk-select');
if (await bulkButton.count() === 0) {
- test.skip('No bulk selection button found');
+ test.skip('No bulk selection button found (user may not be logged in)');
+ return;
}
await expect(bulkButton).toBeVisible();
});