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:
parent
520bf7456b
commit
efdb0bc534
15
package.json
15
package.json
|
@ -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",
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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})
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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'))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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: ')
|
||||
|
|
|
@ -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+$/)
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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"]'))
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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([
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in a new issue