Corrige aussi la hauteur repliée (box-sizing: border-box) : le padding de .text s'ajoutait à la hauteur configurée. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
116 lines
4.9 KiB
JavaScript
116 lines
4.9 KiB
JavaScript
// @ts-check
|
|
// Requires `bash tests/e2e/setup.sh` (FreshRSS 1.30.4 on http://localhost:8080).
|
|
const { test, expect } = require('@playwright/test');
|
|
const { execFileSync } = require('node:child_process');
|
|
const path = require('node:path');
|
|
|
|
const COMPOSE = path.join(__dirname, 'docker-compose.yml');
|
|
const READER = '/i/?a=reader&state=2'; // unread only
|
|
|
|
function reset(height = 250) {
|
|
execFileSync('docker', ['compose', '-f', COMPOSE, 'exec', '-T', '--user', 'www-data', 'freshrss',
|
|
'php', '/var/www/FreshRSS/extensions/freshrss-read-more/tests/e2e/reset.php', String(height)]);
|
|
}
|
|
|
|
// Entries are sorted newest first: "Article 1" is the first .flux.
|
|
const fluxByTitle = (page, n) => page.locator('.flux', { hasText: `Article ${n}.` }).first();
|
|
|
|
async function scrollPast(page, flux) {
|
|
await flux.evaluate((el) => {
|
|
const r = el.getBoundingClientRect();
|
|
window.scrollBy(0, r.bottom + 20);
|
|
});
|
|
await page.waitForTimeout(1000); // FreshRSS throttles onScroll at 500 ms
|
|
await page.evaluate(() => window.dispatchEvent(new Event('scroll')));
|
|
}
|
|
|
|
test.beforeEach(() => reset());
|
|
|
|
test('long entries collapsed', async ({ page }) => {
|
|
await page.goto(READER);
|
|
for (const n of [1, 2, 3, 4, 5, 6, 8]) {
|
|
const flux = fluxByTitle(page, n);
|
|
await expect(flux).toHaveAttribute('data-read-more', 'collapsed');
|
|
await expect(flux.locator('.read-more-btn')).toHaveCount(1);
|
|
await expect(flux.locator('.read-more-btn')).toHaveText('Lire plus');
|
|
const text = flux.locator('.text');
|
|
expect(await text.evaluate((el) => getComputedStyle(el).maxHeight)).toBe('250px');
|
|
expect(await text.evaluate((el) => el.getBoundingClientRect().height)).toBeLessThanOrEqual(250);
|
|
}
|
|
});
|
|
|
|
test('short entry not collapsed', async ({ page }) => {
|
|
await page.goto(READER);
|
|
const flux = fluxByTitle(page, 7);
|
|
await expect(flux).toHaveAttribute('data-read-more', 'short');
|
|
await expect(flux.locator('.read-more-btn')).toHaveCount(0);
|
|
});
|
|
|
|
test('collapsed entry marked read after scrolling past', async ({ page }) => {
|
|
await page.goto(READER);
|
|
const flux = fluxByTitle(page, 1);
|
|
await expect(flux).toHaveAttribute('data-read-more', 'collapsed');
|
|
await expect(flux).toHaveClass(/not_read/);
|
|
await scrollPast(page, flux);
|
|
await expect(flux).not.toHaveClass(/not_read/, { timeout: 5000 });
|
|
await page.waitForTimeout(500); // let the mark-read request land
|
|
await page.goto(READER);
|
|
await expect(page.locator('.flux', { hasText: 'Article 2.' }).first()).toBeVisible();
|
|
await expect(page.locator('.flux', { hasText: 'Article 1.' })).toHaveCount(0);
|
|
});
|
|
|
|
test('read more expands without changing read state or scroll', async ({ page }) => {
|
|
await page.goto(READER);
|
|
const flux = fluxByTitle(page, 1);
|
|
const button = flux.locator('.read-more-btn');
|
|
await button.scrollIntoViewIfNeeded();
|
|
const before = await page.evaluate(() => window.scrollY);
|
|
await button.click();
|
|
await expect(flux).toHaveAttribute('data-read-more', 'expanded');
|
|
expect(await flux.locator('.text').evaluate((el) => getComputedStyle(el).maxHeight)).toBe('none');
|
|
await expect(button).toHaveCount(0);
|
|
await expect(flux).toHaveClass(/not_read/);
|
|
const after = await page.evaluate(() => window.scrollY);
|
|
expect(Math.abs(after - before)).toBeLessThan(5);
|
|
});
|
|
|
|
test('expanded entry marked read after scrolling past', async ({ page }) => {
|
|
await page.goto(READER);
|
|
const flux = fluxByTitle(page, 2);
|
|
await flux.locator('.read-more-btn').click();
|
|
await expect(flux).toHaveAttribute('data-read-more', 'expanded');
|
|
const height = await flux.evaluate((el) => el.getBoundingClientRect().height);
|
|
expect(height).toBeGreaterThan(375);
|
|
await scrollPast(page, flux);
|
|
await expect(flux).not.toHaveClass(/not_read/, { timeout: 5000 });
|
|
});
|
|
|
|
test('normal view untouched', async ({ page }) => {
|
|
await page.goto('/i/?a=normal&state=2');
|
|
await expect(page.locator('.flux').first()).toBeAttached();
|
|
await expect(page.locator('.read-more-btn')).toHaveCount(0);
|
|
await expect(page.locator('.flux[data-read-more]')).toHaveCount(0);
|
|
});
|
|
|
|
test('config height is clamped', async ({ page }) => {
|
|
const configUrl = '/i/?c=extension&a=configure&e=' + encodeURIComponent('Read More');
|
|
const submit = async (value) => {
|
|
await page.goto(configUrl);
|
|
const csrf = await page.locator('input[name="_csrf"]').first().inputValue();
|
|
const res = await page.request.post(configUrl, {
|
|
form: { _csrf: csrf, collapsed_height: String(value) },
|
|
});
|
|
expect(res.ok()).toBeTruthy();
|
|
// FreshRSS answers 304 within the same second: bust the cache.
|
|
await page.goto(configUrl + '&t=' + Date.now());
|
|
return page.locator('#collapsed_height').inputValue();
|
|
};
|
|
expect(await submit(5)).toBe('100');
|
|
expect(await submit(99999)).toBe('2000');
|
|
expect(await submit(400)).toBe('400');
|
|
await page.goto(READER);
|
|
const text = fluxByTitle(page, 1).locator('.text');
|
|
expect(await text.evaluate((el) => getComputedStyle(el).maxHeight)).toBe('400px');
|
|
expect(await text.evaluate((el) => el.getBoundingClientRect().height)).toBeLessThanOrEqual(400);
|
|
});
|