import test from 'node:test'; import assert from 'node:assert/strict'; import puppeteer from 'puppeteer-core'; test('custom width leaves notification avatar columns narrow', async () => { const browser = await puppeteer.launch({ executablePath: process.env.CHROME_PATH || '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome', headless: true, }); try { const page = await browser.newPage(); await page.setViewport({ width: 1600, height: 1000 }); await page.setContent(`
Avatar
Notification text should have room to wrap normally.
`); await page.addScriptTag({ path: 'extension/shared/feed-width.js' }); await page.evaluate(() => window.BskyPlusFeedWidth.setWidth(1000)); await page.waitForFunction(() => document.getElementById('feed').getBoundingClientRect().width === 1000 && !document.documentElement.hasAttribute('data-bskyplus-feed-width-animating')); const layout = await page.evaluate(() => Object.fromEntries( ['feed', 'avatar', 'text'].map(id => [id, document.getElementById(id).getBoundingClientRect().width]))); assert.equal(layout.feed, 1000); assert.equal(layout.avatar, 48, 'Avatar column must retain its own width'); assert.ok(layout.text > 900, 'Notification text must retain the remaining row width'); } finally { await browser.close(); } }); test('widened text-only posts use the full column while embeds stay contained', async () => { const browser = await puppeteer.launch({ executablePath: process.env.CHROME_PATH || '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome', headless: true, }); try { const page = await browser.newPage(); await page.setViewport({ width: 1600, height: 1000 }); await page.setContent(`
A text-only post that should fill the feed.
A post with media.
Sample image
Quoted author
A text-only quote card.
`); await page.addStyleTag({ path: 'extension/shared/features.css' }); await page.addScriptTag({ path: 'extension/shared/feed-width.js' }); await page.evaluate(() => window.BskyPlusFeedWidth.setWidth(1000)); await page.waitForFunction(() => document.getElementById('feed').getBoundingClientRect().width === 1000 && !document.documentElement.hasAttribute('data-bskyplus-feed-width-animating')); const widths = await page.evaluate(() => { const post = document.createElement('div'); post.setAttribute('data-testid', 'contentHider-post'); post.style.cssText = 'display:flex;flex-direction:column'; const wrapper = document.createElement('div'); wrapper.id = 'newTextWrapper'; wrapper.style.marginBottom = '2px'; const text = document.createElement('div'); text.setAttribute('data-testid', 'postText'); text.textContent = 'A newly loaded text-only post.'; wrapper.appendChild(text); post.appendChild(wrapper); document.getElementById('feed').appendChild(post); return Object.fromEntries(['textWrapper', 'newTextWrapper', 'media', 'mediaImage', 'videoWrapper', 'postVideo', 'quote'].map(id => [ id, document.getElementById(id).getBoundingClientRect().width ])); }); assert.deepEqual(widths, { textWrapper: 1000, newTextWrapper: 1000, media: 600, mediaImage: 600, videoWrapper: 600, postVideo: 600, quote: 1000 }); } finally { await browser.close(); } }); test('live width changes animate the feed, borders, and sidebars unless motion is reduced', async () => { const browser = await puppeteer.launch({ executablePath: process.env.CHROME_PATH || '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome', headless: true, }); try { const page = await browser.newPage(); await page.setViewport({ width: 1600, height: 1000 }); await page.setContent(`
`); await page.addScriptTag({ path: 'extension/shared/feed-width.js' }); await page.evaluate(() => window.BskyPlusFeedWidth.setWidth(900)); await page.waitForFunction(() => document.getElementById('feed').getAnimations() .some(animation => animation.transitionProperty === 'max-width')); const active = await page.evaluate(() => { const isTransitioning = (id, property) => document.getElementById(id).getAnimations() .some(animation => animation.transitionProperty === property); return { feed: isTransitioning('feed', 'max-width'), borders: isTransitioning('borders', 'width'), left: isTransitioning('left', 'margin-left'), right: isTransitioning('right', 'margin-left') }; }); assert.deepEqual(active, { feed: true, borders: true, left: true, right: true }); const duration = await page.$eval('#feed', feed => feed.getAnimations() .find(animation => animation.transitionProperty === 'max-width').effect.getTiming().duration); assert.equal(duration, 800); await page.waitForFunction(() => Math.round(document.getElementById('feed').getBoundingClientRect().width) === 900 && !document.documentElement.hasAttribute('data-bskyplus-feed-width-animating')); await page.emulateMediaFeatures([{ name: 'prefers-reduced-motion', value: 'reduce' }]); const reducedMotion = await page.evaluate(() => { window.BskyPlusFeedWidth.setWidth(700); const feed = document.getElementById('feed'); return { width: feed.getBoundingClientRect().width, animations: feed.getAnimations().length }; }); assert.deepEqual(reducedMotion, { width: 700, animations: 0 }); } finally { await browser.close(); } });