241 lines
8.1 KiB
TypeScript
241 lines
8.1 KiB
TypeScript
import { expect, test } from '../fixtures'
|
|
|
|
let consoleErrors: string[]
|
|
let pageErrors: string[]
|
|
|
|
test.beforeEach(async ({ page }) => {
|
|
consoleErrors = []
|
|
pageErrors = []
|
|
page.on('console', (message) => {
|
|
if (message.type() === 'error') consoleErrors.push(message.text())
|
|
})
|
|
page.on('pageerror', (error) => pageErrors.push(error.message))
|
|
})
|
|
|
|
test.afterEach(() => {
|
|
expect(consoleErrors, 'browser console errors').toEqual([])
|
|
expect(pageErrors, 'uncaught browser errors').toEqual([])
|
|
})
|
|
|
|
test('requires intent and infinitely loads a user timeline', async ({
|
|
homePage,
|
|
page,
|
|
userPage,
|
|
}) => {
|
|
await homePage.goTo()
|
|
await expect(page).toHaveURL(/\/$/)
|
|
await expect(page.locator('meta[name="referrer"]')).toHaveAttribute(
|
|
'content',
|
|
'no-referrer',
|
|
)
|
|
await expect(homePage.articlesLocator).toHaveCount(0)
|
|
await expect(homePage.ambientLinksLocator).toHaveCount(0)
|
|
|
|
await homePage.displayUser('@yuta')
|
|
|
|
await expect(page).toHaveURL(/\/yuta$/)
|
|
await expect(page.getByText('user page 1')).toBeVisible()
|
|
await expect(page.getByText('user page 2')).toBeVisible()
|
|
await expect(userPage.endOfFeedLocator).toBeVisible()
|
|
await expect(page).toHaveScreenshot('mist-user.png', {
|
|
animations: 'disabled',
|
|
fullPage: true,
|
|
})
|
|
})
|
|
|
|
test('switches the relay profile and reloads visible content', async ({
|
|
homePage,
|
|
page,
|
|
}) => {
|
|
await homePage.goTo()
|
|
|
|
const profile = page.getByLabel('プロファイル')
|
|
await expect(profile).toHaveValue('e2e')
|
|
await homePage.displayUser('@yuta')
|
|
await expect(page.getByText('user page 1')).toBeVisible()
|
|
|
|
await profile.selectOption('alternate')
|
|
await expect(page.getByRole('status')).toHaveText('切り替えました。')
|
|
|
|
await expect(page.getByText('alternate profile page 1')).toBeVisible()
|
|
})
|
|
|
|
test('keeps the profile selector within narrow viewports', async ({
|
|
homePage,
|
|
page,
|
|
}) => {
|
|
for (const width of [320, 375, 414, 768]) {
|
|
await page.setViewportSize({ width, height: 900 })
|
|
await homePage.goTo()
|
|
await expect(page.getByLabel('プロファイル')).toBeVisible()
|
|
expect(
|
|
await page.evaluate(
|
|
() => document.documentElement.scrollWidth > window.innerWidth,
|
|
),
|
|
).toBe(false)
|
|
}
|
|
})
|
|
|
|
test('selects an authenticated list and infinitely loads its timeline', async ({
|
|
listsPage,
|
|
page,
|
|
}) => {
|
|
await listsPage.goTo()
|
|
await expect(listsPage.articlesLocator).toHaveCount(0)
|
|
|
|
await listsPage.openList(/My E2E List.*10 メンバー/)
|
|
|
|
await expect(page).toHaveURL(/\/i\/lists\/1234567890$/)
|
|
await expect(page.getByText('list page 1')).toBeVisible()
|
|
await expect(page.getByText('list page 2')).toBeVisible()
|
|
await expect(listsPage.endOfFeedLocator).toBeVisible()
|
|
})
|
|
|
|
test('opens a card detail and infinitely loads its conversation', async ({
|
|
page,
|
|
threadPage,
|
|
userPage,
|
|
}) => {
|
|
await userPage.goTo('yuta')
|
|
await userPage.openPostDetails('user page 1')
|
|
|
|
await expect(page).toHaveURL(/\/yuta\/status\/1001$/)
|
|
await expect(threadPage.headingLocator).toBeVisible()
|
|
await expect(threadPage.selectedPostLabelLocator).toBeVisible()
|
|
await expect(page.getByText('user page 1')).toHaveCount(1)
|
|
await expect(page.getByText('thread root')).toBeVisible()
|
|
await expect(page.getByText('thread page 1')).toBeVisible()
|
|
await expect(page.getByText('thread page 2')).toBeVisible()
|
|
await expect(threadPage.endOfFeedLocator).toBeVisible()
|
|
await expect(page).toHaveScreenshot('mist-thread.png', {
|
|
animations: 'disabled',
|
|
fullPage: true,
|
|
})
|
|
})
|
|
|
|
test('resolves a reply root and retries only the failed continuation', async ({
|
|
page,
|
|
threadPage,
|
|
}) => {
|
|
await threadPage.goTo('yuta', '2001')
|
|
|
|
await expect(page.getByText('retry focal')).toHaveCount(1)
|
|
await expect(page.getByText('thread root')).toBeVisible()
|
|
await expect(page.getByText('thread page 1')).toBeVisible()
|
|
await expect(threadPage.continuationErrorLocator).toHaveCount(1)
|
|
await expect(page.getByText('retry focal')).toHaveCount(1)
|
|
|
|
await threadPage.retryContinuation()
|
|
|
|
await expect(page.getByText('thread page 2')).toBeVisible()
|
|
await expect(threadPage.continuationErrorLocator).toHaveCount(0)
|
|
await expect(threadPage.endOfFeedLocator).toBeVisible()
|
|
await expect(threadPage.mutationButtonsLocator).toHaveCount(0)
|
|
})
|
|
|
|
test('ends a thread without loading a continuation when there are no replies', async ({
|
|
page,
|
|
threadPage,
|
|
}) => {
|
|
await threadPage.goTo('yuta', '3001')
|
|
|
|
await expect(page.getByText('no replies')).toHaveCount(1)
|
|
await expect(page.getByText('thread root')).toBeVisible()
|
|
await expect(threadPage.endOfFeedLocator).toBeVisible()
|
|
await expect(threadPage.continuationErrorLocator).toHaveCount(0)
|
|
})
|
|
|
|
test('searches Top posts from followed accounts', async ({
|
|
page,
|
|
searchPage,
|
|
}) => {
|
|
await searchPage.goTo()
|
|
await expect(searchPage.articlesLocator).toHaveCount(0)
|
|
await searchPage.searchTopFromFollowing('AI lang:ja')
|
|
await expect(searchPage.followingOnlyLocator).toBeChecked()
|
|
|
|
await expect(page).toHaveURL(/following=true/)
|
|
await expect(page.getByText('Top · follows page 1')).toBeVisible()
|
|
await expect(page.getByText('Top · follows page 2')).toBeVisible()
|
|
await expect(searchPage.endOfFeedLocator).toBeVisible()
|
|
})
|
|
|
|
test('retains the first page through a pagination error and retries explicitly', async ({
|
|
page,
|
|
searchPage,
|
|
}, testInfo) => {
|
|
await searchPage.goTo()
|
|
await expect(searchPage.articlesLocator).toHaveCount(0)
|
|
await searchPage.search(`retry-${testInfo.project.name}`)
|
|
|
|
await expect(page.getByText('Latest · all page 1')).toBeVisible()
|
|
await expect(searchPage.continuationErrorLocator).toHaveCount(1)
|
|
await expect(page.getByText('Latest · all page 1')).toBeVisible()
|
|
await expect(page.getByText('Latest · all page 2')).toHaveCount(0)
|
|
|
|
await searchPage.retryContinuation()
|
|
|
|
await expect(page.getByText('Latest · all page 2')).toBeVisible()
|
|
await expect(searchPage.continuationErrorLocator).toHaveCount(0)
|
|
await expect(searchPage.endOfFeedLocator).toBeVisible()
|
|
})
|
|
|
|
test('keeps keyboard focus intentional and discovery or mutation controls absent', async ({
|
|
searchPage,
|
|
}) => {
|
|
await searchPage.goTo()
|
|
|
|
await searchPage.tabTo(
|
|
searchPage.page.getByRole('combobox', { name: 'プロファイル' }),
|
|
)
|
|
await searchPage.tabTo(searchPage.page.getByRole('link', { name: '検索' }))
|
|
await searchPage.tabTo(
|
|
searchPage.page.getByRole('link', { name: 'ユーザー' }),
|
|
)
|
|
await searchPage.tabTo(searchPage.page.getByRole('link', { name: 'リスト' }))
|
|
await searchPage.tabTo(searchPage.queryInputLocator)
|
|
await searchPage.tabTo(
|
|
searchPage.page.getByRole('button', { name: '投稿者' }),
|
|
)
|
|
await searchPage.tabTo(searchPage.page.getByRole('button', { name: '期間' }))
|
|
await searchPage.tabTo(searchPage.page.getByRole('button', { name: '言語' }))
|
|
await searchPage.tabTo(
|
|
searchPage.page.getByRole('button', { name: '投稿の種類' }),
|
|
)
|
|
await searchPage.tabTo(searchPage.page.getByRole('button', { name: '除外' }))
|
|
await searchPage.tabTo(
|
|
searchPage.page.getByText('検索式を確認', { exact: true }),
|
|
)
|
|
await expect(searchPage.latestRankingLocator).toBeChecked()
|
|
await searchPage.tabTo(searchPage.latestRankingLocator)
|
|
await searchPage.tabTo(searchPage.followingOnlyLocator)
|
|
await searchPage.tabTo(searchPage.searchButtonLocator)
|
|
|
|
await expect(searchPage.ambientLinksLocator).toHaveCount(0)
|
|
await expect(searchPage.mutationButtonsLocator).toHaveCount(0)
|
|
await expect(searchPage.mutationLinksLocator).toHaveCount(0)
|
|
})
|
|
|
|
test('removes loading rail animation for reduced motion', async ({
|
|
page,
|
|
searchPage,
|
|
}, testInfo) => {
|
|
await page.emulateMedia({ reducedMotion: 'reduce' })
|
|
await searchPage.goTo()
|
|
await expect(searchPage.articlesLocator).toHaveCount(0)
|
|
await searchPage.search(`slow-${testInfo.project.name}`)
|
|
|
|
const rail = searchPage.loadingRailLocator
|
|
await expect(rail).toBeVisible()
|
|
await expect
|
|
.poll(() =>
|
|
rail.evaluate((element) =>
|
|
getComputedStyle(element, '::after').getPropertyValue('animation-name'),
|
|
),
|
|
)
|
|
.toBe('none')
|
|
|
|
await expect(page.getByText('Latest · all page 2')).toBeVisible()
|
|
await expect(searchPage.endOfFeedLocator).toBeVisible()
|
|
})
|