fix: fix aria-posinset should be 1-based (#1055)

* fix: fix aria-posinset should be 1-based

fixes #1053

* second attempt to fix tests

* try to fix test

*  fixup

* lint fix

* fix more tests

* simplify test math
This commit is contained in:
Nolan Lawson 2019-02-28 08:56:25 -08:00 committed by GitHub
parent b0aa86ef44
commit 63003c3763
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
44 changed files with 580 additions and 579 deletions

View file

@ -6,7 +6,7 @@
<article id={elementId} <article id={elementId}
class={className} class={className}
tabindex="0" tabindex="0"
aria-posinset={index} aria-posinset={index + 1}
aria-setsize={length} aria-setsize={length}
aria-label={ariaLabel} aria-label={ariaLabel}
on:applyFocusStylesToParent="noop()" on:applyFocusStylesToParent="noop()"

View file

@ -1,7 +1,7 @@
<article id={elementId} <article id={elementId}
class={className} class={className}
tabindex="0" tabindex="0"
aria-posinset={index} aria-posinset={index + 1}
aria-setsize={length} aria-setsize={length}
aria-label={ariaLabel} aria-label={ariaLabel}
on:recalculateHeight on:recalculateHeight

View file

@ -77,10 +77,12 @@ test('Logs in and logs out of localhost:3000', async t => {
test('Logs in, refreshes, then logs out', async t => { test('Logs in, refreshes, then logs out', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.hover(getNthStatus(0)) .expect(getNthStatus(1).exists).ok({ timeout: 30000 })
.hover(getNthStatus(1))
await reload() await reload()
await t await t
.hover(getNthStatus(0)) .expect(getNthStatus(1).exists).ok({ timeout: 30000 })
.hover(getNthStatus(1))
.click(settingsButton) .click(settingsButton)
.click($('a').withText('Instances')) .click($('a').withText('Instances'))
.click($('a').withText('localhost:3000')) .click($('a').withText('localhost:3000'))

View file

@ -14,11 +14,11 @@ test('Shows the home timeline', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.expect(getUrl()).eql('http://localhost:4002/') .expect(getUrl()).eql('http://localhost:4002/')
.expect(getNthStatus(0).exists).ok({ timeout: 30000 }) .expect(getNthStatus(1).exists).ok({ timeout: 30000 })
.hover(getNthStatus(0)) .hover(getNthStatus(1))
.expect(getFirstVisibleStatus().exists).ok() .expect(getFirstVisibleStatus().exists).ok()
.expect(getFirstVisibleStatus().hasAttribute('aria-setsize')).ok() .expect(getFirstVisibleStatus().hasAttribute('aria-setsize')).ok()
.expect(getFirstVisibleStatus().getAttribute('aria-posinset')).eql('0') .expect(getFirstVisibleStatus().getAttribute('aria-posinset')).eql('1')
await validateTimeline(t, homeTimeline) await validateTimeline(t, homeTimeline)
@ -29,7 +29,7 @@ test('Shows notifications', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.expect(getUrl()).eql('http://localhost:4002/') .expect(getUrl()).eql('http://localhost:4002/')
.expect(getNthStatus(0).exists).ok({ timeout: 30000 }) .expect(getNthStatus(1).exists).ok({ timeout: 30000 })
.click(notificationsNavButton) .click(notificationsNavButton)
.expect(getUrl()).contains('/notifications') .expect(getUrl()).contains('/notifications')
@ -40,7 +40,7 @@ test('Shows the local timeline', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.expect(getUrl()).eql('http://localhost:4002/') .expect(getUrl()).eql('http://localhost:4002/')
.expect(getNthStatus(0).exists).ok({ timeout: 30000 }) .expect(getNthStatus(1).exists).ok({ timeout: 30000 })
.click(localTimelineNavButton) .click(localTimelineNavButton)
.expect(getUrl()).contains('/local') .expect(getUrl()).contains('/local')
@ -51,7 +51,7 @@ test('Shows the federated timeline', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.expect(getUrl()).eql('http://localhost:4002/') .expect(getUrl()).eql('http://localhost:4002/')
.expect(getNthStatus(0).exists).ok({ timeout: 30000 }) .expect(getNthStatus(1).exists).ok({ timeout: 30000 })
.click(communityNavButton) .click(communityNavButton)
.expect(getUrl()).contains('/community') .expect(getUrl()).contains('/community')
.click($('a').withText('Federated')) .click($('a').withText('Federated'))
@ -64,7 +64,7 @@ test('Shows favorites', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.expect(getUrl()).eql('http://localhost:4002/') .expect(getUrl()).eql('http://localhost:4002/')
.expect(getNthStatus(0).exists).ok({ timeout: 30000 }) .expect(getNthStatus(1).exists).ok({ timeout: 30000 })
.click(communityNavButton) .click(communityNavButton)
.expect(getUrl()).contains('/community') .expect(getUrl()).contains('/community')
.click($('a').withText('Favorites')) .click($('a').withText('Favorites'))

View file

@ -12,7 +12,7 @@ test("shows a user's pinned statuses", async t => {
.expect(getUrl()).contains('/community') .expect(getUrl()).contains('/community')
.click($('a[href="/pinned"]')) .click($('a[href="/pinned"]'))
.expect(getUrl()).contains('/pinned') .expect(getUrl()).contains('/pinned')
.expect($('.status-article').getAttribute('aria-posinset')).eql('0') .expect($('.status-article').getAttribute('aria-posinset')).eql('1')
.expect($('.status-article').getAttribute('aria-setsize')).eql('1') .expect($('.status-article').getAttribute('aria-setsize')).eql('1')
.expect($('.status-article .status-content').innerText).contains('this is unlisted') .expect($('.status-article .status-content').innerText).contains('this is unlisted')
}) })
@ -21,18 +21,18 @@ test("shows pinned statuses on a user's account page", async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.navigateTo('/accounts/2') .navigateTo('/accounts/2')
.expect(getNthPinnedStatus(0).getAttribute('aria-posinset')).eql('0') .expect(getNthPinnedStatus(1).getAttribute('aria-posinset')).eql('1')
.expect(getNthPinnedStatus(0).getAttribute('aria-setsize')).eql('1') .expect(getNthPinnedStatus(1).getAttribute('aria-setsize')).eql('1')
.expect(getNthPinnedStatus(0).innerText).contains('this is unlisted') .expect(getNthPinnedStatus(1).innerText).contains('this is unlisted')
}) })
test("shows pinned statuses on a user's account page 2", async t => { test("shows pinned statuses on a user's account page 2", async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.navigateTo('/accounts/3') .navigateTo('/accounts/3')
.expect(getNthPinnedStatus(0).getAttribute('aria-posinset')).eql('0') .expect(getNthPinnedStatus(1).getAttribute('aria-posinset')).eql('1')
.expect(getNthPinnedStatus(0).getAttribute('aria-setsize')).eql('2')
.expect(getNthPinnedStatus(0).innerText).contains('pinned toot 1')
.expect(getNthPinnedStatus(1).getAttribute('aria-setsize')).eql('2') .expect(getNthPinnedStatus(1).getAttribute('aria-setsize')).eql('2')
.expect(getNthPinnedStatus(1).innerText).contains('pinned toot 2') .expect(getNthPinnedStatus(1).innerText).contains('pinned toot 1')
.expect(getNthPinnedStatus(2).getAttribute('aria-setsize')).eql('2')
.expect(getNthPinnedStatus(2).innerText).contains('pinned toot 2')
}) })

View file

@ -8,20 +8,6 @@ fixture`005-status-types.js`
test('shows followers-only vs regular in home timeline', async t => { test('shows followers-only vs regular in home timeline', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.expect($(`${getNthStatusSelector(1)} .status-content`).innerText).contains('notification of unlisted message')
.expect($(`${getNthStatusSelector(1)} .status-toolbar button:nth-child(2)`).getAttribute('aria-label'))
.eql('Boost')
.expect($(`${getNthStatusSelector(1)} .status-toolbar button:nth-child(2)`).hasAttribute('disabled')).notOk()
.expect($(`${getNthStatusSelector(2)} .status-content`).innerText).contains('notification of followers-only message')
.expect($(`${getNthStatusSelector(2)} .status-toolbar button:nth-child(2)`).getAttribute('aria-label'))
.eql('Cannot be boosted because this is followers-only')
.expect($(`${getNthStatusSelector(2)} .status-toolbar button:nth-child(2)`).hasAttribute('disabled')).ok()
})
test('shows direct vs followers-only vs regular in notifications', async t => {
await loginAsFoobar(t)
await t
.navigateTo('/notifications')
.expect($(`${getNthStatusSelector(2)} .status-content`).innerText).contains('notification of unlisted message') .expect($(`${getNthStatusSelector(2)} .status-content`).innerText).contains('notification of unlisted message')
.expect($(`${getNthStatusSelector(2)} .status-toolbar button:nth-child(2)`).getAttribute('aria-label')) .expect($(`${getNthStatusSelector(2)} .status-toolbar button:nth-child(2)`).getAttribute('aria-label'))
.eql('Boost') .eql('Boost')
@ -30,8 +16,22 @@ test('shows direct vs followers-only vs regular in notifications', async t => {
.expect($(`${getNthStatusSelector(3)} .status-toolbar button:nth-child(2)`).getAttribute('aria-label')) .expect($(`${getNthStatusSelector(3)} .status-toolbar button:nth-child(2)`).getAttribute('aria-label'))
.eql('Cannot be boosted because this is followers-only') .eql('Cannot be boosted because this is followers-only')
.expect($(`${getNthStatusSelector(3)} .status-toolbar button:nth-child(2)`).hasAttribute('disabled')).ok() .expect($(`${getNthStatusSelector(3)} .status-toolbar button:nth-child(2)`).hasAttribute('disabled')).ok()
.expect($(`${getNthStatusSelector(4)} .status-content`).innerText).contains('notification of direct message') })
.expect($(`${getNthStatusSelector(4)} .status-toolbar button:nth-child(2)`).getAttribute('aria-label'))
.eql('Cannot be boosted because this is a direct message') test('shows direct vs followers-only vs regular in notifications', async t => {
.expect($(`${getNthStatusSelector(4)} .status-toolbar button:nth-child(2)`).hasAttribute('disabled')).ok() await loginAsFoobar(t)
await t
.navigateTo('/notifications')
.expect($(`${getNthStatusSelector(3)} .status-content`).innerText).contains('notification of unlisted message')
.expect($(`${getNthStatusSelector(3)} .status-toolbar button:nth-child(2)`).getAttribute('aria-label'))
.eql('Boost')
.expect($(`${getNthStatusSelector(3)} .status-toolbar button:nth-child(2)`).hasAttribute('disabled')).notOk()
.expect($(`${getNthStatusSelector(4)} .status-content`).innerText).contains('notification of followers-only message')
.expect($(`${getNthStatusSelector(4)} .status-toolbar button:nth-child(2)`).getAttribute('aria-label'))
.eql('Cannot be boosted because this is followers-only')
.expect($(`${getNthStatusSelector(4)} .status-toolbar button:nth-child(2)`).hasAttribute('disabled')).ok()
.expect($(`${getNthStatusSelector(5)} .status-content`).innerText).contains('notification of direct message')
.expect($(`${getNthStatusSelector(5)} .status-toolbar button:nth-child(2)`).getAttribute('aria-label'))
.eql('Cannot be boosted because this is a direct message')
.expect($(`${getNthStatusSelector(5)} .status-toolbar button:nth-child(2)`).hasAttribute('disabled')).ok()
}) })

View file

@ -8,28 +8,28 @@ fixture`006-tabindex.js`
test('shows correct tabindex in home timeline', async t => { test('shows correct tabindex in home timeline', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.expect(getNthStatus(0).getAttribute('tabindex')).eql('0')
.expect(getNthStatus(1).getAttribute('tabindex')).eql('0') .expect(getNthStatus(1).getAttribute('tabindex')).eql('0')
.expect(getNthStatus(2).getAttribute('tabindex')).eql('0') .expect(getNthStatus(2).getAttribute('tabindex')).eql('0')
.expect(getNthStatus(3).getAttribute('tabindex')).eql('0') .expect(getNthStatus(3).getAttribute('tabindex')).eql('0')
.expect(getNthStatus(4).getAttribute('tabindex')).eql('0')
}) })
test('shows correct tabindex in notifications', async t => { test('shows correct tabindex in notifications', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.navigateTo('/notifications') .navigateTo('/notifications')
.expect(getNthStatus(0).getAttribute('tabindex')).eql('0')
.expect(getNthStatus(1).getAttribute('tabindex')).eql('0') .expect(getNthStatus(1).getAttribute('tabindex')).eql('0')
.expect(getNthStatus(2).getAttribute('tabindex')).eql('0') .expect(getNthStatus(2).getAttribute('tabindex')).eql('0')
.hover(getNthStatus(2))
.expect(getNthStatus(3).getAttribute('tabindex')).eql('0') .expect(getNthStatus(3).getAttribute('tabindex')).eql('0')
.hover(getNthStatus(3))
.expect(getNthStatus(4).getAttribute('tabindex')).eql('0') .expect(getNthStatus(4).getAttribute('tabindex')).eql('0')
.hover(getNthStatus(4))
.expect(getNthStatus(5).getAttribute('tabindex')).eql('0') .expect(getNthStatus(5).getAttribute('tabindex')).eql('0')
.hover(getNthStatus(5))
.expect(getNthStatus(6).getAttribute('tabindex')).eql('0') .expect(getNthStatus(6).getAttribute('tabindex')).eql('0')
.hover(getNthStatus(6))
.expect(getNthStatus(7).getAttribute('tabindex')).eql('0') .expect(getNthStatus(7).getAttribute('tabindex')).eql('0')
.expect(getNthStatus(7).getAttribute('aria-setsize')).eql('8') .hover(getNthStatus(7))
.expect(getNthStatus(8).getAttribute('tabindex')).eql('0')
.expect(getNthStatus(8).getAttribute('aria-setsize')).eql('8')
}) })
test('shows correct tabindex in pinned statuses', async t => { test('shows correct tabindex in pinned statuses', async t => {
@ -37,6 +37,6 @@ test('shows correct tabindex in pinned statuses', async t => {
await t await t
.navigateTo('/pinned') .navigateTo('/pinned')
.expect($('.status-article').getAttribute('tabindex')).eql('0') .expect($('.status-article').getAttribute('tabindex')).eql('0')
.expect($('.status-article').getAttribute('aria-posinset')).eql('0') .expect($('.status-article').getAttribute('aria-posinset')).eql('1')
.expect($('.status-article').getAttribute('aria-setsize')).eql('1') .expect($('.status-article').getAttribute('aria-setsize')).eql('1')
}) })

View file

@ -52,8 +52,8 @@ test('shows account profile statuses', async t => {
.click($('.status-author-name').withText(('quux'))) .click($('.status-author-name').withText(('quux')))
.expect(getUrl()).contains('/accounts/3') .expect(getUrl()).contains('/accounts/3')
.expect($('.pinned-statuses .status-article').getAttribute('aria-setsize')).eql('2') .expect($('.pinned-statuses .status-article').getAttribute('aria-setsize')).eql('2')
.expect($('.pinned-statuses .status-article').getAttribute('aria-posinset')).eql('0') .expect($('.pinned-statuses .status-article').getAttribute('aria-posinset')).eql('1')
.expect($('.timeline .status-article').getAttribute('aria-posinset')).eql('0') .expect($('.timeline .status-article').getAttribute('aria-posinset')).eql('1')
await validateTimeline(t, quuxStatuses) await validateTimeline(t, quuxStatuses)
await t.expect($('.timeline .status-article').getAttribute('aria-setsize')).eql('27') await t.expect($('.timeline .status-article').getAttribute('aria-setsize')).eql('27')
}) })

View file

@ -13,15 +13,15 @@ test('shows sensitive images and videos', async t => {
let kittenIdx = indexWhere(homeTimeline, _ => _.spoiler === 'kitten CW') let kittenIdx = indexWhere(homeTimeline, _ => _.spoiler === 'kitten CW')
let videoIdx = indexWhere(homeTimeline, _ => _.content === 'secret video') let videoIdx = indexWhere(homeTimeline, _ => _.content === 'secret video')
await scrollToStatus(t, kittenIdx) await scrollToStatus(t, 1 + kittenIdx)
await t.expect($(`${getNthStatusSelector(kittenIdx)} .status-media img`).exists).notOk() await t.expect($(`${getNthStatusSelector(1 + kittenIdx)} .status-media img`).exists).notOk()
.click($(`${getNthStatusSelector(kittenIdx)} .status-sensitive-media-button`)) .click($(`${getNthStatusSelector(1 + kittenIdx)} .status-sensitive-media-button`))
.expect($(`${getNthStatusSelector(kittenIdx)} .status-media img`).getAttribute('alt')).eql('kitten') .expect($(`${getNthStatusSelector(1 + kittenIdx)} .status-media img`).getAttribute('alt')).eql('kitten')
.expect($(`${getNthStatusSelector(kittenIdx)} .status-media img`).hasAttribute('src')).ok() .expect($(`${getNthStatusSelector(1 + kittenIdx)} .status-media img`).hasAttribute('src')).ok()
.hover(getNthStatus(videoIdx)) .hover(getNthStatus(1 + videoIdx))
.expect($(`${getNthStatusSelector(videoIdx)} .status-media .play-video-button`).exists).notOk() .expect($(`${getNthStatusSelector(1 + videoIdx)} .status-media .play-video-button`).exists).notOk()
.click($(`${getNthStatusSelector(videoIdx)} .status-sensitive-media-button`)) .click($(`${getNthStatusSelector(1 + videoIdx)} .status-sensitive-media-button`))
.expect($(`${getNthStatusSelector(videoIdx)} .status-media .play-video-button`).exists).ok() .expect($(`${getNthStatusSelector(1 + videoIdx)} .status-media .play-video-button`).exists).ok()
}) })
test('click and close image and video modals', async t => { test('click and close image and video modals', async t => {
@ -30,17 +30,17 @@ test('click and close image and video modals', async t => {
let videoIdx = indexWhere(homeTimeline, _ => _.content === "here's a video") let videoIdx = indexWhere(homeTimeline, _ => _.content === "here's a video")
let kittenIdx = indexWhere(homeTimeline, _ => _.content === "here's an animated kitten gif") let kittenIdx = indexWhere(homeTimeline, _ => _.content === "here's an animated kitten gif")
await scrollToStatus(t, videoIdx) await scrollToStatus(t, 1 + videoIdx)
await t.expect(modalDialogContents.exists).notOk() await t.expect(modalDialogContents.exists).notOk()
.click($(`${getNthStatusSelector(videoIdx)} .play-video-button`)) .click($(`${getNthStatusSelector(1 + videoIdx)} .play-video-button`))
.expect(modalDialogContents.exists).ok() .expect(modalDialogContents.exists).ok()
.expect($('.modal-dialog video').getAttribute('src')).contains('mp4') .expect($('.modal-dialog video').getAttribute('src')).contains('mp4')
.expect($('.modal-dialog video').getAttribute('poster')).contains('png') .expect($('.modal-dialog video').getAttribute('poster')).contains('png')
.click(closeDialogButton) .click(closeDialogButton)
.expect(modalDialogContents.exists).notOk() .expect(modalDialogContents.exists).notOk()
.hover(getNthStatus(kittenIdx - 1)) .hover(getNthStatus(1 + kittenIdx - 1))
.hover(getNthStatus(kittenIdx)) .hover(getNthStatus(1 + kittenIdx))
.click($(`${getNthStatusSelector(kittenIdx)} .show-image-button`)) .click($(`${getNthStatusSelector(1 + kittenIdx)} .show-image-button`))
.expect(modalDialogContents.exists).ok() .expect(modalDialogContents.exists).ok()
.expect($('.modal-dialog video').getAttribute('src')).contains('mp4') .expect($('.modal-dialog video').getAttribute('src')).contains('mp4')
.expect($('.modal-dialog video').getAttribute('poster')).contains('png') .expect($('.modal-dialog video').getAttribute('poster')).contains('png')

View file

@ -14,31 +14,31 @@ test('Shows a thread', async t => {
await t await t
.click($('a').withText('quux')) .click($('a').withText('quux'))
await scrollToStatus(t, 26) await scrollToStatus(t, 27)
await t await t
.hover(getNthStatus(26)) .hover(getNthStatus(27))
.click(getNthStatus(26)) .click(getNthStatus(27))
.expect(getUrl()).contains('/statuses/') .expect(getUrl()).contains('/statuses/')
await validateTimeline(t, quuxThread) await validateTimeline(t, quuxThread)
await t.expect(getNthStatus(24).getAttribute('aria-setsize')).eql('25') await t.expect(getNthStatus(25).getAttribute('aria-setsize')).eql('25')
}) })
test('Scrolls to proper point in thread', async t => { test('Scrolls to proper point in thread', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.click($('a').withText('quux')) .click($('a').withText('quux'))
.hover(getNthStatus(0)) .hover(getNthStatus(1))
.hover(getNthStatus(2)) .hover(getNthStatus(3))
.hover(getNthStatus(4)) .hover(getNthStatus(5))
.hover(getNthStatus(6)) .hover(getNthStatus(7))
.hover(getNthStatus(8)) .hover(getNthStatus(9))
.hover(getNthStatus(10)) .hover(getNthStatus(11))
.click(getNthStatus(10)) .click(getNthStatus(11))
.expect(getUrl()).contains('/statuses/') .expect(getUrl()).contains('/statuses/')
.expect(getNthStatus(16).innerText).contains('unlisted thread 17') .expect(getNthStatus(17).innerText).contains('unlisted thread 17')
.expect(Math.round(getNthStatus(16).boundingClientRect.top)) .expect(Math.round(getNthStatus(17).boundingClientRect.top))
.eql(Math.round($('.main-content').boundingClientRect.top)) .eql(Math.round($('.main-content').boundingClientRect.top))
}) })
@ -52,21 +52,21 @@ async function navigateToBazAccount (t) {
} }
async function validateForkedThread (t) { async function validateForkedThread (t) {
await t.hover(getNthStatus(1)) await t.hover(getNthStatus(2))
.click(getNthStatus(2)) .click(getNthStatus(3))
.expect(getUrl()).contains('/statuses') .expect(getUrl()).contains('/statuses')
await validateTimeline(t, bazThreadRelativeTo2B2) await validateTimeline(t, bazThreadRelativeTo2B2)
await goBack() await goBack()
await t.hover(getNthStatus(3)) await t.hover(getNthStatus(4))
.hover(getNthStatus(5)) .hover(getNthStatus(6))
.hover(getNthStatus(7)) .hover(getNthStatus(8))
.hover(getNthStatus(9)) .hover(getNthStatus(10))
.click(getNthStatus(9)) .click(getNthStatus(10))
.expect(getUrl()).contains('/statuses') .expect(getUrl()).contains('/statuses')
await validateTimeline(t, bazThreadRelativeTo2b) await validateTimeline(t, bazThreadRelativeTo2b)
await goBack() await goBack()
await t.hover(getNthStatus(11)) await t.hover(getNthStatus(12))
.click(getNthStatus(11)) .click(getNthStatus(12))
.expect(getUrl()).contains('/statuses') .expect(getUrl()).contains('/statuses')
await validateTimeline(t, bazThreadRelativeTo2) await validateTimeline(t, bazThreadRelativeTo2)
} }

View file

@ -16,62 +16,62 @@ test('modal preserves focus', async t => {
let idx = indexWhere(homeTimeline, _ => _.content === "here's a video") let idx = indexWhere(homeTimeline, _ => _.content === "here's a video")
await scrollToStatus(t, idx) await scrollToStatus(t, 1 + idx)
// explicitly hover-focus-click // explicitly hover-focus-click
await t.hover($(`${getNthStatusSelector(idx)} .play-video-button`)) await t.hover($(`${getNthStatusSelector(1 + idx)} .play-video-button`))
await focus(`${getNthStatusSelector(idx)} .play-video-button`)() await focus(`${getNthStatusSelector(1 + idx)} .play-video-button`)()
await t.click($(`${getNthStatusSelector(idx)} .play-video-button`)) await t.click($(`${getNthStatusSelector(1 + idx)} .play-video-button`))
.click(closeDialogButton) .click(closeDialogButton)
.expect(modalDialogContents.exists).notOk() .expect(modalDialogContents.exists).notOk()
.expect(getActiveElementClassList()).contains('play-video-button') .expect(getActiveElementClassList()).contains('play-video-button')
.expect(getActiveElementInsideNthStatus()).eql(idx.toString()) .expect(getActiveElementInsideNthStatus()).eql((idx + 1).toString())
}) })
test('timeline preserves focus', async t => { test('timeline preserves focus', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
// explicitly hover-focus-click // explicitly hover-focus-click
await t.hover(getNthStatus(0)) await t.hover(getNthStatus(1))
await focus(getNthStatusSelector(0))() await focus(getNthStatusSelector(1))()
await t.click(getNthStatus(0)) await t.click(getNthStatus(1))
.expect(getUrl()).contains('/statuses/') .expect(getUrl()).contains('/statuses/')
await goBack() await goBack()
await t.expect(getUrl()).eql('http://localhost:4002/') await t.expect(getUrl()).eql('http://localhost:4002/')
.expect(getActiveElementClassList()).contains('status-article') .expect(getActiveElementClassList()).contains('status-article')
.expect(getActiveElementClassList()).contains('status-in-timeline') .expect(getActiveElementClassList()).contains('status-in-timeline')
.expect(getActiveElementInsideNthStatus()).eql('0') .expect(getActiveElementInsideNthStatus()).eql('1')
}) })
test('timeline link preserves focus', async t => { test('timeline link preserves focus', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.expect(getNthStatus(0).exists).ok({ timeout: 20000 }) .expect(getNthStatus(1).exists).ok({ timeout: 20000 })
.click($(`${getNthStatusSelector(0)} .status-header a`)) .click($(`${getNthStatusSelector(1)} .status-header a`))
.expect(getUrl()).contains('/accounts/') .expect(getUrl()).contains('/accounts/')
.click(goBackButton) .click(goBackButton)
.expect(getUrl()).eql('http://localhost:4002/') .expect(getUrl()).eql('http://localhost:4002/')
.expect(getNthStatus(0).exists).ok() .expect(getNthStatus(1).exists).ok()
.expect(getActiveElementInnerText()).eql('admin') .expect(getActiveElementInnerText()).eql('admin')
.click($(`${getNthStatusSelector(0)} .status-sidebar`)) .click($(`${getNthStatusSelector(1)} .status-sidebar`))
.expect(getUrl()).contains('/accounts/') .expect(getUrl()).contains('/accounts/')
.click(goBackButton) .click(goBackButton)
.expect(getUrl()).eql('http://localhost:4002/') .expect(getUrl()).eql('http://localhost:4002/')
.expect(getActiveElementClassList()).contains('status-sidebar') .expect(getActiveElementClassList()).contains('status-sidebar')
.expect(getActiveElementInsideNthStatus()).eql('0') .expect(getActiveElementInsideNthStatus()).eql('1')
}) })
test('notification timeline preserves focus', async t => { test('notification timeline preserves focus', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.navigateTo('/notifications') .navigateTo('/notifications')
await scrollToStatus(t, 5) await scrollToStatus(t, 6)
await t.click($(`${getNthStatusSelector(5)} .status-header a`)) await t.click($(`${getNthStatusSelector(6)} .status-header a`))
.expect(getUrl()).contains('/accounts/') .expect(getUrl()).contains('/accounts/')
.click(goBackButton) .click(goBackButton)
.expect(getUrl()).contains('/notifications') .expect(getUrl()).contains('/notifications')
.expect(getNthStatus(0).exists).ok() .expect(getNthStatus(1).exists).ok()
.expect(getActiveElementInnerText()).eql('quux') .expect(getActiveElementInnerText()).eql('quux')
.expect(getActiveElementInsideNthStatus()).eql('5') .expect(getActiveElementInsideNthStatus()).eql('6')
}) })
test('thread preserves focus', async t => { test('thread preserves focus', async t => {
@ -80,33 +80,33 @@ test('thread preserves focus', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.navigateTo('/accounts/3') .navigateTo('/accounts/3')
.expect(getNthStatus(0).exists).ok({ timeout }) .expect(getNthStatus(1).exists).ok({ timeout })
.hover(getNthStatus(0)) .hover(getNthStatus(1))
await scrollToStatus(t, 2) await scrollToStatus(t, 3)
await t.click(getNthStatus(2)) await t.click(getNthStatus(3))
.expect(getUrl()).contains('/statuses/') .expect(getUrl()).contains('/statuses/')
.click($(`${getNthStatusSelector(24)} .status-sidebar`)) .click($(`${getNthStatusSelector(25)} .status-sidebar`))
.expect(getUrl()).contains('/accounts/') .expect(getUrl()).contains('/accounts/')
.click(goBackButton) .click(goBackButton)
.expect(getUrl()).contains('/statuses/') .expect(getUrl()).contains('/statuses/')
.expect(getNthStatus(24).exists).ok() .expect(getNthStatus(25).exists).ok()
.expect(getActiveElementClassList()).contains('status-sidebar') .expect(getActiveElementClassList()).contains('status-sidebar')
.expect(getActiveElementInsideNthStatus()).eql('24') .expect(getActiveElementInsideNthStatus()).eql('25')
.hover(getNthStatus(23)) .hover(getNthStatus(24))
.click(getNthStatus(23)) .click(getNthStatus(24))
.expect($(`${getNthStatusSelector(23)} .status-absolute-date`).exists).ok({ timeout }) .expect($(`${getNthStatusSelector(24)} .status-absolute-date`).exists).ok({ timeout })
await goBack() await goBack()
await t.expect($(`${getNthStatusSelector(24)} .status-absolute-date`).exists).ok({ timeout }) await t.expect($(`${getNthStatusSelector(25)} .status-absolute-date`).exists).ok({ timeout })
.expect(getActiveElementClassList()).contains('status-article', { timeout }) .expect(getActiveElementClassList()).contains('status-article', { timeout })
.expect(getActiveElementClassList()).contains('status-in-timeline', { timeout }) .expect(getActiveElementClassList()).contains('status-in-timeline', { timeout })
.expect(getActiveElementInsideNthStatus()).eql('23', { timeout }) .expect(getActiveElementInsideNthStatus()).eql('24', { timeout })
}) })
test('reply preserves focus and moves focus to the text input', async t => { test('reply preserves focus and moves focus to the text input', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.expect(getNthStatus(1).exists).ok({ timeout: 20000 }) .expect(getNthStatus(2).exists).ok({ timeout: 20000 })
.click(getNthReplyButton(1)) .click(getNthReplyButton(2))
.expect(getActiveElementClassList()).contains('compose-box-input') .expect(getActiveElementClassList()).contains('compose-box-input')
}) })

View file

@ -11,7 +11,7 @@ fixture`011-reblog-favorites-count.js`
test('shows favorites', async t => { test('shows favorites', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.click(getNthStatus(0)) .click(getNthStatus(1))
.expect(getUrl()).contains('/statuses/') .expect(getUrl()).contains('/statuses/')
.expect(getFavoritesCount()).eql(2) .expect(getFavoritesCount()).eql(2)
.expect(favoritesCountElement.getAttribute('aria-label')).eql('Favorited 2 times') .expect(favoritesCountElement.getAttribute('aria-label')).eql('Favorited 2 times')
@ -27,7 +27,7 @@ test('shows favorites', async t => {
test('shows boosts', async t => { test('shows boosts', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.click(getNthStatus(0)) .click(getNthStatus(1))
.expect(getUrl()).contains('/statuses/') .expect(getUrl()).contains('/statuses/')
.expect(getReblogsCount()).eql(1) .expect(getReblogsCount()).eql(1)
.expect(reblogsCountElement.getAttribute('aria-label')).eql('Boosted 1 time') .expect(reblogsCountElement.getAttribute('aria-label')).eql('Boosted 1 time')

View file

@ -120,9 +120,9 @@ test('inserts native emoji without typing anything', async t => {
test('cannot post an empty status', async t => { test('cannot post an empty status', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.expect(getNthStatusContent(0).innerText).contains('pinned toot 1') .expect(getNthStatusContent(1).innerText).contains('pinned toot 1')
.click(composeButton) .click(composeButton)
await sleep(2) await sleep(2)
await t await t
.expect(getNthStatusContent(0).innerText).contains('pinned toot 1') .expect(getNthStatusContent(1).innerText).contains('pinned toot 1')
}) })

View file

@ -6,7 +6,7 @@ fixture`016-external-links.js`
.page`http://localhost:4002` .page`http://localhost:4002`
function getAnchor (nthStatus, nthAnchor) { function getAnchor (nthStatus, nthAnchor) {
return $(`${getNthStatusSelector(nthStatus)} .status-content a`).nth(nthAnchor) return $(`${getNthStatusSelector(1 + nthStatus)} .status-content a`).nth(nthAnchor)
} }
function getAnchorInProfile (n) { function getAnchorInProfile (n) {
@ -16,7 +16,7 @@ function getAnchorInProfile (n) {
test('converts external links in statuses', async t => { test('converts external links in statuses', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.hover(getNthStatus(0)) .hover(getNthStatus(1))
.navigateTo('/accounts/4') .navigateTo('/accounts/4')
.expect(getUrl()).contains('/accounts/4') .expect(getUrl()).contains('/accounts/4')
.expect(getAnchor(0, 0).getAttribute('href')).eql('/accounts/1') .expect(getAnchor(0, 0).getAttribute('href')).eql('/accounts/1')
@ -34,7 +34,7 @@ test('converts external links in statuses', async t => {
test('converts external links in profiles', async t => { test('converts external links in profiles', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.hover(getNthStatus(0)) .hover(getNthStatus(1))
.navigateTo('/accounts/4') .navigateTo('/accounts/4')
.expect(getUrl()).contains('/accounts/4') .expect(getUrl()).contains('/accounts/4')
.expect($('.account-profile-name').innerText).contains('External Lonk') .expect($('.account-profile-name').innerText).contains('External Lonk')

View file

@ -15,118 +15,118 @@ fixture`017-compose-reply.js`
test('account handle populated correctly for replies', async t => { test('account handle populated correctly for replies', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.click(getNthReplyButton(0)) .click(getNthReplyButton(1))
.expect(getNthComposeReplyInput(0).value).eql('@quux ') .expect(getNthComposeReplyInput(1).value).eql('@quux ')
.typeText(getNthComposeReplyInput(0), 'hello quux', { paste: true }) .typeText(getNthComposeReplyInput(1), 'hello quux', { paste: true })
.expect(getNthComposeReplyInput(0).value).eql('@quux hello quux') .expect(getNthComposeReplyInput(1).value).eql('@quux hello quux')
.click(notificationsNavButton) .click(notificationsNavButton)
.expect(getUrl()).contains('/notifications') .expect(getUrl()).contains('/notifications')
.click(homeNavButton) .click(homeNavButton)
.expect(getUrl()).notContains('/notifications') .expect(getUrl()).notContains('/notifications')
.expect(getNthComposeReplyInput(0).value).eql('@quux hello quux') .expect(getNthComposeReplyInput(1).value).eql('@quux hello quux')
.expect(composeInput.value).eql('') .expect(composeInput.value).eql('')
.hover(getNthStatus(2)) .hover(getNthStatus(3))
.hover(getNthStatus(4)) .hover(getNthStatus(5))
.click(getNthReplyButton(4)) .click(getNthReplyButton(5))
.expect(getNthComposeReplyInput(4).value).eql('') .expect(getNthComposeReplyInput(5).value).eql('')
}) })
test('replying to posts with mentions', async t => { test('replying to posts with mentions', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.click(getNthReplyButton(1)) .click(getNthReplyButton(2))
.expect(getNthComposeReplyInput(1).value).eql('@admin ') .expect(getNthComposeReplyInput(2).value).eql('@admin ')
.navigateTo('/accounts/4') .navigateTo('/accounts/4')
.click(getNthReplyButton(0)) .click(getNthReplyButton(1))
.expect(getNthComposeReplyInput(0).value).eql('@ExternalLinks @admin @quux ') .expect(getNthComposeReplyInput(1).value).eql('@ExternalLinks @admin @quux ')
}) })
test('replies have same privacy as replied-to status by default', async t => { test('replies have same privacy as replied-to status by default', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.hover(getNthStatus(0))
.hover(getNthStatus(1)) .hover(getNthStatus(1))
.click(getNthReplyButton(1))
.expect(getNthPostPrivacyButton(1).getAttribute('aria-label')).eql('Adjust privacy (currently Unlisted)')
.click(getNthReplyButton(1))
.hover(getNthStatus(2)) .hover(getNthStatus(2))
.click(getNthReplyButton(2)) .click(getNthReplyButton(2))
.expect(getNthPostPrivacyButton(2).getAttribute('aria-label')).eql('Adjust privacy (currently Followers-only)') .expect(getNthPostPrivacyButton(2).getAttribute('aria-label')).eql('Adjust privacy (currently Unlisted)')
.click(getNthReplyButton(2)) .click(getNthReplyButton(2))
.hover(getNthStatus(3)) .hover(getNthStatus(3))
.click(getNthReplyButton(3))
.expect(getNthPostPrivacyButton(3).getAttribute('aria-label')).eql('Adjust privacy (currently Followers-only)')
.click(getNthReplyButton(3))
.hover(getNthStatus(4)) .hover(getNthStatus(4))
.hover(getNthStatus(5)) .hover(getNthStatus(5))
.click(getNthReplyButton(5)) .hover(getNthStatus(6))
.expect(getNthPostPrivacyButton(5).getAttribute('aria-label')).eql('Adjust privacy (currently Public)') .click(getNthReplyButton(6))
.click(getNthReplyButton(5)) .expect(getNthPostPrivacyButton(6).getAttribute('aria-label')).eql('Adjust privacy (currently Public)')
.click(getNthReplyButton(6))
}) })
test('replies have same CW as replied-to status', async t => { test('replies have same CW as replied-to status', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
let kittenIdx = indexWhere(homeTimeline, _ => _.spoiler === 'kitten CW') let kittenIdx = indexWhere(homeTimeline, _ => _.spoiler === 'kitten CW')
await scrollToStatus(t, kittenIdx) await scrollToStatus(t, 1 + kittenIdx)
await t.click(getNthReplyButton(kittenIdx)) await t.click(getNthReplyButton(1 + kittenIdx))
.expect(getNthReplyContentWarningInput(kittenIdx).value).eql('kitten CW') .expect(getNthReplyContentWarningInput(1 + kittenIdx).value).eql('kitten CW')
.click(getNthStatusRelativeDate(kittenIdx)) .click(getNthStatusRelativeDate(1 + kittenIdx))
.expect(getUrl()).contains('/statuses') .expect(getUrl()).contains('/statuses')
.click(getNthReplyButton(0)) .click(getNthReplyButton(1))
.expect(getNthReplyContentWarningInput(0).value).eql('kitten CW') .expect(getNthReplyContentWarningInput(1).value).eql('kitten CW')
}) })
test('replies save deletions of CW', async t => { test('replies save deletions of CW', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
let kittenIdx = indexWhere(homeTimeline, _ => _.spoiler === 'kitten CW') let kittenIdx = indexWhere(homeTimeline, _ => _.spoiler === 'kitten CW')
await scrollToStatus(t, kittenIdx) await scrollToStatus(t, 1 + kittenIdx)
await t.click(getNthReplyButton(kittenIdx)) await t.click(getNthReplyButton(1 + kittenIdx))
.expect(getNthReplyContentWarningInput(kittenIdx).value).eql('kitten CW') .expect(getNthReplyContentWarningInput(1 + kittenIdx).value).eql('kitten CW')
.click(getNthReplyContentWarningButton(kittenIdx)) .click(getNthReplyContentWarningButton(1 + kittenIdx))
.expect(getNthReplyContentWarningInput(kittenIdx).exists).notOk() .expect(getNthReplyContentWarningInput(1 + kittenIdx).exists).notOk()
.click(getNthStatusRelativeDate(kittenIdx)) .click(getNthStatusRelativeDate(1 + kittenIdx))
.expect(getUrl()).contains('/statuses') .expect(getUrl()).contains('/statuses')
.click(getNthReplyButton(0)) .click(getNthReplyButton(1))
.expect(getNthReplyContentWarningInput(0).exists).notOk() .expect(getNthReplyContentWarningInput(1).exists).notOk()
}) })
test('replies save changes to CW', async t => { test('replies save changes to CW', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
let kittenIdx = indexWhere(homeTimeline, _ => _.spoiler === 'kitten CW') let kittenIdx = indexWhere(homeTimeline, _ => _.spoiler === 'kitten CW')
await scrollToStatus(t, kittenIdx) await scrollToStatus(t, 1 + kittenIdx)
await t.click(getNthReplyButton(kittenIdx)) await t.click(getNthReplyButton(1 + kittenIdx))
.expect(getNthReplyContentWarningInput(kittenIdx).value).eql('kitten CW') .expect(getNthReplyContentWarningInput(1 + kittenIdx).value).eql('kitten CW')
.typeText(getNthReplyContentWarningInput(kittenIdx), ' yolo', { paste: true }) .typeText(getNthReplyContentWarningInput(1 + kittenIdx), ' yolo', { paste: true })
.expect(getNthReplyContentWarningInput(kittenIdx).value).eql('kitten CW yolo') .expect(getNthReplyContentWarningInput(1 + kittenIdx).value).eql('kitten CW yolo')
.click(getNthStatusRelativeDate(kittenIdx)) .click(getNthStatusRelativeDate(1 + kittenIdx))
.expect(getUrl()).contains('/statuses') .expect(getUrl()).contains('/statuses')
.click(getNthReplyButton(0)) .click(getNthReplyButton(1))
.expect(getNthReplyContentWarningInput(0).value).eql('kitten CW yolo') .expect(getNthReplyContentWarningInput(1).value).eql('kitten CW yolo')
}) })
test('replies save changes to post privacy', async t => { test('replies save changes to post privacy', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.hover(getNthStatus(0))
.hover(getNthStatus(1)) .hover(getNthStatus(1))
.click(getNthReplyButton(1)) .hover(getNthStatus(2))
.expect(getNthPostPrivacyButton(1).getAttribute('aria-label')).eql('Adjust privacy (currently Unlisted)') .click(getNthReplyButton(2))
.click(getNthReplyPostPrivacyButton(1)) .expect(getNthPostPrivacyButton(2).getAttribute('aria-label')).eql('Adjust privacy (currently Unlisted)')
.click(getNthReplyPostPrivacyButton(2))
.click(getNthPostPrivacyOptionInDialog(1)) .click(getNthPostPrivacyOptionInDialog(1))
.expect(getNthPostPrivacyButton(1).getAttribute('aria-label')).eql('Adjust privacy (currently Public)') .expect(getNthPostPrivacyButton(2).getAttribute('aria-label')).eql('Adjust privacy (currently Public)')
.click(getNthStatusRelativeDate(1)) .click(getNthStatusRelativeDate(2))
.expect(getUrl()).contains('/statuses') .expect(getUrl()).contains('/statuses')
.click(getNthReplyButton(0)) .click(getNthReplyButton(1))
.expect(getNthPostPrivacyButton(0).getAttribute('aria-label')).eql('Adjust privacy (currently Public)') .expect(getNthPostPrivacyButton(1).getAttribute('aria-label')).eql('Adjust privacy (currently Public)')
}) })
test('replies are the same whatever thread they are in', async t => { test('replies are the same whatever thread they are in', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.hover(getNthStatus(0))
.hover(getNthStatus(1)) .hover(getNthStatus(1))
.click(getNthReplyButton(1)) .hover(getNthStatus(2))
.typeText(getNthComposeReplyInput(1), 'this is a reply', { paste: true }) .click(getNthReplyButton(2))
.expect(getNthComposeReplyInput(1).value).eql('@admin this is a reply') .typeText(getNthComposeReplyInput(2), 'this is a reply', { paste: true })
.click(getNthStatusRelativeDate(1)) .expect(getNthComposeReplyInput(2).value).eql('@admin this is a reply')
.click(getNthStatusRelativeDate(2))
.expect(getUrl()).contains('/statuses') .expect(getUrl()).contains('/statuses')
.click(getNthReplyButton(0)) .click(getNthReplyButton(1))
.expect(getNthComposeReplyInput(0).value).eql('@admin this is a reply') .expect(getNthComposeReplyInput(1).value).eql('@admin this is a reply')
}) })

View file

@ -97,11 +97,11 @@ test('autosuggest only shows for one input', async t => {
await t await t
.hover(composeInput) .hover(composeInput)
.typeText(composeInput, '@quu') .typeText(composeInput, '@quu')
.hover(getNthStatus(0)) .hover(getNthStatus(1))
.click(getNthReplyButton(0)) .click(getNthReplyButton(1))
.selectText(getNthComposeReplyInput(0)) .selectText(getNthComposeReplyInput(1))
.pressKey('delete') .pressKey('delete')
.typeText(getNthComposeReplyInput(0), 'uu') .typeText(getNthComposeReplyInput(1), 'uu')
.expect($('.compose-autosuggest.shown').exists).notOk() .expect($('.compose-autosuggest.shown').exists).notOk()
}) })
@ -112,11 +112,11 @@ test('autosuggest only shows for one input part 2', async t => {
.typeText(composeInput, '@adm') .typeText(composeInput, '@adm')
.expect($('.compose-autosuggest.shown').exists).ok({ timeout }) .expect($('.compose-autosuggest.shown').exists).ok({ timeout })
.expect(getNthAutosuggestionResult(1).innerText).contains('@admin') .expect(getNthAutosuggestionResult(1).innerText).contains('@admin')
.hover(getNthStatus(0)) .hover(getNthStatus(1))
.click(getNthReplyButton(0)) .click(getNthReplyButton(1))
.selectText(getNthComposeReplyInput(0)) .selectText(getNthComposeReplyInput(1))
.pressKey('delete') .pressKey('delete')
.typeText(getNthComposeReplyInput(0), '@dd') .typeText(getNthComposeReplyInput(1), '@dd')
await sleep(1000) await sleep(1000)
await t.pressKey('backspace') await t.pressKey('backspace')
.expect($('.compose-autosuggest.shown').exists).notOk() .expect($('.compose-autosuggest.shown').exists).notOk()

View file

@ -12,7 +12,7 @@ fixture`021-followers-follows.js`
test('shows followers and follows', async t => { test('shows followers and follows', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.click($(`${getNthStatusSelector(0)} .status-author-name`)) .click($(`${getNthStatusSelector(1)} .status-author-name`))
.expect(getUrl()).match(/\/accounts\/3$/) .expect(getUrl()).match(/\/accounts\/3$/)
.expect(followsButton.getAttribute('aria-label')).eql('Follows 2') .expect(followsButton.getAttribute('aria-label')).eql('Follows 2')
.click(followsButton) .click(followsButton)

View file

@ -17,12 +17,12 @@ fixture`022-status-aria-label.js`
test('basic aria-labels for statuses', async t => { test('basic aria-labels for statuses', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.hover(getNthStatus(0)) .hover(getNthStatus(1))
.expect(getNthStatus(0).getAttribute('aria-label')).match( .expect(getNthStatus(1).getAttribute('aria-label')).match(
/quux, pinned toot 1, .+ ago, @quux, Unlisted, Boosted by admin/i /quux, pinned toot 1, .+ ago, @quux, Unlisted, Boosted by admin/i
) )
.hover(getNthStatus(0)) .hover(getNthStatus(1))
.expect(getNthStatus(1).getAttribute('aria-label')).match( .expect(getNthStatus(2).getAttribute('aria-label')).match(
/admin, @foobar notification of unlisted message, .* ago, @admin, Unlisted/i /admin, @foobar notification of unlisted message, .* ago, @admin, Unlisted/i
) )
}) })
@ -30,18 +30,18 @@ test('basic aria-labels for statuses', async t => {
test('aria-labels for CWed statuses', async t => { test('aria-labels for CWed statuses', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
let kittenIdx = indexWhere(homeTimeline, _ => _.spoiler === 'kitten CW') let kittenIdx = indexWhere(homeTimeline, _ => _.spoiler === 'kitten CW')
await scrollToStatus(t, kittenIdx) await scrollToStatus(t, 1 + kittenIdx)
await t await t
.hover(getNthStatus(kittenIdx)) .hover(getNthStatus(1 + kittenIdx))
.expect(getNthStatus(kittenIdx).getAttribute('aria-label')).match( .expect(getNthStatus(1 + kittenIdx).getAttribute('aria-label')).match(
/foobar, Content warning: kitten CW, .* ago, @foobar, Public/i /foobar, Content warning: kitten CW, .* ago, @foobar, Public/i
) )
.click(getNthShowOrHideButton(kittenIdx)) .click(getNthShowOrHideButton(1 + kittenIdx))
.expect(getNthStatus(kittenIdx).getAttribute('aria-label')).match( .expect(getNthStatus(1 + kittenIdx).getAttribute('aria-label')).match(
/foobar, here's a kitten with a CW, .* ago, @foobar, Public/i /foobar, here's a kitten with a CW, .* ago, @foobar, Public/i
) )
.click(getNthShowOrHideButton(kittenIdx)) .click(getNthShowOrHideButton(1 + kittenIdx))
.expect(getNthStatus(kittenIdx).getAttribute('aria-label')).match( .expect(getNthStatus(1 + kittenIdx).getAttribute('aria-label')).match(
/foobar, Content warning: kitten CW, .* ago, @foobar, Public/i /foobar, Content warning: kitten CW, .* ago, @foobar, Public/i
) )
}) })
@ -50,28 +50,28 @@ test('aria-labels for notifications', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.click(notificationsNavButton) .click(notificationsNavButton)
.hover(getNthStatus(0))
.expect(getNthStatus(0).getAttribute('aria-label')).match(
/admin favorited your status, foobar, this is unlisted, .* ago, @foobar, Unlisted/i
)
.hover(getNthStatus(1)) .hover(getNthStatus(1))
.expect(getNthStatus(1).getAttribute('aria-label')).match( .expect(getNthStatus(1).getAttribute('aria-label')).match(
/admin boosted your status, foobar, this is unlisted, .* ago, @foobar, Unlisted/i /admin favorited your status, foobar, this is unlisted, .* ago, @foobar, Unlisted/i
) )
.hover(getNthStatus(2)) .hover(getNthStatus(2))
.expect(getNthStatus(2).getAttribute('aria-label')).match( .expect(getNthStatus(2).getAttribute('aria-label')).match(
/admin, @foobar notification of unlisted message, .* ago, @admin, Unlisted/i /admin boosted your status, foobar, this is unlisted, .* ago, @foobar, Unlisted/i
) )
await scrollToStatus(t, 4) .hover(getNthStatus(3))
await t .expect(getNthStatus(3).getAttribute('aria-label')).match(
.hover(getNthStatus(4)) /admin, @foobar notification of unlisted message, .* ago, @admin, Unlisted/i
.expect(getNthStatus(4).getAttribute('aria-label')).match(
/admin, @foobar notification of direct message, .* ago, @admin, Direct/i
) )
await scrollToStatus(t, 5) await scrollToStatus(t, 5)
await t await t
.hover(getNthStatus(5)) .hover(getNthStatus(5))
.expect(getNthStatus(5).getAttribute('aria-label')).match( .expect(getNthStatus(5).getAttribute('aria-label')).match(
/admin, @foobar notification of direct message, .* ago, @admin, Direct/i
)
await scrollToStatus(t, 6)
await t
.hover(getNthStatus(6))
.expect(getNthStatus(6).getAttribute('aria-label')).match(
/quux followed you, @quux/i /quux followed you, @quux/i
) )
}) })
@ -83,16 +83,16 @@ test('can shorten aria-labels', async t => {
.click(generalSettingsButton) .click(generalSettingsButton)
.click($('#choice-disable-long-aria-labels')) .click($('#choice-disable-long-aria-labels'))
.click(homeNavButton) .click(homeNavButton)
.hover(getNthStatus(0)) .hover(getNthStatus(1))
.expect(getNthStatus(0).getAttribute('aria-label')).match( .expect(getNthStatus(1).getAttribute('aria-label')).match(
/Unlisted status by quux/ /Unlisted status by quux/
) )
.click(settingsNavButton) .click(settingsNavButton)
.click(generalSettingsButton) .click(generalSettingsButton)
.click($('#choice-disable-long-aria-labels')) .click($('#choice-disable-long-aria-labels'))
.click(homeNavButton) .click(homeNavButton)
.hover(getNthStatus(0)) .hover(getNthStatus(1))
.expect(getNthStatus(0).getAttribute('aria-label')).match( .expect(getNthStatus(1).getAttribute('aria-label')).match(
/quux, pinned toot 1, .+ ago, @quux, Unlisted, Boosted by admin/i /quux, pinned toot 1, .+ ago, @quux, Unlisted, Boosted by admin/i
) )
}) })

View file

@ -13,12 +13,12 @@ fixture`023-mark-media-as-sensitive.js`
async function checkSensitivityForStatus (t, idx, sensitive) { async function checkSensitivityForStatus (t, idx, sensitive) {
if (sensitive) { if (sensitive) {
await t await t
.expect(getNthStatusSensitiveMediaButton(idx).exists).ok() .expect(getNthStatusSensitiveMediaButton(1 + idx).exists).ok()
.expect(getNthStatusMedia(idx).exists).notOk() .expect(getNthStatusMedia(1 + idx).exists).notOk()
} else { } else {
await t await t
.expect(getNthStatusSensitiveMediaButton(idx).exists).notOk() .expect(getNthStatusSensitiveMediaButton(1 + idx).exists).notOk()
.expect(getNthStatusMedia(idx).exists).ok() .expect(getNthStatusMedia(1 + idx).exists).ok()
} }
} }
@ -29,7 +29,7 @@ async function checkSensitivity (t, shouldBeSensitive) {
let sensitiveAnimatedKittenIdx = indexWhere(homeTimeline, _ => _.content === "here's a secret animated kitten gif") let sensitiveAnimatedKittenIdx = indexWhere(homeTimeline, _ => _.content === "here's a secret animated kitten gif")
let animatedKittenIdx = indexWhere(homeTimeline, _ => _.content === "here's an animated kitten gif") let animatedKittenIdx = indexWhere(homeTimeline, _ => _.content === "here's an animated kitten gif")
await t.hover(getNthStatus(0)) await t.hover(getNthStatus(1))
let expected = [ let expected = [
[ sensitiveKittenIdx, shouldBeSensitive(true) ], [ sensitiveKittenIdx, shouldBeSensitive(true) ],
@ -40,7 +40,7 @@ async function checkSensitivity (t, shouldBeSensitive) {
] ]
for (let [ idx, sensitive ] of expected) { for (let [ idx, sensitive ] of expected) {
await scrollToStatus(t, sensitiveKittenIdx) await scrollToStatus(t, 1 + sensitiveKittenIdx)
await checkSensitivityForStatus(t, idx, sensitive) await checkSensitivityForStatus(t, idx, sensitive)
} }
} }

View file

@ -22,9 +22,9 @@ fixture`025-shortcuts-status.js`
async function activateStatus (t, idx) { async function activateStatus (t, idx) {
let timeout = 20000 let timeout = 20000
for (let i = 0; i <= idx; i++) { for (let i = 0; i <= idx; i++) {
await t.expect(getNthStatus(i).exists).ok({ timeout }) await t.expect(getNthStatus(1 + i).exists).ok({ timeout })
.pressKey('j') .pressKey('j')
.expect(isNthStatusActive(i)()).ok() .expect(isNthStatusActive(1 + i)()).ok()
} }
} }
@ -32,34 +32,34 @@ test('Shortcut j/k change the active status', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.expect(getUrl()).eql('http://localhost:4002/') .expect(getUrl()).eql('http://localhost:4002/')
.expect(getNthStatus(0).exists).ok({ timeout: 30000 }) .expect(getNthStatus(1).exists).ok({ timeout: 30000 })
.expect(isNthStatusActive(0)()).notOk() .expect(isNthStatusActive(1)()).notOk()
.pressKey('j')
.expect(isNthStatusActive(0)()).ok()
.pressKey('j') .pressKey('j')
.expect(isNthStatusActive(1)()).ok() .expect(isNthStatusActive(1)()).ok()
.pressKey('j') .pressKey('j')
.expect(isNthStatusActive(2)()).ok() .expect(isNthStatusActive(2)()).ok()
.pressKey('j') .pressKey('j')
.expect(isNthStatusActive(3)()).ok() .expect(isNthStatusActive(3)()).ok()
.pressKey('j')
.expect(isNthStatusActive(4)()).ok()
.pressKey('k')
.expect(isNthStatusActive(3)()).ok()
.pressKey('k') .pressKey('k')
.expect(isNthStatusActive(2)()).ok() .expect(isNthStatusActive(2)()).ok()
.pressKey('k') .pressKey('k')
.expect(isNthStatusActive(1)()).ok() .expect(isNthStatusActive(1)()).ok()
.pressKey('k')
.expect(isNthStatusActive(0)()).ok()
.expect(isNthStatusActive(1)()).notOk()
.expect(isNthStatusActive(2)()).notOk() .expect(isNthStatusActive(2)()).notOk()
.expect(isNthStatusActive(3)()).notOk() .expect(isNthStatusActive(3)()).notOk()
.expect(isNthStatusActive(4)()).notOk()
}) })
test('Shortcut j goes to the first visible status', async t => { test('Shortcut j goes to the first visible status', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.expect(getUrl()).eql('http://localhost:4002/') .expect(getUrl()).eql('http://localhost:4002/')
await scrollToStatus(t, 10) await scrollToStatus(t, 11)
await t await t
.expect(getNthStatus(10).exists).ok({ timeout: 30000 }) .expect(getNthStatus(11).exists).ok({ timeout: 30000 })
.pressKey('j') .pressKey('j')
.expect(getActiveElementRectTop()).gte(0) .expect(getActiveElementRectTop()).gte(0)
}) })
@ -68,15 +68,15 @@ test('Shortcut o opens active status, backspace goes back', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.expect(getUrl()).eql('http://localhost:4002/') .expect(getUrl()).eql('http://localhost:4002/')
.expect(getNthStatus(2).exists).ok({ timeout: 30000 }) .expect(getNthStatus(3).exists).ok({ timeout: 30000 })
.pressKey('j') // activates status 0 .pressKey('j') // activates status 0
.pressKey('j') // activates status 1 .pressKey('j') // activates status 1
.pressKey('j') // activates status 2 .pressKey('j') // activates status 2
.expect(isNthStatusActive(2)()).ok() .expect(isNthStatusActive(3)()).ok()
.pressKey('o') .pressKey('o')
.expect(getUrl()).contains('/statuses/') .expect(getUrl()).contains('/statuses/')
.pressKey('Backspace') .pressKey('Backspace')
.expect(isNthStatusActive(2)()).ok() .expect(isNthStatusActive(3)()).ok()
}) })
test('Shortcut x shows/hides spoilers', async t => { test('Shortcut x shows/hides spoilers', async t => {
@ -86,13 +86,13 @@ test('Shortcut x shows/hides spoilers', async t => {
.expect(getUrl()).eql('http://localhost:4002/') .expect(getUrl()).eql('http://localhost:4002/')
await activateStatus(t, idx) await activateStatus(t, idx)
await t await t
.expect(isNthStatusActive(idx)()).ok() .expect(isNthStatusActive(1 + idx)()).ok()
.expect(getNthStatusSpoiler(idx).innerText).contains('kitten CW') .expect(getNthStatusSpoiler(1 + idx).innerText).contains('kitten CW')
.expect(getNthStatusContent(idx).hasClass('shown')).notOk() .expect(getNthStatusContent(1 + idx).hasClass('shown')).notOk()
.pressKey('x') .pressKey('x')
.expect(getNthStatusContent(idx).hasClass('shown')).ok() .expect(getNthStatusContent(1 + idx).hasClass('shown')).ok()
.pressKey('x') .pressKey('x')
.expect(getNthStatusContent(idx).hasClass('shown')).notOk() .expect(getNthStatusContent(1 + idx).hasClass('shown')).notOk()
}) })
test('Shortcut y shows/hides sensitive image', async t => { test('Shortcut y shows/hides sensitive image', async t => {
@ -102,13 +102,13 @@ test('Shortcut y shows/hides sensitive image', async t => {
.expect(getUrl()).eql('http://localhost:4002/') .expect(getUrl()).eql('http://localhost:4002/')
await activateStatus(t, idx) await activateStatus(t, idx)
await t await t
.expect(isNthStatusActive(idx)()).ok() .expect(isNthStatusActive(1 + idx)()).ok()
.expect(getNthStatusSensitiveMediaButton(idx).exists).ok() .expect(getNthStatusSensitiveMediaButton(1 + idx).exists).ok()
.expect(getNthStatusMedia(idx).exists).notOk() .expect(getNthStatusMedia(1 + idx).exists).notOk()
.pressKey('y') .pressKey('y')
.expect(getNthStatusMedia(idx).exists).ok() .expect(getNthStatusMedia(1 + idx).exists).ok()
.pressKey('y') .pressKey('y')
.expect(getNthStatusMedia(idx).exists).notOk() .expect(getNthStatusMedia(1 + idx).exists).notOk()
}) })
test('Shortcut f toggles favorite status', async t => { test('Shortcut f toggles favorite status', async t => {
@ -116,14 +116,14 @@ test('Shortcut f toggles favorite status', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.expect(getUrl()).eql('http://localhost:4002/') .expect(getUrl()).eql('http://localhost:4002/')
.expect(getNthStatus(idx).exists).ok({ timeout: 30000 }) .expect(getNthStatus(1 + idx).exists).ok({ timeout: 30000 })
.expect(getNthFavorited(idx)).eql('false') .expect(getNthFavorited(1 + idx)).eql('false')
.pressKey('j '.repeat(idx + 1)) .pressKey('j '.repeat(idx + 1))
.expect(isNthStatusActive(idx)()).ok() .expect(isNthStatusActive(1 + idx)()).ok()
.pressKey('f') .pressKey('f')
.expect(getNthFavorited(idx)).eql('true') .expect(getNthFavorited(1 + idx)).eql('true')
.pressKey('f') .pressKey('f')
.expect(getNthFavorited(idx)).eql('false') .expect(getNthFavorited(1 + idx)).eql('false')
}) })
test('Shortcut p toggles profile', async t => { test('Shortcut p toggles profile', async t => {
@ -131,9 +131,9 @@ test('Shortcut p toggles profile', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.expect(getUrl()).eql('http://localhost:4002/') .expect(getUrl()).eql('http://localhost:4002/')
.expect(getNthStatus(idx).exists).ok({ timeout: 30000 }) .expect(getNthStatus(1 + idx).exists).ok({ timeout: 30000 })
.pressKey('j '.repeat(idx + 1)) .pressKey('j '.repeat(idx + 1))
.expect(isNthStatusActive(idx)()).ok() .expect(isNthStatusActive(1 + idx)()).ok()
.pressKey('p') .pressKey('p')
.expect(getUrl()).contains('/accounts/3') .expect(getUrl()).contains('/accounts/3')
}) })
@ -143,9 +143,9 @@ test('Shortcut m toggles mention', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.expect(getUrl()).eql('http://localhost:4002/') .expect(getUrl()).eql('http://localhost:4002/')
.expect(getNthStatus(idx).exists).ok({ timeout: 30000 }) .expect(getNthStatus(1 + idx).exists).ok({ timeout: 30000 })
.pressKey('j '.repeat(idx + 1)) .pressKey('j '.repeat(idx + 1))
.expect(isNthStatusActive(idx)()).ok() .expect(isNthStatusActive(1 + idx)()).ok()
.pressKey('m') .pressKey('m')
.expect(composeModalInput.value).eql('@quux ') .expect(composeModalInput.value).eql('@quux ')
.click(closeDialogButton) .click(closeDialogButton)
@ -156,32 +156,32 @@ test('Shortcut j/k change the active status on a thread', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.click($('a').withText('quux')) .click($('a').withText('quux'))
await scrollToStatus(t, 2) await scrollToStatus(t, 3)
await t await t
.click(getNthStatus(2)) .click(getNthStatus(3))
.expect(getUrl()).contains('/statuses') .expect(getUrl()).contains('/statuses')
await scrollToStatus(t, 0) await scrollToStatus(t, 1)
await scrollToTop() await scrollToTop()
await t await t
.expect(getNthStatus(0).exists).ok({ timeout: 30000 }) .expect(getNthStatus(1).exists).ok({ timeout: 30000 })
.expect(isNthStatusActive(0)()).notOk() .expect(isNthStatusActive(1)()).notOk()
.pressKey('j')
.expect(isNthStatusActive(0)()).ok()
.pressKey('j') .pressKey('j')
.expect(isNthStatusActive(1)()).ok() .expect(isNthStatusActive(1)()).ok()
.pressKey('j') .pressKey('j')
.expect(isNthStatusActive(2)()).ok() .expect(isNthStatusActive(2)()).ok()
.pressKey('j') .pressKey('j')
.expect(isNthStatusActive(3)()).ok() .expect(isNthStatusActive(3)()).ok()
.pressKey('j')
.expect(isNthStatusActive(4)()).ok()
.pressKey('k')
.expect(isNthStatusActive(3)()).ok()
.pressKey('k') .pressKey('k')
.expect(isNthStatusActive(2)()).ok() .expect(isNthStatusActive(2)()).ok()
.pressKey('k') .pressKey('k')
.expect(isNthStatusActive(1)()).ok() .expect(isNthStatusActive(1)()).ok()
.pressKey('k')
.expect(isNthStatusActive(0)()).ok()
.expect(isNthStatusActive(1)()).notOk()
.expect(isNthStatusActive(2)()).notOk() .expect(isNthStatusActive(2)()).notOk()
.expect(isNthStatusActive(3)()).notOk() .expect(isNthStatusActive(3)()).notOk()
.expect(isNthStatusActive(4)()).notOk()
}) })
test('Shortcut j/k change the active status on pinned statuses', async t => { test('Shortcut j/k change the active status on pinned statuses', async t => {
@ -190,27 +190,27 @@ test('Shortcut j/k change the active status on pinned statuses', async t => {
.click($('a').withText('quux')) .click($('a').withText('quux'))
.expect(getUrl()).contains('/accounts') .expect(getUrl()).contains('/accounts')
await t await t
.expect(getNthStatus(0).exists).ok({ timeout: 30000 }) .expect(getNthStatus(1).exists).ok({ timeout: 30000 })
.expect(isNthStatusActive(0)()).notOk() .expect(isNthStatusActive(1)()).notOk()
.pressKey('j')
.expect(isNthStatusActive(0)()).ok()
.expect(isActiveStatusPinned()).eql(true)
.pressKey('j') .pressKey('j')
.expect(isNthStatusActive(1)()).ok() .expect(isNthStatusActive(1)()).ok()
.expect(isActiveStatusPinned()).eql(true) .expect(isActiveStatusPinned()).eql(true)
.pressKey('j') .pressKey('j')
.expect(isNthStatusActive(0)()).ok() .expect(isNthStatusActive(2)()).ok()
.expect(isActiveStatusPinned()).eql(false) .expect(isActiveStatusPinned()).eql(true)
.pressKey('j') .pressKey('j')
.expect(isNthStatusActive(1)()).ok() .expect(isNthStatusActive(1)()).ok()
.expect(isActiveStatusPinned()).eql(false) .expect(isActiveStatusPinned()).eql(false)
.pressKey('k') .pressKey('j')
.expect(isNthStatusActive(0)()).ok() .expect(isNthStatusActive(2)()).ok()
.expect(isActiveStatusPinned()).eql(false) .expect(isActiveStatusPinned()).eql(false)
.pressKey('k') .pressKey('k')
.expect(isNthStatusActive(1)()).ok() .expect(isNthStatusActive(1)()).ok()
.expect(isActiveStatusPinned()).eql(false)
.pressKey('k')
.expect(isNthStatusActive(2)()).ok()
.expect(isActiveStatusPinned()).eql(true) .expect(isActiveStatusPinned()).eql(true)
.pressKey('k') .pressKey('k')
.expect(isNthStatusActive(0)()).ok() .expect(isNthStatusActive(1)()).ok()
.expect(isActiveStatusPinned()).eql(true) .expect(isActiveStatusPinned()).eql(true)
}) })

View file

@ -18,14 +18,14 @@ test('Shortcut f toggles favorite status in notification', async t => {
.expect(getUrl()).eql('http://localhost:4002/') .expect(getUrl()).eql('http://localhost:4002/')
.click(notificationsNavButton) .click(notificationsNavButton)
.expect(getUrl()).contains('/notifications') .expect(getUrl()).contains('/notifications')
.expect(getNthStatus(idx).exists).ok({ timeout: 30000 }) .expect(getNthStatus(1 + idx).exists).ok({ timeout: 30000 })
.expect(getNthFavorited(idx)).eql('false') .expect(getNthFavorited(1 + idx)).eql('false')
.pressKey('j '.repeat(idx + 1)) .pressKey('j '.repeat(idx + 1))
.expect(isNthStatusActive(idx)()).ok() .expect(isNthStatusActive(1 + idx)()).ok()
.pressKey('f') .pressKey('f')
.expect(getNthFavorited(idx)).eql('true') .expect(getNthFavorited(1 + idx)).eql('true')
.pressKey('f') .pressKey('f')
.expect(getNthFavorited(idx)).eql('false') .expect(getNthFavorited(1 + idx)).eql('false')
}) })
test('Shortcut p toggles profile in a follow notification', async t => { test('Shortcut p toggles profile in a follow notification', async t => {
@ -35,14 +35,14 @@ test('Shortcut p toggles profile in a follow notification', async t => {
.expect(getUrl()).eql('http://localhost:4002/') .expect(getUrl()).eql('http://localhost:4002/')
.click(notificationsNavButton) .click(notificationsNavButton)
.expect(getUrl()).contains('/notifications') .expect(getUrl()).contains('/notifications')
.expect(getNthStatus(0).exists).ok({ timeout: 30000 }) .expect(getNthStatus(1).exists).ok({ timeout: 30000 })
.pressKey('j '.repeat(idx + 1)) .pressKey('j '.repeat(idx + 1))
.expect(isNthStatusActive(idx)()).ok() .expect(isNthStatusActive(1 + idx)()).ok()
.pressKey('p') .pressKey('p')
.expect(getUrl()).contains('/accounts/3') .expect(getUrl()).contains('/accounts/3')
await goBack() await goBack()
await t await t
.expect(isNthStatusActive(idx)()).ok() // focus preserved .expect(isNthStatusActive(1 + idx)()).ok() // focus preserved
}) })
test('Shortcut m toggles mention in a follow notification', async t => { test('Shortcut m toggles mention in a follow notification', async t => {
@ -52,9 +52,9 @@ test('Shortcut m toggles mention in a follow notification', async t => {
.expect(getUrl()).eql('http://localhost:4002/') .expect(getUrl()).eql('http://localhost:4002/')
.click(notificationsNavButton) .click(notificationsNavButton)
.expect(getUrl()).contains('/notifications') .expect(getUrl()).contains('/notifications')
.expect(getNthStatus(0).exists).ok({ timeout: 30000 }) .expect(getNthStatus(1).exists).ok({ timeout: 30000 })
.pressKey('j '.repeat(idx + 1)) .pressKey('j '.repeat(idx + 1))
.expect(isNthStatusActive(idx)()).ok() .expect(isNthStatusActive(1 + idx)()).ok()
.pressKey('m') .pressKey('m')
.expect(composeModalInput.value).eql('@quux ') .expect(composeModalInput.value).eql('@quux ')
.click(closeDialogButton) .click(closeDialogButton)
@ -68,9 +68,9 @@ test('Shortcut p refers to booster in a boost notification', async t => {
.expect(getUrl()).eql('http://localhost:4002/') .expect(getUrl()).eql('http://localhost:4002/')
.click(notificationsNavButton) .click(notificationsNavButton)
.expect(getUrl()).contains('/notifications') .expect(getUrl()).contains('/notifications')
.expect(getNthStatus(0).exists).ok({ timeout: 30000 }) .expect(getNthStatus(1).exists).ok({ timeout: 30000 })
.pressKey('j '.repeat(idx + 1)) .pressKey('j '.repeat(idx + 1))
.expect(isNthStatusActive(idx)()).ok() .expect(isNthStatusActive(1 + idx)()).ok()
.pressKey('p') .pressKey('p')
.expect(getUrl()).contains('/accounts/1') .expect(getUrl()).contains('/accounts/1')
}) })
@ -82,9 +82,9 @@ test('Shortcut m refers to favoriter in a favorite notification', async t => {
.expect(getUrl()).eql('http://localhost:4002/') .expect(getUrl()).eql('http://localhost:4002/')
.click(notificationsNavButton) .click(notificationsNavButton)
.expect(getUrl()).contains('/notifications') .expect(getUrl()).contains('/notifications')
.expect(getNthStatus(0).exists).ok({ timeout: 30000 }) .expect(getNthStatus(1).exists).ok({ timeout: 30000 })
.pressKey('j '.repeat(idx + 1)) .pressKey('j '.repeat(idx + 1))
.expect(isNthStatusActive(idx)()).ok() .expect(isNthStatusActive(1 + idx)()).ok()
.pressKey('m') .pressKey('m')
.expect(composeModalInput.value).eql('@admin ') .expect(composeModalInput.value).eql('@admin ')
.click(closeDialogButton) .click(closeDialogButton)

View file

@ -13,7 +13,7 @@ fixture`028-report-ui.js`
test('Can open a report UI from a status', async t => { test('Can open a report UI from a status', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.click(getNthStatusOptionsButton(0)) .click(getNthStatusOptionsButton(1))
.click($('.modal-dialog button').withText('Report')) .click($('.modal-dialog button').withText('Report'))
.expect(modalDialog.innerText).contains('You are reporting @quux') .expect(modalDialog.innerText).contains('You are reporting @quux')
.expect(modalDialog.find('.recent-statuses').innerText).contains('pinned toot 2') .expect(modalDialog.find('.recent-statuses').innerText).contains('pinned toot 2')

View file

@ -13,72 +13,72 @@ fixture`100-favorite-unfavorite.js`
test('favorites a status', async t => { test('favorites a status', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.hover(getNthStatus(4)) .hover(getNthStatus(5))
.expect(getNthFavorited(4)).eql('false') .expect(getNthFavorited(5)).eql('false')
.click(getNthFavoriteButton(4)) .click(getNthFavoriteButton(5))
.expect(getNthFavorited(4)).eql('true') .expect(getNthFavorited(5)).eql('true')
// scroll down and back up to force an unrender // scroll down and back up to force an unrender
await scrollToBottom() await scrollToBottom()
await sleep(1) await sleep(1)
await scrollToTop() await scrollToTop()
await t await t
.hover(getNthStatus(4)) .hover(getNthStatus(5))
.expect(getNthFavorited(4)).eql('true') .expect(getNthFavorited(5)).eql('true')
.click(notificationsNavButton) .click(notificationsNavButton)
.click(homeNavButton) .click(homeNavButton)
.expect(getNthFavorited(4)).eql('true') .expect(getNthFavorited(5)).eql('true')
.click(notificationsNavButton) .click(notificationsNavButton)
.expect(getUrl()).contains('/notifications') .expect(getUrl()).contains('/notifications')
.click(homeNavButton) .click(homeNavButton)
.expect(getUrl()).eql('http://localhost:4002/') .expect(getUrl()).eql('http://localhost:4002/')
.hover(getNthStatus(4)) .hover(getNthStatus(5))
.expect(getNthFavorited(4)).eql('true') .expect(getNthFavorited(5)).eql('true')
.click(getNthFavoriteButton(4)) .click(getNthFavoriteButton(5))
.expect(getNthFavorited(4)).eql('false') .expect(getNthFavorited(5)).eql('false')
}) })
test('unfavorites a status', async t => { test('unfavorites a status', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.expect(getNthFavorited(1)).eql('true') .expect(getNthFavorited(2)).eql('true')
.click(getNthFavoriteButton(1)) .click(getNthFavoriteButton(2))
.expect(getNthFavorited(1)).eql('false') .expect(getNthFavorited(2)).eql('false')
// scroll down and back up to force an unrender // scroll down and back up to force an unrender
await scrollToBottom() await scrollToBottom()
await sleep(1) await sleep(1)
await scrollToTop() await scrollToTop()
await t await t
.expect(getNthFavorited(1)).eql('false') .expect(getNthFavorited(2)).eql('false')
.click(notificationsNavButton) .click(notificationsNavButton)
.click(homeNavButton) .click(homeNavButton)
.expect(getNthFavorited(1)).eql('false') .expect(getNthFavorited(2)).eql('false')
.click(notificationsNavButton) .click(notificationsNavButton)
.navigateTo('/') .navigateTo('/')
.expect(getNthFavorited(1)).eql('false') .expect(getNthFavorited(2)).eql('false')
.click(getNthFavoriteButton(1)) .click(getNthFavoriteButton(2))
.expect(getNthFavorited(1)).eql('true') .expect(getNthFavorited(2)).eql('true')
}) })
test('Keeps the correct favorites count', async t => { test('Keeps the correct favorites count', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
let idx = indexWhere(homeTimeline, _ => _.content === 'this is unlisted') let idx = indexWhere(homeTimeline, _ => _.content === 'this is unlisted')
await t await t
.hover(getNthStatus(idx)) .hover(getNthStatus(1 + idx))
.click(getNthFavoriteButton(idx)) .click(getNthFavoriteButton(1 + idx))
.expect(getNthFavorited(idx)).eql('true') .expect(getNthFavorited(1 + idx)).eql('true')
.click(getNthStatus(idx)) .click(getNthStatus(1 + idx))
.expect(getUrl()).contains('/status') .expect(getUrl()).contains('/status')
.expect(getNthFavorited(0)).eql('true') .expect(getNthFavorited(1)).eql('true')
.expect(getFavoritesCount()).eql(2) .expect(getFavoritesCount()).eql(2)
.click(homeNavButton) .click(homeNavButton)
.expect(getUrl()).eql('http://localhost:4002/') .expect(getUrl()).eql('http://localhost:4002/')
.hover(getNthStatus(idx)) .hover(getNthStatus(1 + idx))
.click(getNthFavoriteButton(idx)) .click(getNthFavoriteButton(1 + idx))
.expect(getNthFavorited(idx)).eql('false') .expect(getNthFavorited(1 + idx)).eql('false')
.click(getNthStatus(idx)) .click(getNthStatus(1 + idx))
.expect(getUrl()).contains('/status') .expect(getUrl()).contains('/status')
.expect(getNthFavorited(0)).eql('false') .expect(getNthFavorited(1)).eql('false')
.expect(getFavoritesCount()).eql(1) .expect(getFavoritesCount()).eql(1)
}) })

View file

@ -12,58 +12,58 @@ test('reblogs a status', async t => {
await postAs('foobar', 'hello this should be reblogged') await postAs('foobar', 'hello this should be reblogged')
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.hover(getNthStatus(0)) .hover(getNthStatus(1))
.expect(getNthStatusContent(0).innerText).contains('should be reblogged') .expect(getNthStatusContent(1).innerText).contains('should be reblogged')
.expect(getNthReblogged(0)).eql('false') .expect(getNthReblogged(1)).eql('false')
.click(getNthReblogButton(0)) .click(getNthReblogButton(1))
.expect(getNthReblogged(0)).eql('true') .expect(getNthReblogged(1)).eql('true')
// scroll down and back up to force an unrender // scroll down and back up to force an unrender
await scrollToBottom() await scrollToBottom()
await sleep(1) await sleep(1)
await scrollToTop() await scrollToTop()
await t await t
.hover(getNthStatus(0)) .hover(getNthStatus(1))
.expect(getNthReblogged(0)).eql('true') .expect(getNthReblogged(1)).eql('true')
.click(notificationsNavButton) .click(notificationsNavButton)
.click(homeNavButton) .click(homeNavButton)
.expect(getNthReblogged(0)).eql('true') .expect(getNthReblogged(1)).eql('true')
.click(notificationsNavButton) .click(notificationsNavButton)
.expect(getUrl()).contains('/notifications') .expect(getUrl()).contains('/notifications')
.click(homeNavButton) .click(homeNavButton)
.expect(getUrl()).eql('http://localhost:4002/') .expect(getUrl()).eql('http://localhost:4002/')
.expect(getNthReblogged(0)).eql('true') .expect(getNthReblogged(1)).eql('true')
.click(getNthReblogButton(0)) .click(getNthReblogButton(1))
.expect(getNthReblogged(0)).eql('false') .expect(getNthReblogged(1)).eql('false')
}) })
test('unreblogs a status', async t => { test('unreblogs a status', async t => {
await postAs('foobar', 'woot i wanna reblog this') await postAs('foobar', 'woot i wanna reblog this')
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.hover(getNthStatus(0)) .hover(getNthStatus(1))
.expect(getNthStatusContent(0).innerText).contains('woot i wanna') .expect(getNthStatusContent(1).innerText).contains('woot i wanna')
.expect(getNthReblogged(0)).eql('false') .expect(getNthReblogged(1)).eql('false')
.click(getNthReblogButton(0)) .click(getNthReblogButton(1))
.expect(getNthReblogged(0)).eql('true') .expect(getNthReblogged(1)).eql('true')
.click(getNthReblogButton(0)) .click(getNthReblogButton(1))
.expect(getNthReblogged(0)).eql('false') .expect(getNthReblogged(1)).eql('false')
// scroll down and back up to force an unrender // scroll down and back up to force an unrender
await scrollToBottom() await scrollToBottom()
await sleep(1) await sleep(1)
await scrollToTop() await scrollToTop()
await t await t
.hover(getNthStatus(0)) .hover(getNthStatus(1))
.expect(getNthReblogged(0)).eql('false') .expect(getNthReblogged(1)).eql('false')
.click(notificationsNavButton) .click(notificationsNavButton)
.click(homeNavButton) .click(homeNavButton)
.expect(getNthReblogged(0)).eql('false') .expect(getNthReblogged(1)).eql('false')
.click(notificationsNavButton) .click(notificationsNavButton)
.navigateTo('/') .navigateTo('/')
.expect(getNthReblogged(0)).eql('false') .expect(getNthReblogged(1)).eql('false')
.click(getNthReblogButton(0)) .click(getNthReblogButton(1))
.expect(getNthReblogged(0)).eql('true') .expect(getNthReblogged(1)).eql('true')
}) })
test('Keeps the correct reblogs count', async t => { test('Keeps the correct reblogs count', async t => {
@ -72,20 +72,20 @@ test('Keeps the correct reblogs count', async t => {
await reblogStatusAs('admin', id) await reblogStatusAs('admin', id)
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.hover(getNthStatus(0)) .hover(getNthStatus(1))
.expect(getNthStatusContent(0).innerText).contains('this will be reblogged') .expect(getNthStatusContent(1).innerText).contains('this will be reblogged')
.expect(getNthReblogged(0)).eql('true') .expect(getNthReblogged(1)).eql('true')
.click(getNthStatus(0)) .click(getNthStatus(1))
.expect(getUrl()).contains('/status') .expect(getUrl()).contains('/status')
.expect(getNthReblogged(0)).eql('true') .expect(getNthReblogged(1)).eql('true')
.expect(getReblogsCount()).eql(2) .expect(getReblogsCount()).eql(2)
.click(homeNavButton) .click(homeNavButton)
.expect(getUrl()).eql('http://localhost:4002/') .expect(getUrl()).eql('http://localhost:4002/')
.hover(getNthStatus(0)) .hover(getNthStatus(1))
.click(getNthReblogButton(0)) .click(getNthReblogButton(1))
.expect(getNthReblogged(0)).eql('false') .expect(getNthReblogged(1)).eql('false')
.click(getNthStatus(0)) .click(getNthStatus(1))
.expect(getUrl()).contains('/status') .expect(getUrl()).contains('/status')
.expect(getNthReblogged(0)).eql('false') .expect(getNthReblogged(1)).eql('false')
.expect(getReblogsCount()).eql(1) .expect(getReblogsCount()).eql(1)
}) })

View file

@ -13,7 +13,7 @@ test('shows unread notification', async t => {
await t await t
.expect(notificationsNavButton.getAttribute('aria-label')).eql('Notifications') .expect(notificationsNavButton.getAttribute('aria-label')).eql('Notifications')
.expect(getTitleText()).eql('localhost:3000 · Home') .expect(getTitleText()).eql('localhost:3000 · Home')
.expect(getNthStatusContent(0).innerText).contains('somebody please favorite this to validate me', { .expect(getNthStatusContent(1).innerText).contains('somebody please favorite this to validate me', {
timeout: 20000 timeout: 20000
}) })
await favoriteStatusAs('admin', id) await favoriteStatusAs('admin', id)
@ -26,8 +26,8 @@ test('shows unread notification', async t => {
.expect(getUrl()).contains('/notifications') .expect(getUrl()).contains('/notifications')
.expect(notificationsNavButton.getAttribute('aria-label')).eql('Notifications (current page)') .expect(notificationsNavButton.getAttribute('aria-label')).eql('Notifications (current page)')
.expect(getTitleText()).eql('localhost:3000 · Notifications') .expect(getTitleText()).eql('localhost:3000 · Notifications')
.expect(getNthStatus(0).innerText).contains('somebody please favorite this to validate me') .expect(getNthStatus(1).innerText).contains('somebody please favorite this to validate me')
.expect(getNthStatus(0).innerText).match(/admin\s+favorited your status/) .expect(getNthStatus(1).innerText).match(/admin\s+favorited your status/)
await t await t
.click(homeNavButton) .click(homeNavButton)
.expect(notificationsNavButton.getAttribute('aria-label')).eql('Notifications') .expect(notificationsNavButton.getAttribute('aria-label')).eql('Notifications')
@ -40,7 +40,7 @@ test('shows unread notifications, more than one', async t => {
await t await t
.expect(notificationsNavButton.getAttribute('aria-label')).eql('Notifications') .expect(notificationsNavButton.getAttribute('aria-label')).eql('Notifications')
.expect(getTitleText()).eql('localhost:3000 · Home') .expect(getTitleText()).eql('localhost:3000 · Home')
.expect(getNthStatusContent(0).innerText).contains('I need lots of favorites on this one', { .expect(getNthStatusContent(1).innerText).contains('I need lots of favorites on this one', {
timeout: 20000 timeout: 20000
}) })
await favoriteStatusAs('admin', id) await favoriteStatusAs('admin', id)
@ -54,7 +54,7 @@ test('shows unread notifications, more than one', async t => {
.expect(getUrl()).contains('/notifications') .expect(getUrl()).contains('/notifications')
.expect(notificationsNavButton.getAttribute('aria-label')).eql('Notifications (current page)') .expect(notificationsNavButton.getAttribute('aria-label')).eql('Notifications (current page)')
.expect(getTitleText()).eql('localhost:3000 · Notifications') .expect(getTitleText()).eql('localhost:3000 · Notifications')
.expect(getNthStatus(0).innerText).contains('I need lots of favorites on this one') .expect(getNthStatus(1).innerText).contains('I need lots of favorites on this one')
await t await t
.click(homeNavButton) .click(homeNavButton)
.expect(notificationsNavButton.getAttribute('aria-label')).eql('Notifications') .expect(notificationsNavButton.getAttribute('aria-label')).eql('Notifications')

View file

@ -13,28 +13,28 @@ test('statuses show up in home timeline', async t => {
await t await t
.typeText(composeInput, 'hello world', { paste: true }) .typeText(composeInput, 'hello world', { paste: true })
.click(postStatusButton) .click(postStatusButton)
.expect(getNthStatus(0).innerText).contains('hello world') .expect(getNthStatus(1).innerText).contains('hello world')
.click(notificationsNavButton) .click(notificationsNavButton)
.expect(getUrl()).contains('/notifications') .expect(getUrl()).contains('/notifications')
.click(homeNavButton) .click(homeNavButton)
.expect(getUrl()).eql('http://localhost:4002/') .expect(getUrl()).eql('http://localhost:4002/')
.expect(getNthStatus(0).innerText).contains('hello world') .expect(getNthStatus(1).innerText).contains('hello world')
.navigateTo('/') .navigateTo('/')
.expect(getNthStatus(0).innerText).contains('hello world') .expect(getNthStatus(1).innerText).contains('hello world')
}) })
test('statuses in threads show up in right order', async t => { test('statuses in threads show up in right order', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.navigateTo('/accounts/5') .navigateTo('/accounts/5')
.click(getNthStatus(2)) .click(getNthStatus(3))
.expect(getUrl()).contains('/statuses') .expect(getUrl()).contains('/statuses')
.click(getNthReplyButton(3)) .click(getNthReplyButton(4))
.typeText(getNthComposeReplyInput(3), 'my reply!', { paste: true }) .typeText(getNthComposeReplyInput(4), 'my reply!', { paste: true })
.click(getNthComposeReplyButton(3)) .click(getNthComposeReplyButton(4))
.expect(getNthComposeReplyInput(3).exists).notOk() .expect(getNthComposeReplyInput(4).exists).notOk()
.expect(getNthStatus(5).innerText).contains('@baz my reply!') .expect(getNthStatus(6).innerText).contains('@baz my reply!')
.navigateTo('/accounts/5') .navigateTo('/accounts/5')
.click(getNthStatus(2)) .click(getNthStatus(3))
.expect(getNthStatus(5).innerText).contains('@baz my reply!') .expect(getNthStatus(6).innerText).contains('@baz my reply!')
}) })

View file

@ -10,23 +10,23 @@ fixture`104-streaming.js`
test('new incoming statuses show up immediately', async t => { test('new incoming statuses show up immediately', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.hover(getNthStatus(0)) .hover(getNthStatus(1))
await postAs('admin', 'hello my baby hello my honey') await postAs('admin', 'hello my baby hello my honey')
await t.expect(getNthStatus(0).innerText).contains('hello my baby hello my honey') await t.expect(getNthStatus(1).innerText).contains('hello my baby hello my honey')
}) })
test('new incoming toots show a button if scrolled down', async t => { test('new incoming toots show a button if scrolled down', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.hover(getNthStatus(0)) .hover(getNthStatus(1))
.hover(getNthStatus(2)) .hover(getNthStatus(3))
.hover(getNthStatus(4)) .hover(getNthStatus(5))
await sleep(1000) await sleep(1000)
await postAs('admin', 'hello my ragtime gal') await postAs('admin', 'hello my ragtime gal')
await postAs('admin', 'send me a kiss by wire') await postAs('admin', 'send me a kiss by wire')
await sleep(4000) await sleep(4000)
await t.hover(getNthStatus(2)) await t.hover(getNthStatus(3))
.hover(getNthStatus(0)) .hover(getNthStatus(1))
await scrollToTop() await scrollToTop()
await sleep(1000) await sleep(1000)
await t await t
@ -36,11 +36,11 @@ test('new incoming toots show a button if scrolled down', async t => {
await t.expect(showMoreButton.innerText).contains('Show 3 more') await t.expect(showMoreButton.innerText).contains('Show 3 more')
.click(showMoreButton) .click(showMoreButton)
await t await t
.expect(getNthStatus(0).innerText).contains("baby my heart's on fire") .expect(getNthStatus(1).innerText).contains("baby my heart's on fire")
.expect(getNthStatus(1).innerText).contains('send me a kiss by wire') .expect(getNthStatus(2).innerText).contains('send me a kiss by wire')
.expect(getNthStatus(2).innerText).contains('hello my ragtime gal') .expect(getNthStatus(3).innerText).contains('hello my ragtime gal')
.navigateTo('/') .navigateTo('/')
.expect(getNthStatus(0).innerText).contains("baby my heart's on fire") .expect(getNthStatus(1).innerText).contains("baby my heart's on fire")
.expect(getNthStatus(1).innerText).contains('send me a kiss by wire') .expect(getNthStatus(2).innerText).contains('send me a kiss by wire')
.expect(getNthStatus(2).innerText).contains('hello my ragtime gal') .expect(getNthStatus(3).innerText).contains('hello my ragtime gal')
}) })

View file

@ -12,45 +12,45 @@ test('deleted statuses are removed from the timeline', async t => {
let timeout = 20000 let timeout = 20000
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.hover(getNthStatus(0)) .hover(getNthStatus(1))
let status = await postAs('admin', "I'm gonna delete this") let status = await postAs('admin', "I'm gonna delete this")
await t.expect(getNthStatus(0).innerText).contains("I'm gonna delete this", { timeout }) await t.expect(getNthStatus(1).innerText).contains("I'm gonna delete this", { timeout })
await deleteAs('admin', status.id) await deleteAs('admin', status.id)
await t.expect(getNthStatus(0).innerText).notContains("I'm gonna delete this", { timeout }) await t.expect(getNthStatus(1).innerText).notContains("I'm gonna delete this", { timeout })
await clickToNotificationsAndBackHome(t) await clickToNotificationsAndBackHome(t)
await t.expect(getNthStatus(0).innerText).notContains("I'm gonna delete this", { timeout }) await t.expect(getNthStatus(1).innerText).notContains("I'm gonna delete this", { timeout })
await t.navigateTo('/notifications') await t.navigateTo('/notifications')
await forceOffline() await forceOffline()
await t.click(homeNavButton) await t.click(homeNavButton)
await t.expect(getNthStatus(0).innerText).notContains("I'm gonna delete this", { timeout }) await t.expect(getNthStatus(1).innerText).notContains("I'm gonna delete this", { timeout })
await forceOnline() await forceOnline()
await t await t
.navigateTo('/') .navigateTo('/')
.expect(getNthStatus(0).innerText).notContains("I'm gonna delete this", { timeout }) .expect(getNthStatus(1).innerText).notContains("I'm gonna delete this", { timeout })
}) })
test('deleted statuses are removed from threads', async t => { test('deleted statuses are removed from threads', async t => {
let timeout = 20000 let timeout = 20000
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.hover(getNthStatus(0)) .hover(getNthStatus(1))
let status = await postAs('admin', "I won't delete this") let status = await postAs('admin', "I won't delete this")
let reply = await postReplyAs('admin', 'But I will delete this', status.id) let reply = await postReplyAs('admin', 'But I will delete this', status.id)
await t.expect(getNthStatus(0).innerText).contains('But I will delete this', { timeout }) await t.expect(getNthStatus(1).innerText).contains('But I will delete this', { timeout })
.expect(getNthStatus(1).innerText).contains("I won't delete this", { timeout }) .expect(getNthStatus(2).innerText).contains("I won't delete this", { timeout })
.click(getNthStatus(1)) .click(getNthStatus(2))
.expect(getUrl()).contains('/statuses') .expect(getUrl()).contains('/statuses')
.expect(getNthStatus(0).innerText).contains("I won't delete this", { timeout }) .expect(getNthStatus(1).innerText).contains("I won't delete this", { timeout })
.expect(getNthStatus(1).innerText).contains('But I will delete this', { timeout }) .expect(getNthStatus(2).innerText).contains('But I will delete this', { timeout })
await deleteAs('admin', reply.id) await deleteAs('admin', reply.id)
await t.expect(getNthStatus(1).exists).notOk() await t.expect(getNthStatus(2).exists).notOk()
.expect(getNthStatus(0).innerText).contains("I won't delete this", { timeout }) .expect(getNthStatus(1).innerText).contains("I won't delete this", { timeout })
await t.navigateTo('/') await t.navigateTo('/')
await forceOffline() await forceOffline()
await t.click(getNthStatus(0)) await t.click(getNthStatus(1))
.expect(getUrl()).contains('/statuses') .expect(getUrl()).contains('/statuses')
.expect(getNthStatus(1).exists).notOk() .expect(getNthStatus(2).exists).notOk()
.expect(getNthStatus(0).innerText).contains("I won't delete this", { timeout }) .expect(getNthStatus(1).innerText).contains("I won't delete this", { timeout })
await forceOnline() await forceOnline()
}) })
@ -58,7 +58,7 @@ test('deleted statuses result in deleted notifications', async t => {
let timeout = 20000 let timeout = 20000
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.hover(getNthStatus(0)) .hover(getNthStatus(1))
.expect(notificationsNavButton.getAttribute('aria-label')).eql('Notifications') .expect(notificationsNavButton.getAttribute('aria-label')).eql('Notifications')
let status = await postAs('admin', "@foobar yo yo foobar what's up") let status = await postAs('admin', "@foobar yo yo foobar what's up")
await t.expect(notificationsNavButton.getAttribute('aria-label')).eql('Notifications (1 notification)', { timeout }) await t.expect(notificationsNavButton.getAttribute('aria-label')).eql('Notifications (1 notification)', { timeout })

View file

@ -30,7 +30,7 @@ test('can request to follow an account', async t => {
await t.navigateTo('/accounts/6') await t.navigateTo('/accounts/6')
.expect(accountProfileFollowButton.getAttribute('aria-label')).eql('Unfollow') .expect(accountProfileFollowButton.getAttribute('aria-label')).eql('Unfollow')
.expect(getNthStatus(0).innerText).contains('This account is locked') .expect(getNthStatus(1).innerText).contains('This account is locked')
.click(accountProfileFollowButton) .click(accountProfileFollowButton)
.expect(accountProfileFollowButton.getAttribute('aria-label')).eql('Follow') .expect(accountProfileFollowButton.getAttribute('aria-label')).eql('Follow')
}) })

View file

@ -15,18 +15,18 @@ test('fills in a status posted while away from timeline', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.click(localTimelineNavButton) .click(localTimelineNavButton)
.expect(getNthStatus(0).exists).ok({ timeout }) .expect(getNthStatus(1).exists).ok({ timeout })
.hover(getNthStatus(0)) .hover(getNthStatus(1))
await postAs('admin', 'heyo') await postAs('admin', 'heyo')
await t.expect(getNthStatus(0).innerText).contains('heyo', { timeout }) await t.expect(getNthStatus(1).innerText).contains('heyo', { timeout })
.click(homeNavButton) .click(homeNavButton)
.hover(getNthStatus(0)) .hover(getNthStatus(1))
await postAs('admin', 'posted this while you were away!') await postAs('admin', 'posted this while you were away!')
await t.expect(getNthStatus(0).innerText).contains('posted this while you were away!', { timeout }) await t.expect(getNthStatus(1).innerText).contains('posted this while you were away!', { timeout })
.click(localTimelineNavButton) .click(localTimelineNavButton)
.expect(getNthStatus(0).innerText).contains('posted this while you were away!', { timeout }) .expect(getNthStatus(1).innerText).contains('posted this while you were away!', { timeout })
.expect(getNthStatus(1).innerText).contains('heyo', { timeout }) .expect(getNthStatus(2).innerText).contains('heyo', { timeout })
await sleep(5000) await sleep(5000)
await postAs('admin', 'posted this while you were watching') await postAs('admin', 'posted this while you were watching')
await t.expect(getNthStatus(0).innerText).contains('posted this while you were watching', { timeout }) await t.expect(getNthStatus(1).innerText).contains('posted this while you were watching', { timeout })
}) })

View file

@ -19,7 +19,7 @@ fixture`108-compose-dialog.js`
test('can compose using a dialog', async t => { test('can compose using a dialog', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await scrollToStatus(t, 15) await scrollToStatus(t, 16)
await t.expect(modalDialog.exists).notOk() await t.expect(modalDialog.exists).notOk()
.expect(composeButton.getAttribute('aria-label')).eql('Compose') .expect(composeButton.getAttribute('aria-label')).eql('Compose')
await sleep(2000) await sleep(2000)
@ -31,13 +31,13 @@ test('can compose using a dialog', async t => {
.click(notificationsNavButton) .click(notificationsNavButton)
.expect(getUrl()).contains('/notifications') .expect(getUrl()).contains('/notifications')
.navigateTo('/') .navigateTo('/')
.hover(getNthStatus(0)) .hover(getNthStatus(1))
.expect(getNthStatus(0).innerText).contains('hello from the modal', { timeout: 20000 }) .expect(getNthStatus(1).innerText).contains('hello from the modal', { timeout: 20000 })
}) })
test('can use emoji dialog within compose dialog', async t => { test('can use emoji dialog within compose dialog', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await scrollToStatus(t, 15) await scrollToStatus(t, 16)
await t.expect(composeButton.getAttribute('aria-label')).eql('Compose') await t.expect(composeButton.getAttribute('aria-label')).eql('Compose')
await sleep(2000) await sleep(2000)
await t.click(composeButton) await t.click(composeButton)
@ -50,5 +50,5 @@ test('can use emoji dialog within compose dialog', async t => {
.click(notificationsNavButton) .click(notificationsNavButton)
.expect(getUrl()).contains('/notifications') .expect(getUrl()).contains('/notifications')
.navigateTo('/') .navigateTo('/')
await t.expect($(`${getNthStatusSelector(0)} img[alt=":blobpats:"]`).exists).ok({ timeout: 20000 }) await t.expect($(`${getNthStatusSelector(1)} img[alt=":blobpats:"]`).exists).ok({ timeout: 20000 })
}) })

View file

@ -25,10 +25,10 @@ test('uploads alts for media', async t => {
await t.typeText(getNthMediaAltInput(2), 'kitten 2') await t.typeText(getNthMediaAltInput(2), 'kitten 2')
.typeText(getNthMediaAltInput(1), 'kitten 1') .typeText(getNthMediaAltInput(1), 'kitten 1')
.click(composeButton) .click(composeButton)
.expect(getNthStatusAndImage(0, 0).getAttribute('alt')).eql('kitten 1') .expect(getNthStatusAndImage(1, 0).getAttribute('alt')).eql('kitten 1')
.expect(getNthStatusAndImage(0, 0).getAttribute('title')).eql('kitten 1') .expect(getNthStatusAndImage(1, 0).getAttribute('title')).eql('kitten 1')
.expect(getNthStatusAndImage(0, 1).getAttribute('alt')).eql('kitten 2') .expect(getNthStatusAndImage(1, 1).getAttribute('alt')).eql('kitten 2')
.expect(getNthStatusAndImage(0, 1).getAttribute('title')).eql('kitten 2') .expect(getNthStatusAndImage(1, 1).getAttribute('title')).eql('kitten 2')
}) })
test('uploads alts when deleting and re-uploading media', async t => { test('uploads alts when deleting and re-uploading media', async t => {
@ -44,7 +44,7 @@ test('uploads alts when deleting and re-uploading media', async t => {
.expect(getNthMedia(1).getAttribute('alt')).eql('kitten2.jpg') .expect(getNthMedia(1).getAttribute('alt')).eql('kitten2.jpg')
.typeText(getNthMediaAltInput(1), 'this will not be deleted') .typeText(getNthMediaAltInput(1), 'this will not be deleted')
.click(composeButton) .click(composeButton)
.expect(getNthStatusAndImage(0, 0).getAttribute('alt')).eql('this will not be deleted') .expect(getNthStatusAndImage(1, 0).getAttribute('alt')).eql('this will not be deleted')
}) })
test('uploads alts mixed with no-alts', async t => { test('uploads alts mixed with no-alts', async t => {
@ -54,8 +54,8 @@ test('uploads alts mixed with no-alts', async t => {
await uploadTwoKittens(t) await uploadTwoKittens(t)
await t.typeText(getNthMediaAltInput(2), 'kitten numero dos') await t.typeText(getNthMediaAltInput(2), 'kitten numero dos')
.click(composeButton) .click(composeButton)
.expect(getNthStatusAndImage(0, 0).getAttribute('alt')).eql('') .expect(getNthStatusAndImage(1, 0).getAttribute('alt')).eql('')
.expect(getNthStatusAndImage(0, 1).getAttribute('alt')).eql('kitten numero dos') .expect(getNthStatusAndImage(1, 1).getAttribute('alt')).eql('kitten numero dos')
}) })
test('saves alts to local storage', async t => { test('saves alts to local storage', async t => {
@ -74,8 +74,8 @@ test('saves alts to local storage', async t => {
.expect(getNthMediaAltInput(1).value).eql('kitten numero uno') .expect(getNthMediaAltInput(1).value).eql('kitten numero uno')
.expect(getNthMediaAltInput(2).value).eql('kitten numero dos') .expect(getNthMediaAltInput(2).value).eql('kitten numero dos')
.click(composeButton) .click(composeButton)
.expect(getNthStatusAndImage(0, 0).getAttribute('alt')).eql('kitten numero uno') .expect(getNthStatusAndImage(1, 0).getAttribute('alt')).eql('kitten numero uno')
.expect(getNthStatusAndImage(0, 1).getAttribute('alt')).eql('kitten numero dos') .expect(getNthStatusAndImage(1, 1).getAttribute('alt')).eql('kitten numero dos')
}) })
test('can post a status with empty content if there is media', async t => { test('can post a status with empty content if there is media', async t => {
@ -87,5 +87,5 @@ test('can post a status with empty content if there is media', async t => {
await t await t
.typeText(getNthMediaAltInput(1), 'just an image!') .typeText(getNthMediaAltInput(1), 'just an image!')
await t.click(composeButton) await t.click(composeButton)
.expect(getNthStatusAndImage(0, 0).getAttribute('alt')).eql('just an image!') .expect(getNthStatusAndImage(1, 0).getAttribute('alt')).eql('just an image!')
}) })

View file

@ -15,11 +15,11 @@ test('content warnings are posted', async t => {
.click(contentWarningButton) .click(contentWarningButton)
.typeText(composeContentWarning, 'CW', { paste: true }) .typeText(composeContentWarning, 'CW', { paste: true })
.click(composeButton) .click(composeButton)
.expect($(`${getNthStatusSelector(0)} .status-spoiler`).innerText).contains('CW', { timeout: 30000 }) .expect($(`${getNthStatusSelector(1)} .status-spoiler`).innerText).contains('CW', { timeout: 30000 })
.click(getNthShowOrHideButton(0)) .click(getNthShowOrHideButton(1))
.expect($(`${getNthStatusSelector(0)} .status-content`).innerText).contains('hello this is a toot') .expect($(`${getNthStatusSelector(1)} .status-content`).innerText).contains('hello this is a toot')
.click(getNthShowOrHideButton(0)) .click(getNthShowOrHideButton(1))
.expect(getNthStatus(0).innerText).notContains('hello this is a toot') .expect(getNthStatus(1).innerText).notContains('hello this is a toot')
}) })
test('content warnings are not posted if removed', async t => { test('content warnings are not posted if removed', async t => {
@ -31,9 +31,9 @@ test('content warnings are not posted if removed', async t => {
.click(contentWarningButton) .click(contentWarningButton)
.expect(composeContentWarning.exists).notOk() .expect(composeContentWarning.exists).notOk()
.click(composeButton) .click(composeButton)
.expect(getNthStatus(0).innerText).contains('hi this is another toot', { timeout: 30000 }) .expect(getNthStatus(1).innerText).contains('hi this is another toot', { timeout: 30000 })
.expect(getNthStatus(0).innerText).notContains('content warning!') .expect(getNthStatus(1).innerText).notContains('content warning!')
.expect($(`${getNthStatusSelector(0)} .status-content`).innerText).contains('hi this is another toot') .expect($(`${getNthStatusSelector(1)} .status-content`).innerText).contains('hi this is another toot')
}) })
test('content warnings can have emoji', async t => { test('content warnings can have emoji', async t => {
@ -43,10 +43,10 @@ test('content warnings can have emoji', async t => {
.click(contentWarningButton) .click(contentWarningButton)
.typeText(composeContentWarning, 'can you feel the :blobpats: tonight') .typeText(composeContentWarning, 'can you feel the :blobpats: tonight')
.click(composeButton) .click(composeButton)
.expect(getNthStatus(0).innerText).contains('can you feel the', { timeout: 30000 }) .expect(getNthStatus(1).innerText).contains('can you feel the', { timeout: 30000 })
.expect($(`${getNthStatusSelector(0)} .status-spoiler img.inline-custom-emoji`).getAttribute('alt')).eql(':blobpats:') .expect($(`${getNthStatusSelector(1)} .status-spoiler img.inline-custom-emoji`).getAttribute('alt')).eql(':blobpats:')
.click(getNthShowOrHideButton(0)) .click(getNthShowOrHideButton(1))
.expect($(`${getNthStatusSelector(0)} .status-content img.inline-custom-emoji`).getAttribute('alt')).eql(':blobnom:') .expect($(`${getNthStatusSelector(1)} .status-content img.inline-custom-emoji`).getAttribute('alt')).eql(':blobnom:')
}) })
test('no XSS in content warnings or text', async t => { test('no XSS in content warnings or text', async t => {
@ -58,7 +58,7 @@ test('no XSS in content warnings or text', async t => {
.click(contentWarningButton) .click(contentWarningButton)
.typeText(composeContentWarning, pwned2) .typeText(composeContentWarning, pwned2)
.click(composeButton) .click(composeButton)
.expect($(`${getNthStatusSelector(0)} .status-spoiler`).innerText).contains(pwned2) .expect($(`${getNthStatusSelector(1)} .status-spoiler`).innerText).contains(pwned2)
.click(getNthShowOrHideButton(0)) .click(getNthShowOrHideButton(1))
.expect($(`${getNthStatusSelector(0)} .status-content`).innerText).contains(pwned1) .expect($(`${getNthStatusSelector(1)} .status-content`).innerText).contains(pwned1)
}) })

View file

@ -15,9 +15,9 @@ test('replying to a toot returns focus to reply button', async t => {
await t await t
.typeText(composeInput, 'I would like, if I may, to take you on a strange journey', { paste: true }) .typeText(composeInput, 'I would like, if I may, to take you on a strange journey', { paste: true })
.pressKey('ctrl+enter') .pressKey('ctrl+enter')
.expect($(`${getNthStatusSelector(0)} .status-content`).innerText).contains('I would like, if I may, to take you on a strange journey') .expect($(`${getNthStatusSelector(1)} .status-content`).innerText).contains('I would like, if I may, to take you on a strange journey')
.click(getNthReplyButton(0)) .click(getNthReplyButton(1))
.typeText(getNthComposeReplyInput(0), 'How strange was it?', { paste: true }) .typeText(getNthComposeReplyInput(1), 'How strange was it?', { paste: true })
.click(getNthComposeReplyButton(0)) .click(getNthComposeReplyButton(1))
.expect(getActiveElementClassList()).contains('status-toolbar-reply-button', { timeout: 20000 }) .expect(getActiveElementClassList()).contains('status-toolbar-reply-button', { timeout: 20000 })
}) })

View file

@ -15,13 +15,13 @@ test('External links, hashtags, and mentions have correct attributes', async t =
'and also http://example.com and https://joinmastodon.org and ' + 'and also http://example.com and https://joinmastodon.org and ' +
'https://mastodon.social.' 'https://mastodon.social.'
const nthAnchor = n => $(`${getNthStatusSelector(0)} .status-content a`).nth(n) const nthAnchor = n => $(`${getNthStatusSelector(1)} .status-content a`).nth(n)
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.typeText(composeInput, text, { paste: true }) .typeText(composeInput, text, { paste: true })
.click(composeButton) .click(composeButton)
.expect(getNthStatus(0).innerText).contains('Why hello there', { timeout: 20000 }) .expect(getNthStatus(1).innerText).contains('Why hello there', { timeout: 20000 })
.expect(nthAnchor(0).getAttribute('href')).eql('/accounts/1') .expect(nthAnchor(0).getAttribute('href')).eql('/accounts/1')
.expect(nthAnchor(0).hasAttribute('rel')).notOk() .expect(nthAnchor(0).hasAttribute('rel')).notOk()
.expect(nthAnchor(0).getAttribute('title')).eql('@admin') .expect(nthAnchor(0).getAttribute('title')).eql('@admin')

View file

@ -15,8 +15,8 @@ test('Can block and unblock an account from a status', async t => {
await postAs('admin', post) await postAs('admin', post)
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.expect(getNthStatus(0).innerText).contains(post, { timeout: 30000 }) .expect(getNthStatus(1).innerText).contains(post, { timeout: 30000 })
.click(getNthStatusOptionsButton(0)) .click(getNthStatusOptionsButton(1))
.expect(getNthDialogOptionsOption(1).innerText).contains('Unfollow @admin') .expect(getNthDialogOptionsOption(1).innerText).contains('Unfollow @admin')
.expect(getNthDialogOptionsOption(2).innerText).contains('Block @admin') .expect(getNthDialogOptionsOption(2).innerText).contains('Block @admin')
.click(getNthDialogOptionsOption(2)) .click(getNthDialogOptionsOption(2))

View file

@ -23,8 +23,8 @@ test('Can mute and unmute an account', async t => {
let post = 'blah blah blah' let post = 'blah blah blah'
await postAs('admin', post) await postAs('admin', post)
await t.expect(getNthStatus(0).innerText).contains(post, { timeout: 20000 }) await t.expect(getNthStatus(1).innerText).contains(post, { timeout: 20000 })
.click(getNthStatusOptionsButton(0)) .click(getNthStatusOptionsButton(1))
.expect(getNthDialogOptionsOption(1).innerText).contains('Unfollow @admin') .expect(getNthDialogOptionsOption(1).innerText).contains('Unfollow @admin')
.expect(getNthDialogOptionsOption(2).innerText).contains('Block @admin') .expect(getNthDialogOptionsOption(2).innerText).contains('Block @admin')
.expect(getNthDialogOptionsOption(3).innerText).contains('Mute @admin') .expect(getNthDialogOptionsOption(3).innerText).contains('Mute @admin')

View file

@ -17,40 +17,40 @@ test('Can pin statuses', async t => {
await t await t
.typeText(composeInput, 'I am going to pin this', { paste: true }) .typeText(composeInput, 'I am going to pin this', { paste: true })
.click(postStatusButton) .click(postStatusButton)
.expect(getNthStatus(0).innerText).contains('I am going to pin this') .expect(getNthStatus(1).innerText).contains('I am going to pin this')
.click(avatarInComposeBox) .click(avatarInComposeBox)
.expect(getUrl()).contains(`/accounts/${users.foobar.id}`) .expect(getUrl()).contains(`/accounts/${users.foobar.id}`)
.expect(getNthPinnedStatus(0).getAttribute('aria-setsize')).eql('1') .expect(getNthPinnedStatus(1).getAttribute('aria-setsize')).eql('1')
.expect(getNthPinnedStatus(0).innerText).contains('this is unlisted') .expect(getNthPinnedStatus(1).innerText).contains('this is unlisted')
.expect(getNthStatus(0).innerText).contains('I am going to pin this') .expect(getNthStatus(1).innerText).contains('I am going to pin this')
.click(getNthStatusOptionsButton(0)) .click(getNthStatusOptionsButton(1))
.expect(getNthDialogOptionsOption(1).innerText).contains('Delete') .expect(getNthDialogOptionsOption(1).innerText).contains('Delete')
.expect(getNthDialogOptionsOption(2).innerText).contains('Pin to profile') .expect(getNthDialogOptionsOption(2).innerText).contains('Pin to profile')
.click(getNthDialogOptionsOption(2)) .click(getNthDialogOptionsOption(2))
.expect(getNthPinnedStatus(0).getAttribute('aria-setsize')).eql('2') .expect(getNthPinnedStatus(1).getAttribute('aria-setsize')).eql('2')
.expect(getNthPinnedStatus(0).innerText).contains('I am going to pin this') .expect(getNthPinnedStatus(1).innerText).contains('I am going to pin this')
.expect(getNthPinnedStatus(1).innerText).contains('this is unlisted') .expect(getNthPinnedStatus(2).innerText).contains('this is unlisted')
.expect(getNthStatus(0).innerText).contains('I am going to pin this') .expect(getNthStatus(1).innerText).contains('I am going to pin this')
.click(getNthStatusOptionsButton(0)) .click(getNthStatusOptionsButton(1))
.expect(getNthDialogOptionsOption(1).innerText).contains('Delete') .expect(getNthDialogOptionsOption(1).innerText).contains('Delete')
.expect(getNthDialogOptionsOption(2).innerText).contains('Unpin from profile') .expect(getNthDialogOptionsOption(2).innerText).contains('Unpin from profile')
.click(getNthDialogOptionsOption(2)) .click(getNthDialogOptionsOption(2))
.expect(getUrl()).contains(`/accounts/${users.foobar.id}`) .expect(getUrl()).contains(`/accounts/${users.foobar.id}`)
.expect(getNthPinnedStatus(0).getAttribute('aria-setsize')).eql('1') .expect(getNthPinnedStatus(1).getAttribute('aria-setsize')).eql('1')
.expect(getNthPinnedStatus(0).innerText).contains('this is unlisted') .expect(getNthPinnedStatus(1).innerText).contains('this is unlisted')
.expect(getNthStatus(0).innerText).contains('I am going to pin this') .expect(getNthStatus(1).innerText).contains('I am going to pin this')
}) })
test('Can favorite a pinned status', async t => { test('Can favorite a pinned status', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.click(avatarInComposeBox) .click(avatarInComposeBox)
.expect(getNthPinnedStatus(0).getAttribute('aria-setsize')).eql('1') .expect(getNthPinnedStatus(1).getAttribute('aria-setsize')).eql('1')
.expect(getNthPinnedStatusFavoriteButton(0).getAttribute('aria-pressed')).eql('false') .expect(getNthPinnedStatusFavoriteButton(1).getAttribute('aria-pressed')).eql('false')
.click(getNthPinnedStatusFavoriteButton(0)) .click(getNthPinnedStatusFavoriteButton(1))
.expect(getNthPinnedStatusFavoriteButton(0).getAttribute('aria-pressed')).eql('true') .expect(getNthPinnedStatusFavoriteButton(1).getAttribute('aria-pressed')).eql('true')
.click(getNthPinnedStatusFavoriteButton(0)) .click(getNthPinnedStatusFavoriteButton(1))
.expect(getNthPinnedStatusFavoriteButton(0).getAttribute('aria-pressed')).eql('false') .expect(getNthPinnedStatusFavoriteButton(1).getAttribute('aria-pressed')).eql('false')
}) })
test('Saved pinned/unpinned state of status', async t => { test('Saved pinned/unpinned state of status', async t => {
@ -58,13 +58,13 @@ test('Saved pinned/unpinned state of status', async t => {
await postAs('foobar', 'hey I am going to pin and unpin this') await postAs('foobar', 'hey I am going to pin and unpin this')
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.expect(getNthStatusContent(0).innerText).contains('hey I am going to pin and unpin this', { timeout }) .expect(getNthStatusContent(1).innerText).contains('hey I am going to pin and unpin this', { timeout })
.click(getNthStatusOptionsButton(0)) .click(getNthStatusOptionsButton(1))
.expect(getNthDialogOptionsOption(2).innerText).contains('Pin to profile') .expect(getNthDialogOptionsOption(2).innerText).contains('Pin to profile')
.click(getNthDialogOptionsOption(2)) .click(getNthDialogOptionsOption(2))
await sleep(1) await sleep(1)
await t await t
.click(getNthStatusOptionsButton(0)) .click(getNthStatusOptionsButton(1))
.expect(getNthDialogOptionsOption(2).innerText).contains('Unpin from profile') .expect(getNthDialogOptionsOption(2).innerText).contains('Unpin from profile')
.click(closeDialogButton) .click(closeDialogButton)
@ -74,13 +74,13 @@ test('Saved pinned/unpinned state of status', async t => {
await scrollToTop() await scrollToTop()
await t await t
.expect(getNthStatusContent(0).innerText).contains('hey I am going to pin and unpin this', { timeout }) .expect(getNthStatusContent(1).innerText).contains('hey I am going to pin and unpin this', { timeout })
.click(getNthStatusOptionsButton(0)) .click(getNthStatusOptionsButton(1))
.expect(getNthDialogOptionsOption(2).innerText).contains('Unpin from profile', { timeout }) .expect(getNthDialogOptionsOption(2).innerText).contains('Unpin from profile', { timeout })
// navigate to another page and back to force another unrender // navigate to another page and back to force another unrender
.click(settingsNavButton) .click(settingsNavButton)
.click(homeNavButton) .click(homeNavButton)
.expect(getNthStatusContent(0).innerText).contains('hey I am going to pin and unpin this', { timeout }) .expect(getNthStatusContent(1).innerText).contains('hey I am going to pin and unpin this', { timeout })
.click(getNthStatusOptionsButton(0)) .click(getNthStatusOptionsButton(1))
.expect(getNthDialogOptionsOption(2).innerText).contains('Unpin from profile', { timeout }) .expect(getNthDialogOptionsOption(2).innerText).contains('Unpin from profile', { timeout })
}) })

View file

@ -26,7 +26,7 @@ test('Can put custom emoji in display name', async t => {
.expect($('.compose-box-display-name img').getAttribute('alt')).eql(':blobpats:') .expect($('.compose-box-display-name img').getAttribute('alt')).eql(':blobpats:')
.click(displayNameInComposeBox) .click(displayNameInComposeBox)
.expect(getUrl()).contains('/accounts/2') .expect(getUrl()).contains('/accounts/2')
.expect($(`${getNthStatusSelector(0)} .status-author-name img`).getAttribute('alt')).eql(':blobpats:') .expect($(`${getNthStatusSelector(1)} .status-author-name img`).getAttribute('alt')).eql(':blobpats:')
}) })
test('Cannot XSS using display name HTML', async t => { test('Cannot XSS using display name HTML', async t => {
@ -97,7 +97,7 @@ test('Check status aria labels for de-emojified text', async t => {
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.click(displayNameInComposeBox) .click(displayNameInComposeBox)
.expect(getNthStatus(0).getAttribute('aria-label')).match( .expect(getNthStatus(1).getAttribute('aria-label')).match(
new RegExp(`${rainbow} foo :blobpats: ${rainbow}, hey ho lotsa emojos, (.* ago|just now), @foobar, Public`, 'i') new RegExp(`${rainbow} foo :blobpats: ${rainbow}, hey ho lotsa emojos, (.* ago|just now), @foobar, Public`, 'i')
) )
.click(settingsNavButton) .click(settingsNavButton)
@ -106,7 +106,7 @@ test('Check status aria labels for de-emojified text', async t => {
.expect(removeEmojiFromDisplayNamesInput.checked).ok() .expect(removeEmojiFromDisplayNamesInput.checked).ok()
.click(homeNavButton) .click(homeNavButton)
.click(displayNameInComposeBox) .click(displayNameInComposeBox)
.expect(getNthStatus(0).getAttribute('aria-label')).match( .expect(getNthStatus(1).getAttribute('aria-label')).match(
new RegExp(`foo, hey ho lotsa emojos, (.* ago|just now), @foobar, Public`, 'i') new RegExp(`foo, hey ho lotsa emojos, (.* ago|just now), @foobar, Public`, 'i')
) )
.click(settingsNavButton) .click(settingsNavButton)
@ -115,7 +115,7 @@ test('Check status aria labels for de-emojified text', async t => {
.expect(removeEmojiFromDisplayNamesInput.checked).notOk() .expect(removeEmojiFromDisplayNamesInput.checked).notOk()
.click(homeNavButton) .click(homeNavButton)
.click(displayNameInComposeBox) .click(displayNameInComposeBox)
.expect(getNthStatus(0).getAttribute('aria-label')).match( .expect(getNthStatus(1).getAttribute('aria-label')).match(
new RegExp(`${rainbow} foo :blobpats: ${rainbow}, hey ho lotsa emojos, (.* ago|just now), @foobar, Public`, 'i') new RegExp(`${rainbow} foo :blobpats: ${rainbow}, hey ho lotsa emojos, (.* ago|just now), @foobar, Public`, 'i')
) )
}) })

View file

@ -14,23 +14,23 @@ test('Fav stats update', async t => {
await favoriteStatusAs('admin', statusId) await favoriteStatusAs('admin', statusId)
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.expect(getNthStatusContent(0).innerText).contains('hey hello look at this toot') .expect(getNthStatusContent(1).innerText).contains('hey hello look at this toot')
.click(getNthStatus(0)) .click(getNthStatus(1))
.expect(getFavoritesCount()).eql(1) .expect(getFavoritesCount()).eql(1)
.click(homeNavButton) .click(homeNavButton)
await favoriteStatusAs('quux', statusId) await favoriteStatusAs('quux', statusId)
await t await t
.click(getNthStatus(0)) .click(getNthStatus(1))
.expect(getFavoritesCount()).eql(2) .expect(getFavoritesCount()).eql(2)
.click(homeNavButton) .click(homeNavButton)
await favoriteStatusAs('baz', statusId) await favoriteStatusAs('baz', statusId)
await t await t
.click(getNthStatus(0)) .click(getNthStatus(1))
.expect(getFavoritesCount()).eql(3) .expect(getFavoritesCount()).eql(3)
.click(homeNavButton) .click(homeNavButton)
await favoriteStatusAs('LockedAccount', statusId) await favoriteStatusAs('LockedAccount', statusId)
await t await t
.click(getNthStatus(0)) .click(getNthStatus(1))
.expect(getFavoritesCount()).eql(4) .expect(getFavoritesCount()).eql(4)
}) })
@ -40,23 +40,23 @@ test('Reblog stats update', async t => {
await reblogStatusAs('admin', statusId) await reblogStatusAs('admin', statusId)
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.expect(getNthStatusContent(0).innerText).contains('oh why hello it looks like another toot') .expect(getNthStatusContent(1).innerText).contains('oh why hello it looks like another toot')
.click(getNthStatus(0)) .click(getNthStatus(1))
.expect(getReblogsCount()).eql(1) .expect(getReblogsCount()).eql(1)
.click(homeNavButton) .click(homeNavButton)
await reblogStatusAs('quux', statusId) await reblogStatusAs('quux', statusId)
await t await t
.click(getNthStatus(0)) .click(getNthStatus(1))
.expect(getReblogsCount()).eql(2) .expect(getReblogsCount()).eql(2)
.click(homeNavButton) .click(homeNavButton)
await reblogStatusAs('baz', statusId) await reblogStatusAs('baz', statusId)
await t await t
.click(getNthStatus(0)) .click(getNthStatus(1))
.expect(getReblogsCount()).eql(3) .expect(getReblogsCount()).eql(3)
.click(homeNavButton) .click(homeNavButton)
await reblogStatusAs('LockedAccount', statusId) await reblogStatusAs('LockedAccount', statusId)
await t await t
.click(getNthStatus(0)) .click(getNthStatus(1))
.expect(getReblogsCount()).eql(4) .expect(getReblogsCount()).eql(4)
}) })
@ -68,20 +68,20 @@ test('Fav and reblog stats update for a boosted toot', async t => {
await favoriteStatusAs('quux', statusId) await favoriteStatusAs('quux', statusId)
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.expect(getNthStatusContent(0).innerText).contains('this will get boosted') .expect(getNthStatusContent(1).innerText).contains('this will get boosted')
.click(getNthStatus(0)) .click(getNthStatus(1))
.expect(getReblogsCount()).eql(1) .expect(getReblogsCount()).eql(1)
.expect(getFavoritesCount()).eql(2) .expect(getFavoritesCount()).eql(2)
.click(homeNavButton) .click(homeNavButton)
await favoriteStatusAs('baz', statusId) await favoriteStatusAs('baz', statusId)
await t await t
.click(getNthStatus(0)) .click(getNthStatus(1))
.expect(getReblogsCount()).eql(1) .expect(getReblogsCount()).eql(1)
.expect(getFavoritesCount()).eql(3) .expect(getFavoritesCount()).eql(3)
.click(homeNavButton) .click(homeNavButton)
await favoriteStatusAs('LockedAccount', statusId) await favoriteStatusAs('LockedAccount', statusId)
await t await t
.click(getNthStatus(0)) .click(getNthStatus(1))
.expect(getReblogsCount()).eql(1) .expect(getReblogsCount()).eql(1)
.expect(getFavoritesCount()).eql(4) .expect(getFavoritesCount()).eql(4)
}) })

View file

@ -9,8 +9,8 @@ test('aria-labels for statuses with no content text', async t => {
await postEmptyStatusWithMediaAs('foobar', 'kitten1.jpg', 'kitteh') await postEmptyStatusWithMediaAs('foobar', 'kitten1.jpg', 'kitteh')
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.hover(getNthStatus(0)) .hover(getNthStatus(1))
.expect(getNthStatus(0).getAttribute('aria-label')).match( .expect(getNthStatus(1).getAttribute('aria-label')).match(
/foobar, (.+ ago|just now), @foobar, Public/i /foobar, (.+ ago|just now), @foobar, Public/i
) )
}) })

View file

@ -24,9 +24,9 @@ test('basic delete and redraft', async t => {
await postAs('foobar', 'hey ho this is grate') await postAs('foobar', 'hey ho this is grate')
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.hover(getNthStatus(0)) .hover(getNthStatus(1))
.expect(getNthStatusContent(0).innerText).contains('hey ho this is grate') .expect(getNthStatusContent(1).innerText).contains('hey ho this is grate')
.click(getNthStatusOptionsButton(0)) .click(getNthStatusOptionsButton(1))
.click(dialogOptionsOption.withText('Delete and redraft')) .click(dialogOptionsOption.withText('Delete and redraft'))
.expect(modalDialog.hasAttribute('aria-hidden')).notOk() .expect(modalDialog.hasAttribute('aria-hidden')).notOk()
.expect(composeModalInput.value).contains('hey ho this is grate') .expect(composeModalInput.value).contains('hey ho this is grate')
@ -34,16 +34,16 @@ test('basic delete and redraft', async t => {
.typeText(composeModalInput, 'hey ho this is great', { replace: true, paste: true }) .typeText(composeModalInput, 'hey ho this is great', { replace: true, paste: true })
.click(composeModalComposeButton) .click(composeModalComposeButton)
.expect(modalDialog.exists).notOk() .expect(modalDialog.exists).notOk()
.expect(getNthStatusContent(0).innerText).contains('hey ho this is great') .expect(getNthStatusContent(1).innerText).contains('hey ho this is great')
}) })
test('image with empty text delete and redraft', async t => { test('image with empty text delete and redraft', async t => {
await postEmptyStatusWithMediaAs('foobar', 'kitten2.jpg', 'what a kitteh') await postEmptyStatusWithMediaAs('foobar', 'kitten2.jpg', 'what a kitteh')
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.hover(getNthStatus(0)) .hover(getNthStatus(1))
.expect(getNthStatusMediaImg(0).getAttribute('alt')).eql('what a kitteh') .expect(getNthStatusMediaImg(1).getAttribute('alt')).eql('what a kitteh')
.click(getNthStatusOptionsButton(0)) .click(getNthStatusOptionsButton(1))
.click(dialogOptionsOption.withText('Delete and redraft')) .click(dialogOptionsOption.withText('Delete and redraft'))
.expect(modalDialog.hasAttribute('aria-hidden')).notOk() .expect(modalDialog.hasAttribute('aria-hidden')).notOk()
.expect(composeModalInput.value).eql('') .expect(composeModalInput.value).eql('')
@ -53,17 +53,17 @@ test('image with empty text delete and redraft', async t => {
.typeText(composeModalInput, 'I love this kitteh', { replace: true, paste: true }) .typeText(composeModalInput, 'I love this kitteh', { replace: true, paste: true })
.click(composeModalComposeButton) .click(composeModalComposeButton)
.expect(modalDialog.exists).notOk() .expect(modalDialog.exists).notOk()
.expect(getNthStatusContent(0).innerText).contains('I love this kitteh') .expect(getNthStatusContent(1).innerText).contains('I love this kitteh')
.expect(getNthStatusMediaImg(0).getAttribute('alt')).eql('what a kitteh') .expect(getNthStatusMediaImg(1).getAttribute('alt')).eql('what a kitteh')
}) })
test('image with no alt delete and redraft', async t => { test('image with no alt delete and redraft', async t => {
await postEmptyStatusWithMediaAs('foobar', 'kitten3.jpg', '') await postEmptyStatusWithMediaAs('foobar', 'kitten3.jpg', '')
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.hover(getNthStatus(0)) .hover(getNthStatus(1))
.expect(getNthStatusMediaImg(0).getAttribute('alt')).eql('') .expect(getNthStatusMediaImg(1).getAttribute('alt')).eql('')
.click(getNthStatusOptionsButton(0)) .click(getNthStatusOptionsButton(1))
.click(dialogOptionsOption.withText('Delete and redraft')) .click(dialogOptionsOption.withText('Delete and redraft'))
.expect(modalDialog.hasAttribute('aria-hidden')).notOk() .expect(modalDialog.hasAttribute('aria-hidden')).notOk()
.expect(composeModalInput.value).eql('') .expect(composeModalInput.value).eql('')
@ -74,17 +74,17 @@ test('image with no alt delete and redraft', async t => {
.typeText(getComposeModalNthMediaAltInput(1), 'lovely kitteh', { replace: true, paste: true }) .typeText(getComposeModalNthMediaAltInput(1), 'lovely kitteh', { replace: true, paste: true })
.click(composeModalComposeButton) .click(composeModalComposeButton)
.expect(modalDialog.exists).notOk() .expect(modalDialog.exists).notOk()
.expect(getNthStatusContent(0).innerText).contains('oops forgot an alt') .expect(getNthStatusContent(1).innerText).contains('oops forgot an alt')
.expect(getNthStatusMediaImg(0).getAttribute('alt')).eql('lovely kitteh') .expect(getNthStatusMediaImg(1).getAttribute('alt')).eql('lovely kitteh')
}) })
test('privacy and spoiler delete and redraft', async t => { test('privacy and spoiler delete and redraft', async t => {
await postWithSpoilerAndPrivacyAs('foobar', 'this is hidden', 'click to see!', 'private') await postWithSpoilerAndPrivacyAs('foobar', 'this is hidden', 'click to see!', 'private')
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.hover(getNthStatus(0)) .hover(getNthStatus(1))
.expect(getNthStatusSpoiler(0).innerText).contains('click to see!') .expect(getNthStatusSpoiler(1).innerText).contains('click to see!')
.click(getNthStatusOptionsButton(0)) .click(getNthStatusOptionsButton(1))
.click(dialogOptionsOption.withText('Delete and redraft')) .click(dialogOptionsOption.withText('Delete and redraft'))
.expect(modalDialog.hasAttribute('aria-hidden')).notOk() .expect(modalDialog.hasAttribute('aria-hidden')).notOk()
.expect(composeModalInput.value).eql('this is hidden') .expect(composeModalInput.value).eql('this is hidden')
@ -93,57 +93,57 @@ test('privacy and spoiler delete and redraft', async t => {
.typeText(composeModalContentWarningInput, 'no really, you should click this!', { replace: true, paste: true }) .typeText(composeModalContentWarningInput, 'no really, you should click this!', { replace: true, paste: true })
.click(composeModalComposeButton) .click(composeModalComposeButton)
.expect(modalDialog.exists).notOk() .expect(modalDialog.exists).notOk()
.expect(getNthStatusSpoiler(0).innerText).contains('no really, you should click this!') .expect(getNthStatusSpoiler(1).innerText).contains('no really, you should click this!')
}) })
test('delete and redraft reply', async t => { test('delete and redraft reply', async t => {
await postAs('admin', 'hey hello') await postAs('admin', 'hey hello')
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.hover(getNthStatus(0)) .hover(getNthStatus(1))
.expect(getNthStatusContent(0).innerText).contains('hey hello') .expect(getNthStatusContent(1).innerText).contains('hey hello')
.click(getNthReplyButton(0)) .click(getNthReplyButton(1))
.typeText(getNthComposeReplyInput(0), 'hello there admin', { paste: true }) .typeText(getNthComposeReplyInput(1), 'hello there admin', { paste: true })
.click(getNthComposeReplyButton(0)) .click(getNthComposeReplyButton(1))
.expect(getNthStatus(0).innerText).contains('@admin hello there admin') .expect(getNthStatus(1).innerText).contains('@admin hello there admin')
.click(getNthStatusOptionsButton(0)) .click(getNthStatusOptionsButton(1))
.click(dialogOptionsOption.withText('Delete and redraft')) .click(dialogOptionsOption.withText('Delete and redraft'))
.expect(modalDialog.hasAttribute('aria-hidden')).notOk() .expect(modalDialog.hasAttribute('aria-hidden')).notOk()
.typeText(composeModalInput, ' oops forgot to say thank you') .typeText(composeModalInput, ' oops forgot to say thank you')
.click(composeModalComposeButton) .click(composeModalComposeButton)
.expect(modalDialog.exists).notOk() .expect(modalDialog.exists).notOk()
.expect(getNthStatusContent(0).innerText).match(/@admin hello there admin\s+oops forgot to say thank you/, { .expect(getNthStatusContent(1).innerText).match(/@admin hello there admin\s+oops forgot to say thank you/, {
timeout: 30000 timeout: 30000
}) })
.click(getNthStatus(0)) .click(getNthStatus(1))
.expect(getUrl()).match(/statuses/) .expect(getUrl()).match(/statuses/)
.expect(getNthStatusContent(0).innerText).contains('hey hello') .expect(getNthStatusContent(1).innerText).contains('hey hello')
.expect(getNthStatusContent(1).innerText).match(/@admin hello there admin\s+oops forgot to say thank you/) .expect(getNthStatusContent(2).innerText).match(/@admin hello there admin\s+oops forgot to say thank you/)
}) })
test('delete and redraft reply within thread', async t => { test('delete and redraft reply within thread', async t => {
await postAs('admin', 'this is a thread') await postAs('admin', 'this is a thread')
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.hover(getNthStatus(0)) .hover(getNthStatus(1))
.expect(getNthStatusContent(0).innerText).contains('this is a thread') .expect(getNthStatusContent(1).innerText).contains('this is a thread')
.click(getNthStatus(0)) .click(getNthStatus(1))
.expect(getUrl()).match(/statuses/) .expect(getUrl()).match(/statuses/)
.expect(getNthStatusContent(0).innerText).contains('this is a thread') .expect(getNthStatusContent(1).innerText).contains('this is a thread')
.click(getNthReplyButton(0)) .click(getNthReplyButton(1))
.typeText(getNthComposeReplyInput(0), 'heyo', { paste: true }) .typeText(getNthComposeReplyInput(1), 'heyo', { paste: true })
.click(getNthComposeReplyButton(0)) .click(getNthComposeReplyButton(1))
.expect(getNthStatus(1).innerText).contains('@admin heyo') .expect(getNthStatus(2).innerText).contains('@admin heyo')
.click(getNthStatusOptionsButton(1)) .click(getNthStatusOptionsButton(2))
.click(dialogOptionsOption.withText('Delete and redraft')) .click(dialogOptionsOption.withText('Delete and redraft'))
.expect(modalDialog.hasAttribute('aria-hidden')).notOk() .expect(modalDialog.hasAttribute('aria-hidden')).notOk()
.typeText(composeModalInput, ' update!', { paste: true }) .typeText(composeModalInput, ' update!', { paste: true })
.click(composeModalComposeButton) .click(composeModalComposeButton)
.expect(modalDialog.exists).notOk() .expect(modalDialog.exists).notOk()
.expect(getNthStatusContent(1).innerText).match(/@admin heyo\s+update!/, { .expect(getNthStatusContent(2).innerText).match(/@admin heyo\s+update!/, {
timeout: 30000 timeout: 30000
}) })
.expect(getNthStatus(2).exists).notOk() .expect(getNthStatus(3).exists).notOk()
}) })
test('multiple paragraphs', async t => { test('multiple paragraphs', async t => {
@ -151,14 +151,14 @@ test('multiple paragraphs', async t => {
await postAs('foobar', text) await postAs('foobar', text)
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.hover(getNthStatus(0)) .hover(getNthStatus(1))
.expect(getNthStatusContent(0).innerText).contains(text) .expect(getNthStatusContent(1).innerText).contains(text)
.click(getNthStatusOptionsButton(0)) .click(getNthStatusOptionsButton(1))
.click(dialogOptionsOption.withText('Delete and redraft')) .click(dialogOptionsOption.withText('Delete and redraft'))
.expect(modalDialog.hasAttribute('aria-hidden')).notOk() .expect(modalDialog.hasAttribute('aria-hidden')).notOk()
.expect(composeModalInput.value).eql(text) .expect(composeModalInput.value).eql(text)
.typeText(composeModalInput, '\n\nwoot', { paste: true }) .typeText(composeModalInput, '\n\nwoot', { paste: true })
.click(composeModalComposeButton) .click(composeModalComposeButton)
.expect(modalDialog.exists).notOk() .expect(modalDialog.exists).notOk()
.expect(getNthStatusContent(0).innerText).contains(text + '\n\nwoot') .expect(getNthStatusContent(1).innerText).contains(text + '\n\nwoot')
}) })

View file

@ -37,13 +37,13 @@ test('reply to non-focused parent status in a thread', async t => {
await postReplyAs('admin', 'and here it is continued', id) await postReplyAs('admin', 'and here it is continued', id)
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.hover(getNthStatus(0)) .hover(getNthStatus(1))
.expect(getNthStatusContent(0).innerText).contains('and here it is continued') .expect(getNthStatusContent(1).innerText).contains('and here it is continued')
.click(getNthStatus(0)) .click(getNthStatus(1))
.expect(getUrl()).match(/statuses/) .expect(getUrl()).match(/statuses/)
.click(getNthReplyButton(0)) .click(getNthReplyButton(1))
.typeText(getNthComposeReplyInput(0), 'haha I totally agree', { paste: true }) .typeText(getNthComposeReplyInput(1), 'haha I totally agree', { paste: true })
.click(getNthComposeReplyButton(0)) .click(getNthComposeReplyButton(1))
await verifyAriaSetSize(t, '2') await verifyAriaSetSize(t, '2')
}) })
@ -53,14 +53,14 @@ test('reply to focused status in a thread', async t => {
await postReplyAs('admin', 'whoa and here it is probably continued', id) await postReplyAs('admin', 'whoa and here it is probably continued', id)
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.hover(getNthStatus(0)) .hover(getNthStatus(1))
.expect(getNthStatusContent(0).innerText).contains('whoa and here it is probably continued') .expect(getNthStatusContent(1).innerText).contains('whoa and here it is probably continued')
.hover(getNthStatusContent(1)) .hover(getNthStatusContent(2))
.click(getNthStatus(1)) .click(getNthStatus(2))
.expect(getUrl()).match(/statuses/) .expect(getUrl()).match(/statuses/)
.click(getNthReplyButton(0)) .click(getNthReplyButton(1))
.typeText(getNthComposeReplyInput(0), 'haha I totally agree', { paste: true }) .typeText(getNthComposeReplyInput(1), 'haha I totally agree', { paste: true })
.click(getNthComposeReplyButton(0)) .click(getNthComposeReplyButton(1))
await verifyAriaSetSize(t, '3') await verifyAriaSetSize(t, '3')
}) })
@ -72,16 +72,16 @@ test('reply to non-focused grandparent status in a thread', async t => {
await postReplyAs('admin', 'cool thread 3/3', id2) await postReplyAs('admin', 'cool thread 3/3', id2)
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.hover(getNthStatus(0)) .hover(getNthStatus(1))
.expect(getNthStatusContent(0).innerText).contains('cool thread 3/3') .expect(getNthStatusContent(1).innerText).contains('cool thread 3/3')
.click(getNthStatus(0)) .click(getNthStatus(1))
.expect(getUrl()).match(/statuses/) .expect(getUrl()).match(/statuses/)
.hover(getNthStatus(3))
.hover(getNthStatus(2)) .hover(getNthStatus(2))
.hover(getNthStatus(1)) .hover(getNthStatus(1))
.hover(getNthStatus(0)) .click(getNthReplyButton(1))
.click(getNthReplyButton(0)) .typeText(getNthComposeReplyInput(1), 'this is sweet', { paste: true })
.typeText(getNthComposeReplyInput(0), 'this is sweet', { paste: true }) .click(getNthComposeReplyButton(1))
.click(getNthComposeReplyButton(0))
await verifyAriaSetSize(t, '3') await verifyAriaSetSize(t, '3')
}) })
@ -92,17 +92,17 @@ test('reply to non-focused grandchild status in a thread', async t => {
await postReplyAs('admin', 'sweet thread 3/3', id2) await postReplyAs('admin', 'sweet thread 3/3', id2)
await loginAsFoobar(t) await loginAsFoobar(t)
await t await t
.hover(getNthStatus(0)) .hover(getNthStatus(1))
.expect(getNthStatusContent(0).innerText).contains('sweet thread 3/3') .expect(getNthStatusContent(1).innerText).contains('sweet thread 3/3')
.hover(getNthStatusContent(1)) .hover(getNthStatusContent(2))
.hover(getNthStatus(2)) .hover(getNthStatus(3))
.click(getNthStatus(2)) .click(getNthStatus(3))
.expect(getUrl()).match(/statuses/) .expect(getUrl()).match(/statuses/)
.hover(getNthStatus(0))
.hover(getNthStatus(1)) .hover(getNthStatus(1))
.hover(getNthStatus(2)) .hover(getNthStatus(2))
.click(getNthReplyButton(2)) .hover(getNthStatus(3))
.typeText(getNthComposeReplyInput(2), 'this is sweet', { paste: true }) .click(getNthReplyButton(3))
.click(getNthComposeReplyButton(2)) .typeText(getNthComposeReplyInput(3), 'this is sweet', { paste: true })
.click(getNthComposeReplyButton(3))
await verifyAriaSetSize(t, '4') await verifyAriaSetSize(t, '4')
}) })

View file

@ -216,7 +216,7 @@ export function getNthDeleteMediaButton (n) {
} }
export function getAriaSetSize () { export function getAriaSetSize () {
return getNthStatus(0).getAttribute('aria-setsize') return getNthStatus(1 + 0).getAttribute('aria-setsize')
} }
export function getNthStatus (n) { export function getNthStatus (n) {
@ -336,25 +336,25 @@ export async function validateTimeline (t, timeline) {
for (let i = 0; i < timeline.length; i++) { for (let i = 0; i < timeline.length; i++) {
let status = timeline[i] let status = timeline[i]
// hovering forces TestCafé to scroll to that element: https://git.io/vABV2 // hovering forces TestCafé to scroll to that element: https://git.io/vABV2
await t.hover(getNthStatus(i)) await t.hover(getNthStatus(1 + i))
if (status.content) { if (status.content) {
await t.expect(getNthStatusContent(i).innerText) await t.expect(getNthStatusContent(1 + i).innerText)
.contains(status.content, { timeout }) .contains(status.content, { timeout })
} }
if (status.spoiler) { if (status.spoiler) {
await t.expect(getNthStatusSpoiler(i).innerText) await t.expect(getNthStatusSpoiler(1 + i).innerText)
.contains(status.spoiler, { timeout }) .contains(status.spoiler, { timeout })
} }
if (status.followedBy) { if (status.followedBy) {
await t.expect(getNthStatusHeader(i).innerText) await t.expect(getNthStatusHeader(1 + i).innerText)
.match(new RegExp(status.followedBy + '\\s+followed you'), { timeout }) .match(new RegExp(status.followedBy + '\\s+followed you'), { timeout })
} }
if (status.rebloggedBy) { if (status.rebloggedBy) {
await t.expect(getNthStatusHeader(i).innerText) await t.expect(getNthStatusHeader(1 + i).innerText)
.match(new RegExp(status.rebloggedBy + '\\s+boosted your status'), { timeout }) .match(new RegExp(status.rebloggedBy + '\\s+boosted your status'), { timeout })
} }
if (status.favoritedBy) { if (status.favoritedBy) {
await t.expect(getNthStatusHeader(i).innerText) await t.expect(getNthStatusHeader(1 + i).innerText)
.match(new RegExp(status.favoritedBy + '\\s+favorited your status'), { timeout }) .match(new RegExp(status.favoritedBy + '\\s+favorited your status'), { timeout })
} }
} }
@ -362,14 +362,13 @@ export async function validateTimeline (t, timeline) {
export async function scrollToStatus (t, n) { export async function scrollToStatus (t, n) {
let timeout = 20000 let timeout = 20000
for (let i = 0; i <= n; i++) { for (let i = 1; i < n; i++) {
await t.expect(getNthStatus(i).exists).ok({ timeout }) await t.expect(getNthStatus(i).exists).ok({ timeout })
.hover(getNthStatus(i)) .hover(getNthStatus(i))
.expect($('.loading-footer').exist).notOk() .expect($('.loading-footer').exist).notOk({ timeout })
if (i < n) { .expect($(`${getNthStatusSelector(i)} .status-toolbar`).exists).ok({ timeout })
await t.hover($(`${getNthStatusSelector(i)} .status-toolbar`)) .hover($(`${getNthStatusSelector(i)} .status-toolbar`))
.expect($('.loading-footer').exist).notOk() .expect($('.loading-footer').exist).notOk({ timeout })
}
} }
await t.hover(getNthStatus(n)) await t.hover(getNthStatus(n))
} }