Remove testcafe roles and run in parallel x4 (#334)

* more attempts to fix test flakiness

* remove testcafe roles entirely

* really remove testcafe roles

* run testcafe in parallel x2

* run testcafe in parallel x4

* fix online/offline forcing in tests

* fix pin test
This commit is contained in:
Nolan Lawson 2018-05-26 13:51:41 -07:00 committed by GitHub
parent 520bf7456b
commit efdb0bc534
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 300 additions and 191 deletions

View File

@ -8,19 +8,22 @@
"dev": "run-s build-svg build-inline-script serve-dev",
"serve-dev": "run-p --race build-sass-watch serve",
"serve": "node server.js",
"build": "cross-env NODE_ENV=production run-s globalize-css build-sass build-svg build-inline-script sapper-build deglobalize-css",
"sapper-build": "cross-env NODE_ENV=production sapper build",
"start": "cross-env NODE_ENV=production node server.js",
"build": "cross-env NODE_ENV=production npm run build-steps",
"build-steps": "run-s globalize-css build-sass build-svg build-inline-script sapper-build deglobalize-css",
"sapper-build": "sapper build",
"start": "cross-env NODE_ENV=production npm run serve",
"build-and-start": "run-s build start",
"build-svg": "node ./bin/build-svg.js",
"build-inline-script": "node ./bin/build-inline-script.js",
"build-sass": "node ./bin/build-sass.js",
"build-sass-watch": "node ./bin/build-sass.js --watch",
"run-mastodon": "node -r esm ./bin/run-mastodon.js",
"run-testcafe": "cross-env-shell testcafe --hostname localhost --skip-js-errors $BROWSER tests/spec",
"testcafe": "run-s testcafe-p testcafe-s",
"testcafe-p": "cross-env-shell testcafe --hostname localhost --skip-js-errors -c 4 $BROWSER tests/spec/0*",
"testcafe-s": "cross-env-shell testcafe --hostname localhost --skip-js-errors $BROWSER tests/spec/1*",
"test": "cross-env BROWSER=chrome:headless npm run test-browser",
"test-browser": "run-p --race run-mastodon dev test-mastodon",
"test-mastodon": "run-s wait-for-mastodon-to-start wait-for-mastodon-data run-testcafe",
"test-browser": "run-p --race run-mastodon build-and-start test-mastodon",
"test-mastodon": "run-s wait-for-mastodon-to-start wait-for-mastodon-data testcafe",
"wait-for-mastodon-to-start": "node -r esm bin/wait-for-mastodon-to-start.js",
"wait-for-mastodon-data": "node -r esm bin/wait-for-mastodon-data.js",
"globalize-css": "node ./bin/globalize-css.js",

View File

@ -53,14 +53,13 @@
export default {
oncreate () {
if (process.env.NODE_ENV !== 'production') {
window.__fakeFileInput = (file) => {
this.onFileChange({
target: {
files: [file]
}
})
}
// for testing
window.__fakeFileInput = (file) => {
this.onFileChange({
target: {
files: [file]
}
})
}
},
components: {

View File

@ -58,3 +58,8 @@ observers(store)
if (process.browser && process.env.NODE_ENV !== 'production') {
window.store = store // for debugging
}
// needed for tests
if (process.browser) {
window.__forceOnline = online => store.set({online})
}

View File

@ -1,4 +1,3 @@
import { Role } from 'testcafe'
import {
authorizeInput, emailInput, getUrl, instanceInput, mastodonLogInButton,
passwordInput,
@ -21,10 +20,21 @@ async function login (t, username, password) {
.expect(getUrl()).eql('http://localhost:4002/', {timeout: 30000})
}
export const foobarRole = Role('http://localhost:4002/settings/instances/add', async t => {
await login(t, users.foobar.email, users.foobar.password)
})
// roles appear not to be working anymore :(
// export const foobarRole = Role('http://localhost:4002/settings/instances/add', async t => {
// await login(t, users.foobar.email, users.foobar.password)
// })
//
// export const lockedAccountRole = Role('http://localhost:4002/settings/instances/add', async t => {
// await login(t, users.LockedAccount.email, users.LockedAccount.password)
// })
export const lockedAccountRole = Role('http://localhost:4002/settings/instances/add', async t => {
export async function loginAsFoobar (t) {
await t.navigateTo('/settings/instances/add')
await login(t, users.foobar.email, users.foobar.password)
}
export async function loginAsLockedAccount (t) {
await t.navigateTo('/settings/instances/add')
await login(t, users.LockedAccount.email, users.LockedAccount.password)
})
}

View File

@ -1,13 +1,18 @@
import { Selector as $ } from 'testcafe'
import { getFirstVisibleStatus, getNthStatus, getUrl, validateTimeline } from '../utils'
import {
communityNavButton,
getFirstVisibleStatus, getNthStatus, getUrl, localTimelineNavButton, notificationsNavButton,
validateTimeline
} from '../utils'
import { homeTimeline, notifications, localTimeline, favorites } from '../fixtures'
import { foobarRole } from '../roles'
import { loginAsFoobar } from '../roles'
fixture`003-basic-timeline-spec.js`
.page`http://localhost:4002`
test('Shows the home timeline', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.hover(getNthStatus(0))
.expect(getFirstVisibleStatus().exists).ok()
.expect(getFirstVisibleStatus().hasAttribute('aria-setsize')).ok()
@ -19,24 +24,27 @@ test('Shows the home timeline', async t => {
})
test('Shows notifications', async t => {
await t.useRole(foobarRole)
.click($('nav a[aria-label=Notifications]'))
await loginAsFoobar(t)
await t
.click(notificationsNavButton)
.expect(getUrl()).contains('/notifications')
await validateTimeline(t, notifications)
})
test('Shows the local timeline', async t => {
await t.useRole(foobarRole)
.click($('nav a[aria-label=Local]'))
await loginAsFoobar(t)
await t
.click(localTimelineNavButton)
.expect(getUrl()).contains('/local')
await validateTimeline(t, localTimeline)
})
test('Shows the federated timeline', async t => {
await t.useRole(foobarRole)
.click($('nav a[aria-label=Community]'))
await loginAsFoobar(t)
await t
.click(communityNavButton)
.expect(getUrl()).contains('/community')
.click($('a').withText('Federated'))
.expect(getUrl()).contains('/federated')
@ -45,8 +53,9 @@ test('Shows the federated timeline', async t => {
})
test('Shows favorites', async t => {
await t.useRole(foobarRole)
.click($('nav a[aria-label=Community]'))
await loginAsFoobar(t)
await t
.click(communityNavButton)
.expect(getUrl()).contains('/community')
.click($('a').withText('Favorites'))
.expect(getUrl()).contains('/favorites')

View File

@ -1,12 +1,13 @@
import { Selector as $ } from 'testcafe'
import { communityNavButton, getNthPinnedStatus, getUrl } from '../utils'
import { foobarRole } from '../roles'
import { loginAsFoobar } from '../roles'
fixture`004-pinned-statuses.js`
.page`http://localhost:4002`
test("shows a user's pinned statuses", async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.click(communityNavButton)
.expect(getUrl()).contains('/community')
.click($('a[href="/pinned"]'))
@ -17,7 +18,8 @@ test("shows a user's pinned statuses", async t => {
})
test("shows pinned statuses on a user's account page", async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.navigateTo('/accounts/2')
.expect(getNthPinnedStatus(0).getAttribute('aria-posinset')).eql('0')
.expect(getNthPinnedStatus(0).getAttribute('aria-setsize')).eql('1')
@ -25,7 +27,8 @@ test("shows pinned statuses on a user's account page", async t => {
})
test("shows pinned statuses on a user's account page 2", async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.navigateTo('/accounts/3')
.expect(getNthPinnedStatus(0).getAttribute('aria-posinset')).eql('0')
.expect(getNthPinnedStatus(0).getAttribute('aria-setsize')).eql('2')

View File

@ -1,11 +1,12 @@
import { getNthStatus } from '../utils'
import { foobarRole } from '../roles'
import { loginAsFoobar } from '../roles'
fixture`005-status-types.js`
.page`http://localhost:4002`
test('shows direct vs followers-only vs regular', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.expect(getNthStatus(1).getAttribute('aria-label')).eql('Status by admin')
.expect(getNthStatus(1).find('.status-content').innerText).contains('notification of unlisted message')
.expect(getNthStatus(1).find('.status-toolbar button:nth-child(2)').getAttribute('aria-label'))
@ -24,7 +25,8 @@ test('shows direct vs followers-only vs regular', async t => {
})
test('shows direct vs followers-only vs regular in notifications', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.navigateTo('/notifications')
.expect(getNthStatus(2).getAttribute('aria-label')).eql('Status by admin')
.expect(getNthStatus(2).find('.status-content').innerText).contains('notification of unlisted message')

View File

@ -1,12 +1,13 @@
import { Selector as $ } from 'testcafe'
import { getNthStatus } from '../utils'
import { foobarRole } from '../roles'
import { loginAsFoobar } from '../roles'
fixture`006-tabindex.js`
.page`http://localhost:4002`
test('shows correct tabindex in home timeline', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.expect(getNthStatus(0).getAttribute('tabindex')).eql('0')
.expect(getNthStatus(1).getAttribute('tabindex')).eql('0')
.expect(getNthStatus(2).getAttribute('tabindex')).eql('0')
@ -14,7 +15,8 @@ test('shows correct tabindex in home timeline', async t => {
})
test('shows correct tabindex in notifications', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.navigateTo('/notifications')
.expect(getNthStatus(0).getAttribute('tabindex')).eql('0')
.expect(getNthStatus(1).getAttribute('tabindex')).eql('0')
@ -31,7 +33,8 @@ test('shows correct tabindex in notifications', async t => {
})
test('shows correct tabindex in pinned statuses', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.navigateTo('/pinned')
.expect($('.status-article').getAttribute('tabindex')).eql('0')
.expect($('.status-article').getAttribute('aria-posinset')).eql('0')

View File

@ -4,14 +4,15 @@ import {
accountProfileFollowedBy, accountProfileName, accountProfileUsername, getUrl,
validateTimeline
} from '../utils'
import { foobarRole } from '../roles'
import { loginAsFoobar } from '../roles'
import { quuxStatuses } from '../fixtures'
fixture`007-account-profile.js`
.page`http://localhost:4002`
test('shows account profile', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.click($('.status-author-name').withText(('quux')))
.expect(getUrl()).contains('/accounts/3')
.expect(accountProfileName.innerText).contains('quux')
@ -22,7 +23,8 @@ test('shows account profile', async t => {
})
test('shows account profile 2', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.click($('.status-author-name').withText(('admin')))
.expect(getUrl()).contains('/accounts/1')
.expect(accountProfileName.innerText).contains('admin')
@ -33,7 +35,8 @@ test('shows account profile 2', async t => {
})
test('shows account profile 3', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.click($('.mention').withText(('foobar')))
.expect(getUrl()).contains('/accounts/2')
.expect(accountProfileName.innerText).contains('foobar')
@ -44,7 +47,8 @@ test('shows account profile 3', async t => {
})
test('shows account profile statuses', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.click($('.status-author-name').withText(('quux')))
.expect(getUrl()).contains('/accounts/3')
.expect($('.pinned-statuses .status-article').getAttribute('aria-setsize')).eql('2')

View File

@ -1,11 +1,11 @@
import { closeDialogButton, getNthStatus, modalDialogContents, scrollToStatus } from '../utils'
import { foobarRole } from '../roles'
import { loginAsFoobar } from '../roles'
fixture`008-status-media.js`
.page`http://localhost:4002`
test('shows sensitive images and videos', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await scrollToStatus(t, 7)
await t.expect(getNthStatus(7).find('.status-media img').exists).notOk()
.click(getNthStatus(7).find('.status-sensitive-media-button'))
@ -18,7 +18,7 @@ test('shows sensitive images and videos', async t => {
})
test('click and close image and video modals', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await scrollToStatus(t, 9)
await t.expect(modalDialogContents.exists).notOk()
.click(getNthStatus(9).find('.play-video-button'))

View File

@ -3,14 +3,15 @@ import {
getNthStatus, getUrl, validateTimeline, scrollToBottomOfTimeline, getFirstVisibleStatus,
goBack, forceOffline, forceOnline, searchNavButton, searchInput, getNthSearchResult
} from '../utils'
import { foobarRole } from '../roles'
import { loginAsFoobar } from '../roles'
import { bazThreadRelativeTo2, bazThreadRelativeTo2b, bazThreadRelativeTo2B2, quuxThread } from '../fixtures'
fixture`009-threads.js`
.page`http://localhost:4002`
test('Shows a thread', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.click($('a').withText('quux'))
await scrollToBottomOfTimeline(t)
@ -24,7 +25,8 @@ test('Shows a thread', async t => {
})
test('Scrolls to proper point in thread', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.click($('a').withText('quux'))
.hover(getNthStatus(0))
.hover(getNthStatus(2))
@ -69,7 +71,8 @@ async function validateForkedThread (t) {
}
test('Forked threads look correct online and offline', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.hover(getFirstVisibleStatus())
await navigateToBazAccount(t)
await validateForkedThread(t)

View File

@ -3,13 +3,13 @@ import {
goBackButton, getActiveElementInnerText, getNthReplyButton, getActiveElementInsideNthStatus, focus,
getNthStatusSelector
} from '../utils'
import { foobarRole } from '../roles'
import { loginAsFoobar } from '../roles'
fixture`010-focus.js`
.page`http://localhost:4002`
test('modal preserves focus', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await scrollToStatus(t, 9)
// explicitly hover-focus-click
await t.hover(getNthStatus(9).find('.play-video-button'))
@ -22,7 +22,7 @@ test('modal preserves focus', async t => {
})
test('timeline preserves focus', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
// explicitly hover-focus-click
await t.hover(getNthStatus(0))
await focus(getNthStatusSelector(0))()
@ -36,7 +36,8 @@ test('timeline preserves focus', async t => {
})
test('timeline link preserves focus', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.expect(getNthStatus(0).exists).ok({timeout: 20000})
.click(getNthStatus(0).find('.status-header a'))
.expect(getUrl()).contains('/accounts/')
@ -53,7 +54,8 @@ test('timeline link preserves focus', async t => {
})
test('notification timeline preserves focus', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.navigateTo('/notifications')
await scrollToStatus(t, 5)
await t.click(getNthStatus(5).find('.status-header a'))
@ -66,7 +68,8 @@ test('notification timeline preserves focus', async t => {
})
test('thread preserves focus', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.navigateTo('/accounts/3')
await scrollToStatus(t, 2)
await t.click(getNthStatus(2))
@ -87,7 +90,8 @@ test('thread preserves focus', async t => {
})
test('reply preserves focus and moves focus to the text input', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.expect(getNthStatus(1).exists).ok({timeout: 20000})
.click(getNthReplyButton(1))
.expect(getActiveElementClass()).contains('compose-box-input')

View File

@ -3,13 +3,14 @@ import {
favoritesCountElement, getFavoritesCount, getNthStatus, getReblogsCount, getUrl,
reblogsCountElement
} from '../utils'
import { foobarRole } from '../roles'
import { loginAsFoobar } from '../roles'
fixture`011-reblog-favorites-count.js`
.page`http://localhost:4002`
test('shows favorites', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.click(getNthStatus(0))
.expect(getUrl()).contains('/statuses/')
.expect(getFavoritesCount()).eql(2)
@ -24,7 +25,8 @@ test('shows favorites', async t => {
})
test('shows boosts', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.click(getNthStatus(0))
.expect(getUrl()).contains('/statuses/')
.expect(getReblogsCount()).eql(1)

View File

@ -5,13 +5,14 @@ import {
notificationsNavButton,
times
} from '../utils'
import { foobarRole } from '../roles'
import { loginAsFoobar } from '../roles'
fixture`012-compose.js`
.page`http://localhost:4002`
test('shows compose limits', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.hover(composeInput)
.expect(composeLengthIndicator.innerText).eql('500')
.expect(composeButton.hasAttribute('disabled')).notOk()
@ -37,7 +38,8 @@ test('shows compose limits', async t => {
})
test('shows compose limits for URLs/handles', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.expect(composeLengthIndicator.innerText).eql('500')
.expect(composeButton.hasAttribute('disabled')).notOk()
.typeText(composeInput, 'hello world ' +
@ -48,14 +50,16 @@ test('shows compose limits for URLs/handles', async t => {
})
test('shows compose limits for emoji', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.typeText(composeInput, 'hello world \ud83c\ude01 \ud83d\udc6a')
.expect(composeLengthIndicator.innerText).eql('485')
.expect(composeButton.hasAttribute('disabled')).notOk()
})
test('shows compose limits for custom emoji', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.typeText(composeInput, 'hello world ')
.click(emojiButton)
.click($('button img[title=":blobnom:"]'))
@ -64,7 +68,8 @@ test('shows compose limits for custom emoji', async t => {
})
test('inserts custom emoji correctly', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.typeText(composeInput, 'hello world')
.selectText(composeInput, 6, 6)
.expect(getComposeSelectionStart()).eql(6)
@ -83,7 +88,8 @@ test('inserts custom emoji correctly', async t => {
})
test('inserts emoji without typing anything', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.click(emojiButton)
.click($('button img[title=":blobpats:"]'))
.expect(composeInput.value).eql(':blobpats: ')

View File

@ -2,13 +2,14 @@ import {
composeInput, getNthDeleteMediaButton, getNthMedia, mediaButton,
uploadKittenImage
} from '../utils'
import { foobarRole } from '../roles'
import { loginAsFoobar } from '../roles'
fixture`013-compose-media.js`
.page`http://localhost:4002`
test('inserts media', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.expect(mediaButton.hasAttribute('disabled')).notOk()
await (uploadKittenImage(1)())
await t.expect(getNthMedia(1).getAttribute('alt')).eql('kitten1.jpg')
@ -35,7 +36,8 @@ test('inserts media', async t => {
})
test('removes media', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.expect(mediaButton.exists).ok()
await (uploadKittenImage(1)())
await t.expect(getNthMedia(1).getAttribute('alt')).eql('kitten1.jpg')
@ -50,7 +52,8 @@ test('removes media', async t => {
})
test('changes URLs as media is added/removed', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.expect(mediaButton.exists).ok()
await (uploadKittenImage(1)())
await t.expect(composeInput.value).match(/^ http:\/\/localhost:3000\/media\/\S+$/)

View File

@ -1,11 +1,12 @@
import { getNthPostPrivacyOptionInDialog, postPrivacyButton } from '../utils'
import { foobarRole } from '../roles'
import { loginAsFoobar } from '../roles'
fixture`014-compose-post-privacy.js`
.page`http://localhost:4002`
test('Changes post privacy', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.expect(postPrivacyButton.getAttribute('aria-label')).eql('Adjust privacy (currently Public)')
.click(postPrivacyButton)
.click(getNthPostPrivacyOptionInDialog(2))

View File

@ -2,13 +2,14 @@ import {
composeContentWarning, composeInput, composeLengthIndicator, contentWarningButton, homeNavButton,
notificationsNavButton
} from '../utils'
import { foobarRole } from '../roles'
import { loginAsFoobar } from '../roles'
fixture`015-compose-content-warnings.js`
.page`http://localhost:4002`
test('Changes content warnings', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.expect(composeContentWarning.exists).notOk()
.expect(contentWarningButton.getAttribute('aria-label')).eql('Add content warning')
.expect(contentWarningButton.getAttribute('aria-pressed')).eql('false')
@ -37,7 +38,8 @@ test('Changes content warnings', async t => {
})
test('Considers content warnings for length limits', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.expect(composeLengthIndicator.innerText).eql('500')
.click(contentWarningButton)
.typeText(composeContentWarning, 'my content warning', {paste: true})
@ -53,7 +55,8 @@ test('Considers content warnings for length limits', async t => {
})
test('Content warning goes away if you hide it', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.click(contentWarningButton)
.expect(composeContentWarning.value).eql('')
.typeText(composeContentWarning, 'yo', {paste: true})

View File

@ -1,5 +1,5 @@
import { getNthStatus, getUrl } from '../utils'
import { foobarRole } from '../roles'
import { loginAsFoobar } from '../roles'
import { Selector as $ } from 'testcafe'
fixture`016-external-links.js`
@ -14,7 +14,8 @@ function getAnchorInProfile (n) {
}
test('converts external links in statuses', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.hover(getNthStatus(0))
.navigateTo('/accounts/4')
.expect(getUrl()).contains('/accounts/4')
@ -31,7 +32,8 @@ test('converts external links in statuses', async t => {
})
test('converts external links in profiles', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.hover(getNthStatus(0))
.navigateTo('/accounts/4')
.expect(getUrl()).contains('/accounts/4')

View File

@ -5,13 +5,14 @@ import {
getNthReplyContentWarningInput, getNthReplyPostPrivacyButton,
getNthStatus, getUrl, homeNavButton, notificationsNavButton, scrollToStatus
} from '../utils'
import { foobarRole } from '../roles'
import { loginAsFoobar } from '../roles'
fixture`017-compose-reply.js`
.page`http://localhost:4002`
test('account handle populated correctly for replies', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.click(getNthReplyButton(0))
.expect(getNthComposeReplyInput(0).value).eql('@quux ')
.typeText(getNthComposeReplyInput(0), 'hello quux', {paste: true})
@ -29,7 +30,8 @@ test('account handle populated correctly for replies', async t => {
})
test('replying to posts with mentions', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.click(getNthReplyButton(1))
.expect(getNthComposeReplyInput(1).value).eql('@admin ')
.navigateTo('/accounts/4')
@ -38,7 +40,8 @@ test('replying to posts with mentions', async t => {
})
test('replies have same privacy as replied-to status by default', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.hover(getNthStatus(0))
.hover(getNthStatus(1))
.click(getNthReplyButton(1))
@ -62,7 +65,7 @@ test('replies have same privacy as replied-to status by default', async t => {
})
test('replies have same CW as replied-to status', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await scrollToStatus(t, 7)
await t.click(getNthReplyButton(7))
.expect(getNthReplyContentWarningInput(7).value).eql('kitten CW')
@ -72,7 +75,7 @@ test('replies have same CW as replied-to status', async t => {
})
test('replies save deletions of CW', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await scrollToStatus(t, 7)
await t.click(getNthReplyButton(7))
.expect(getNthReplyContentWarningInput(7).value).eql('kitten CW')
@ -84,7 +87,7 @@ test('replies save deletions of CW', async t => {
})
test('replies save changes to CW', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await scrollToStatus(t, 7)
await t.click(getNthReplyButton(7))
.expect(getNthReplyContentWarningInput(7).value).eql('kitten CW')
@ -96,7 +99,8 @@ test('replies save changes to CW', async t => {
})
test('replies save changes to post privacy', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.hover(getNthStatus(0))
.hover(getNthStatus(1))
.click(getNthReplyButton(1))

View File

@ -2,7 +2,7 @@ import {
composeInput, getNthAutosuggestionResult, getNthComposeReplyInput, getNthReplyButton, getNthStatus, sleep
} from '../utils'
import { Selector as $ } from 'testcafe'
import { foobarRole } from '../roles'
import { loginAsFoobar } from '../roles'
fixture`018-compose-autosuggest.js`
.page`http://localhost:4002`
@ -10,7 +10,8 @@ fixture`018-compose-autosuggest.js`
const timeout = 30000
test('autosuggests user handles', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.hover(composeInput)
await sleep(1000)
await t
@ -31,7 +32,8 @@ test('autosuggests user handles', async t => {
})
test('autosuggests custom emoji', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.hover(composeInput)
.typeText(composeInput, ':blob')
.click(getNthAutosuggestionResult(1))
@ -51,7 +53,8 @@ test('autosuggests custom emoji', async t => {
})
test('autosuggest custom emoji works with regular emoji - keyboard', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.hover(composeInput)
.typeText(composeInput, '\ud83c\udf4d :blobno')
.expect(getNthAutosuggestionResult(1).innerText).contains(':blobnom:', {timeout})
@ -60,7 +63,8 @@ test('autosuggest custom emoji works with regular emoji - keyboard', async t =>
})
test('autosuggest custom emoji works with regular emoji - clicking', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.hover(composeInput)
.typeText(composeInput, '\ud83c\udf4d :blobno')
.expect(getNthAutosuggestionResult(1).innerText).contains(':blobnom:', {timeout})
@ -69,7 +73,8 @@ test('autosuggest custom emoji works with regular emoji - clicking', async t =>
})
test('autosuggest handles works with regular emoji - keyboard', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.hover(composeInput)
.typeText(composeInput, '\ud83c\udf4d @quu')
.expect(getNthAutosuggestionResult(1).innerText).contains('@quux', {timeout})
@ -78,7 +83,8 @@ test('autosuggest handles works with regular emoji - keyboard', async t => {
})
test('autosuggest handles works with regular emoji - clicking', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.hover(composeInput)
.typeText(composeInput, '\ud83c\udf4d @quu')
.expect(getNthAutosuggestionResult(1).innerText).contains('@quux', {timeout})
@ -87,7 +93,8 @@ test('autosuggest handles works with regular emoji - clicking', async t => {
})
test('autosuggest only shows for one input', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.hover(composeInput)
.typeText(composeInput, '@quu')
.hover(getNthStatus(0))
@ -99,7 +106,8 @@ test('autosuggest only shows for one input', async t => {
})
test('autosuggest only shows for one input part 2', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.hover(composeInput)
.typeText(composeInput, '@adm')
.expect($('.compose-autosuggest.shown').exists).ok({timeout})

View File

@ -2,13 +2,14 @@ import {
accountProfileMoreOptionsButton, closeDialogButton,
getNthDialogOptionsOption, modalDialog
} from '../utils'
import { foobarRole } from '../roles'
import { loginAsFoobar } from '../roles'
fixture`019-mention.js`
.page`http://localhost:4002`
test('can mention from account profile', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.navigateTo('/accounts/5')
.click(accountProfileMoreOptionsButton)
.expect(getNthDialogOptionsOption(1).innerText).contains('Mention @baz')

View File

@ -1,14 +1,15 @@
import {
settingsNavButton
} from '../utils'
import { foobarRole } from '../roles'
import { loginAsFoobar } from '../roles'
import { Selector as $ } from 'testcafe'
fixture`020-themes.js`
.page`http://localhost:4002`
test('can set a theme', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.click(settingsNavButton)
.click($('a[href="/settings/instances"]'))
.click($('a[href="/settings/instances/localhost:3000"]'))

View File

@ -3,13 +3,14 @@ import {
followsButton, getNthSearchResult,
getNthStatus, getUrl, goBack
} from '../utils'
import { foobarRole } from '../roles'
import { loginAsFoobar } from '../roles'
fixture`021-followers-follows.js`
.page`http://localhost:4002`
test('shows followers and follows', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.click(getNthStatus(0).find('.status-author-name'))
.expect(getUrl()).match(/\/accounts\/3$/)
.expect(followsButton.getAttribute('aria-label')).eql('Follows 2')

View File

@ -3,13 +3,14 @@ import {
getNthFavoriteButton, getNthFavorited, getNthStatus, getUrl, homeNavButton, notificationsNavButton,
scrollToBottomOfTimeline, scrollToTopOfTimeline
} from '../utils'
import { foobarRole } from '../roles'
import { loginAsFoobar } from '../roles'
fixture`100-favorite-unfavorite.js`
.page`http://localhost:4002`
test('favorites a status', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.hover(getNthStatus(4))
.expect(getNthFavorited(4)).eql('false')
.click(getNthFavoriteButton(4))
@ -35,7 +36,8 @@ test('favorites a status', async t => {
})
test('unfavorites a status', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.expect(getNthFavorited(1)).eql('true')
.click(getNthFavoriteButton(1))
.expect(getNthFavorited(1)).eql('false')
@ -56,7 +58,8 @@ test('unfavorites a status', async t => {
})
test('Keeps the correct favorites count', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.hover(getNthStatus(4))
.click(getNthFavoriteButton(4))
.expect(getNthFavorited(4)).eql('true')

View File

@ -3,13 +3,14 @@ import {
notificationsNavButton,
scrollToBottomOfTimeline, scrollToTopOfTimeline
} from '../utils'
import { foobarRole } from '../roles'
import { loginAsFoobar } from '../roles'
fixture`101-reblog-unreblog.js`
.page`http://localhost:4002`
test('reblogs a status', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.hover(getNthStatus(0))
.expect(getNthReblogged(0)).eql('false')
.click(getNthReblogButton(0))
@ -34,7 +35,8 @@ test('reblogs a status', async t => {
})
test('unreblogs a status', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.hover(getNthStatus(4))
.expect(getNthReblogged(4)).eql('false')
.click(getNthReblogButton(4))
@ -59,7 +61,8 @@ test('unreblogs a status', async t => {
})
test('Keeps the correct reblogs count', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.hover(getNthStatus(4))
.expect(getNthReblogged(4)).eql('true')
.click(getNthStatus(4))

View File

@ -1,4 +1,4 @@
import { foobarRole } from '../roles'
import { loginAsFoobar } from '../roles'
import { getNthStatus, getUrl, homeNavButton, notificationsNavButton, validateTimeline } from '../utils'
import { favoriteStatusAs } from '../serverActions'
import { notifications } from '../fixtures'
@ -7,7 +7,8 @@ fixture`102-notifications.js`
.page`http://localhost:4002`
test('shows unread notifications', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.hover(getNthStatus(0))
.hover(getNthStatus(2))
.hover(getNthStatus(4))

View File

@ -1,4 +1,4 @@
import { foobarRole } from '../roles'
import { loginAsFoobar } from '../roles'
import {
composeInput, getNthComposeReplyButton, getNthComposeReplyInput, getNthReplyButton, getNthStatus, getUrl,
homeNavButton, notificationsNavButton,
@ -9,7 +9,8 @@ fixture`103-compose.js`
.page`http://localhost:4002`
test('statuses show up in home timeline', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.typeText(composeInput, 'hello world', {paste: true})
.click(postStatusButton)
.expect(getNthStatus(0).innerText).contains('hello world')
@ -23,7 +24,8 @@ test('statuses show up in home timeline', async t => {
})
test('statuses in threads show up in right order', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.navigateTo('/accounts/5')
.click(getNthStatus(2))
.expect(getUrl()).contains('/statuses')

View File

@ -1,4 +1,4 @@
import { foobarRole } from '../roles'
import { loginAsFoobar } from '../roles'
import {
getNthStatus, scrollContainerToTop, showMoreButton, sleep
} from '../utils'
@ -8,14 +8,16 @@ fixture`104-streaming.js`
.page`http://localhost:4002`
test('new incoming statuses show up immediately', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.hover(getNthStatus(0))
await postAs('admin', 'hello my baby hello my honey')
await t.expect(getNthStatus(0).innerText).contains('hello my baby hello my honey')
})
test('new incoming toots show a button if scrolled down', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.hover(getNthStatus(0))
.hover(getNthStatus(2))
.hover(getNthStatus(4))

View File

@ -1,4 +1,4 @@
import { foobarRole } from '../roles'
import { loginAsFoobar } from '../roles'
import {
clickToNotificationsAndBackHome, forceOffline, forceOnline, getNthStatus, getUrl, homeNavButton,
notificationsNavButton
@ -10,7 +10,8 @@ fixture`105-deletes.js`
test('deleted statuses are removed from the timeline', async t => {
let timeout = 20000
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.hover(getNthStatus(0))
let status = await postAs('admin', "I'm gonna delete this")
await t.expect(getNthStatus(0).innerText).contains("I'm gonna delete this", {timeout})
@ -30,7 +31,8 @@ test('deleted statuses are removed from the timeline', async t => {
test('deleted statuses are removed from threads', async t => {
let timeout = 20000
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.hover(getNthStatus(0))
let status = await postAs('admin', "I won't delete this")
let reply = await postReplyAs('admin', 'But I will delete this', status.id)
@ -54,7 +56,8 @@ test('deleted statuses are removed from threads', async t => {
test('deleted statuses result in deleted notifications', async t => {
let timeout = 20000
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.hover(getNthStatus(0))
.expect(notificationsNavButton.getAttribute('aria-label')).eql('Notifications')
let status = await postAs('admin', "@foobar yo yo foobar what's up")

View File

@ -1,4 +1,4 @@
import { foobarRole } from '../roles'
import { loginAsFoobar } from '../roles'
import {
accountProfileFollowButton,
getNthStatus,
@ -12,7 +12,8 @@ fixture`106-follow-requests.js`
.page`http://localhost:4002`
test('can request to follow an account', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.navigateTo('/accounts/6')
.expect(accountProfileFollowButton.getAttribute('aria-label')).eql('Follow')
.click(accountProfileFollowButton)

View File

@ -1,4 +1,4 @@
import { foobarRole } from '../roles'
import { loginAsFoobar } from '../roles'
import {
getNthStatus, homeNavButton, localTimelineNavButton, sleep
} from '../utils'
@ -12,7 +12,8 @@ fixture`107-streaming-gap.js`
test('fills in a status posted while away from timeline', async t => {
let timeout = 30000
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.click(localTimelineNavButton)
.expect(getNthStatus(0).exists).ok({timeout})
.hover(getNthStatus(0))

View File

@ -2,14 +2,14 @@ import {
composeButton, getNthStatus, scrollToStatus, modalDialog, sleep,
notificationsNavButton, getUrl
} from '../utils'
import { foobarRole } from '../roles'
import { loginAsFoobar } from '../roles'
import { Selector as $ } from 'testcafe'
fixture`108-compose-dialog.js`
.page`http://localhost:4002`
test('can compose using a dialog', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await scrollToStatus(t, 15)
await t.expect(modalDialog.exists).notOk()
.expect(composeButton.getAttribute('aria-label')).eql('Compose')
@ -27,7 +27,7 @@ test('can compose using a dialog', async t => {
})
test('can use emoji dialog within compose dialog', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await scrollToStatus(t, 15)
await t.expect(composeButton.getAttribute('aria-label')).eql('Compose')
await sleep(2000)

View File

@ -4,7 +4,7 @@ import {
mediaButton, notificationsNavButton,
uploadKittenImage
} from '../utils'
import { foobarRole } from '../roles'
import { loginAsFoobar } from '../roles'
fixture`109-compose-media.js`
.page`http://localhost:4002`
@ -18,7 +18,8 @@ async function uploadTwoKittens (t) {
}
test('uploads alts for media', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.expect(mediaButton.hasAttribute('disabled')).notOk()
await uploadTwoKittens(t)
await t.typeText(getNthMediaAltInput(2), 'kitten 2')
@ -31,7 +32,8 @@ test('uploads alts for media', async t => {
})
test('uploads alts when deleting and re-uploading media', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.expect(mediaButton.hasAttribute('disabled')).notOk()
await (uploadKittenImage(1)())
await t.typeText(getNthMediaAltInput(1), 'this will be deleted')
@ -46,7 +48,8 @@ test('uploads alts when deleting and re-uploading media', async t => {
})
test('uploads alts mixed with no-alts', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.expect(mediaButton.hasAttribute('disabled')).notOk()
await uploadTwoKittens(t)
await t.typeText(getNthMediaAltInput(2), 'kitten numero dos')
@ -56,7 +59,8 @@ test('uploads alts mixed with no-alts', async t => {
})
test('saves alts to local storage', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.expect(mediaButton.hasAttribute('disabled')).notOk()
await uploadTwoKittens(t)
await t.typeText(getNthMediaAltInput(1), 'kitten numero uno')

View File

@ -2,13 +2,14 @@ import {
composeButton, composeContentWarning, composeInput, contentWarningButton,
getNthShowOrHideButton, getNthStatus
} from '../utils'
import { foobarRole } from '../roles'
import { loginAsFoobar } from '../roles'
fixture`110-compose-content-warnings.js`
.page`http://localhost:4002`
test('content warnings are posted', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.typeText(composeInput, 'hello this is a toot', {paste: true})
.click(contentWarningButton)
.typeText(composeContentWarning, 'CW', {paste: true})
@ -21,7 +22,8 @@ test('content warnings are posted', async t => {
})
test('content warnings are not posted if removed', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.typeText(composeInput, 'hi this is another toot', {paste: true})
.click(contentWarningButton)
.typeText(composeContentWarning, 'content warning!', {paste: true})
@ -34,7 +36,8 @@ test('content warnings are not posted if removed', async t => {
})
test('content warnings can have emoji', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.typeText(composeInput, 'I can: :blobnom:')
.click(contentWarningButton)
.typeText(composeContentWarning, 'can you feel the :blobpats: tonight')
@ -48,7 +51,8 @@ test('content warnings can have emoji', async t => {
test('no XSS in content warnings or text', async t => {
let pwned1 = `<script>alert("pwned!")</script>`
let pwned2 = `<script>alert("pwned from CW!")</script>`
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.typeText(composeInput, pwned1)
.click(contentWarningButton)
.typeText(composeContentWarning, pwned2)

View File

@ -4,13 +4,14 @@ import {
getNthComposeReplyInput, getNthReplyButton,
getNthStatus
} from '../utils'
import { foobarRole } from '../roles'
import { loginAsFoobar } from '../roles'
fixture`111-focus.js`
.page`http://localhost:4002`
test('replying to a toot returns focus to reply button', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.typeText(composeInput, 'I would like, if I may, to take you on a strange journey', {paste: true})
.pressKey('ctrl+enter')
.expect(getNthStatus(0).find('.status-content').innerText).contains('I would like, if I may, to take you on a strange journey')

View File

@ -3,7 +3,7 @@ import {
composeInput,
getNthStatus
} from '../utils'
import { foobarRole } from '../roles'
import { loginAsFoobar } from '../roles'
fixture`112-status-links.js`
.page`http://localhost:4002`
@ -16,7 +16,8 @@ test('External links, hashtags, and mentions have correct attributes', async t =
const nthAnchor = n => getNthStatus(0).find('.status-content a').nth(n)
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.typeText(composeInput, text, {paste: true})
.click(composeButton)
.expect(getNthStatus(0).innerText).contains('Why hello there', {timeout: 20000})

View File

@ -4,7 +4,7 @@ import {
getNthStatus, getNthStatusOptionsButton, getNthDialogOptionsOption, getUrl, modalDialog
} from '../utils'
import { Selector as $ } from 'testcafe'
import { foobarRole } from '../roles'
import { loginAsFoobar } from '../roles'
import { postAs } from '../serverActions'
fixture`113-block-unblock.js`
@ -13,7 +13,8 @@ fixture`113-block-unblock.js`
test('Can block and unblock an account from a status', async t => {
let post = 'a very silly statement that should probably get me blocked'
await postAs('admin', post)
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.expect(getNthStatus(0).innerText).contains(post, {timeout: 30000})
.click(getNthStatusOptionsButton(0))
.expect(getNthDialogOptionsOption(1).innerText).contains('Unfollow @admin')
@ -35,7 +36,8 @@ test('Can block and unblock an account from a status', async t => {
})
test('Can block and unblock an account from the account profile page', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.navigateTo('/accounts/5')
.expect(accountProfileFollowButton.getAttribute('aria-label')).eql('Follow')
.click(accountProfileMoreOptionsButton)

View File

@ -4,14 +4,14 @@ import {
getNthStatus, getNthStatusOptionsButton, getNthDialogOptionsOption, getUrl, modalDialog, closeDialogButton
} from '../utils'
import { Selector as $ } from 'testcafe'
import { foobarRole } from '../roles'
import { loginAsFoobar } from '../roles'
import { postAs } from '../serverActions'
fixture`114-mute-unmute.js`
.page`http://localhost:4002`
test('Can mute and unmute an account', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
let post = 'blah blah blah'
await postAs('admin', post)

View File

@ -3,13 +3,14 @@ import {
accountProfileMoreOptionsButton, closeDialogButton,
getNthDialogOptionsOption
} from '../utils'
import { foobarRole } from '../roles'
import { loginAsFoobar } from '../roles'
fixture`115-follow-unfollow.js`
.page`http://localhost:4002`
test('Can follow and unfollow an account from the profile page', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.navigateTo('/accounts/5')
.expect(accountProfileFollowButton.getAttribute('aria-label')).eql('Follow')
.click(accountProfileMoreOptionsButton)

View File

@ -1,4 +1,4 @@
import { lockedAccountRole } from '../roles'
import { loginAsLockedAccount } from '../roles'
import { followAs, unfollowAs } from '../serverActions'
import {
avatarInComposeBox,
@ -14,7 +14,7 @@ fixture`116-follow-requests.js`
const timeout = 30000
test('Can approve and reject follow requests', async t => {
await t.useRole(lockedAccountRole)
await loginAsLockedAccount(t)
// necessary for re-running this test in local testing
await Promise.all([

View File

@ -1,8 +1,8 @@
import { foobarRole } from '../roles'
import { postAs } from '../serverActions'
import { loginAsFoobar } from '../roles'
import {
avatarInComposeBox, getNthDialogOptionsOption, getNthPinnedStatus, getNthPinnedStatusFavoriteButton, getNthStatus,
getNthStatusOptionsButton, getUrl, sleep
avatarInComposeBox, composeInput, getNthDialogOptionsOption, getNthPinnedStatus, getNthPinnedStatusFavoriteButton,
getNthStatus,
getNthStatusOptionsButton, getUrl, postStatusButton
} from '../utils'
import { users } from '../users'
@ -10,13 +10,12 @@ fixture`117-pin-unpin.js`
.page`http://localhost:4002`
test('Can pin statuses', async t => {
await t.useRole(foobarRole)
await postAs('foobar', 'I am going to pin this')
await sleep(2000)
await t.click(avatarInComposeBox)
await loginAsFoobar(t)
await t
.typeText(composeInput, 'I am going to pin this', {paste: true})
.click(postStatusButton)
.expect(getNthStatus(0).innerText).contains('I am going to pin this')
.click(avatarInComposeBox)
.expect(getUrl()).contains(`/accounts/${users.foobar.id}`)
.expect(getNthPinnedStatus(0).getAttribute('aria-setsize')).eql('1')
.expect(getNthPinnedStatus(0).innerText).contains('this is unlisted')
@ -40,7 +39,8 @@ test('Can pin statuses', async t => {
})
test('Can favorite a pinned status', async t => {
await t.useRole(foobarRole)
await loginAsFoobar(t)
await t
.click(avatarInComposeBox)
.expect(getNthPinnedStatus(0).getAttribute('aria-setsize')).eql('1')
.expect(getNthPinnedStatusFavoriteButton(0).getAttribute('aria-pressed')).eql('false')

View File

@ -2,8 +2,6 @@ import { ClientFunction as exec, Selector as $ } from 'testcafe'
import * as images from './images'
import * as blobUtils from './blobUtils'
const SCROLL_INTERVAL = 1
export const settingsButton = $('nav a[aria-label=Settings]')
export const instanceInput = $('#instanceInput')
export const modalDialog = $('.modal-dialog')
@ -80,9 +78,9 @@ export const getActiveElementInsideNthStatus = exec(() => {
export const goBack = exec(() => window.history.back())
export const forceOffline = exec(() => window.store.set({online: false}))
export const forceOffline = exec(() => window.__forceOnline(false))
export const forceOnline = exec(() => window.store.set({online: true}))
export const forceOnline = exec(() => window.__forceOnline(true))
export const getComposeSelectionStart = exec(() => composeInput().selectionStart, {
dependencies: { composeInput }
@ -157,6 +155,18 @@ export function getNthStatusSelector (n) {
return `div[aria-hidden="false"] > article[aria-posinset="${n}"]`
}
export function getNthStatusContent (n) {
return $(`${getNthStatusSelector(n)} .status-content`)
}
export function getNthStatusSpoiler (n) {
return $(`${getNthStatusSelector(n)} .status-spoiler`)
}
export function getNthStatusHeader (n) {
return $(`${getNthStatusSelector(n)} .status-header`)
}
export function getNthStatusAndImage (nStatus, nImage) {
return getNthStatus(nStatus).find(`.status-media .show-image-button:nth-child(${nImage + 1}) img`)
}
@ -234,36 +244,31 @@ export function getNthPinnedStatusFavoriteButton (n) {
}
export async function validateTimeline (t, timeline) {
const timeout = 20000
const timeout = 30000
for (let i = 0; i < timeline.length; i++) {
let status = timeline[i]
await t.expect(getNthStatus(i).exists).ok({ timeout })
// hovering forces TestCafé to scroll to that element: https://git.io/vABV2
await t.hover(getNthStatus(i))
if (status.content) {
await t.expect(getNthStatus(i).find('.status-content p').innerText)
await t.expect(getNthStatusContent(i).innerText)
.contains(status.content, { timeout })
}
if (status.spoiler) {
await t.expect(getNthStatus(i).find('.status-spoiler p').innerText)
await t.expect(getNthStatusSpoiler(i).innerText)
.contains(status.spoiler, { timeout })
}
if (status.followedBy) {
await t.expect(getNthStatus(i).find('.status-header span').innerText)
await t.expect(getNthStatusHeader(i).innerText)
.contains(status.followedBy + ' followed you', { timeout })
}
if (status.rebloggedBy) {
await t.expect(getNthStatus(i).find('.status-header span').innerText)
await t.expect(getNthStatusHeader(i).innerText)
.contains(status.rebloggedBy + ' boosted your status', { timeout })
}
if (status.favoritedBy) {
await t.expect(getNthStatus(i).find('.status-header span').innerText)
await t.expect(getNthStatusHeader(i).innerText)
.contains(status.favoritedBy + ' favorited your status', { timeout })
}
// hovering forces TestCafé to scroll to that element: https://git.io/vABV2
if (i % SCROLL_INTERVAL === (SCROLL_INTERVAL - 1)) { // only scroll every nth element
await t.hover(getNthStatus(i))
.expect($('.loading-footer').exist).notOk()
}
}
}
@ -272,8 +277,7 @@ export async function scrollToTopOfTimeline (t) {
while (true) {
await t.hover(getNthStatus(i))
.expect($('.loading-footer').exist).notOk()
i -= SCROLL_INTERVAL
if (i <= 0) {
if (--i <= 0) {
break
}
}
@ -285,8 +289,7 @@ export async function scrollToBottomOfTimeline (t) {
await t.hover(getNthStatus(i))
.expect($('.loading-footer').exist).notOk()
let size = await getNthStatus(i).getAttribute('aria-setsize')
i += SCROLL_INTERVAL
if (i >= size - 1) {
if (++i >= size - 1) {
break
}
}
@ -294,7 +297,7 @@ export async function scrollToBottomOfTimeline (t) {
export async function scrollToStatus (t, n) {
let timeout = 20000
for (let i = 0; i <= n; i += SCROLL_INTERVAL) {
for (let i = 0; i <= n; i++) {
await t.expect(getNthStatus(i).exists).ok({timeout})
.hover(getNthStatus(i))
.expect($('.loading-footer').exist).notOk()