approve/reject follow requests, unblock, unmute (#230)
* approve/reject follow requests, unblock, unmute * make tests less flaky
This commit is contained in:
parent
e342eadbd0
commit
ffb00fcc5c
|
@ -31,8 +31,7 @@ Lint:
|
||||||
|
|
||||||
Automatically fix most linting issues:
|
Automatically fix most linting issues:
|
||||||
|
|
||||||
npx standard --fix
|
npm run lint-fix
|
||||||
npx standard --fix --plugin html 'routes/**/*.html'
|
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
"version": "0.2.3",
|
"version": "0.2.3",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "standard && standard --plugin html 'routes/**/*.html'",
|
"lint": "standard && standard --plugin html 'routes/**/*.html'",
|
||||||
|
"lint-fix": "standard --fix && standard --fix --plugin html 'routes/**/*.html'",
|
||||||
"dev": "run-s build-svg build-inline-script serve-dev",
|
"dev": "run-s build-svg build-inline-script serve-dev",
|
||||||
"serve-dev": "run-p --race build-sass-watch serve",
|
"serve-dev": "run-p --race build-sass-watch serve",
|
||||||
"serve": "node server.js",
|
"serve": "node server.js",
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { store } from '../_store/store'
|
||||||
import { blockAccount, unblockAccount } from '../_api/block'
|
import { blockAccount, unblockAccount } from '../_api/block'
|
||||||
import { toast } from '../_utils/toast'
|
import { toast } from '../_utils/toast'
|
||||||
import { updateProfileAndRelationship } from './accounts'
|
import { updateProfileAndRelationship } from './accounts'
|
||||||
|
import { emit } from '../_utils/eventBus'
|
||||||
|
|
||||||
export async function setAccountBlocked (accountId, block, toastOnSuccess) {
|
export async function setAccountBlocked (accountId, block, toastOnSuccess) {
|
||||||
let { currentInstance, accessToken } = store.get()
|
let { currentInstance, accessToken } = store.get()
|
||||||
|
@ -19,6 +20,7 @@ export async function setAccountBlocked (accountId, block, toastOnSuccess) {
|
||||||
toast.say('Unblocked account')
|
toast.say('Unblocked account')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
emit('refreshAccountsList')
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
toast.say(`Unable to ${block ? 'block' : 'unblock'} account: ` + (e.message || ''))
|
toast.say(`Unable to ${block ? 'block' : 'unblock'} account: ` + (e.message || ''))
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { store } from '../_store/store'
|
||||||
import { muteAccount, unmuteAccount } from '../_api/mute'
|
import { muteAccount, unmuteAccount } from '../_api/mute'
|
||||||
import { toast } from '../_utils/toast'
|
import { toast } from '../_utils/toast'
|
||||||
import { updateProfileAndRelationship } from './accounts'
|
import { updateProfileAndRelationship } from './accounts'
|
||||||
|
import { emit } from '../_utils/eventBus'
|
||||||
|
|
||||||
export async function setAccountMuted (accountId, mute, toastOnSuccess) {
|
export async function setAccountMuted (accountId, mute, toastOnSuccess) {
|
||||||
let { currentInstance, accessToken } = store.get()
|
let { currentInstance, accessToken } = store.get()
|
||||||
|
@ -19,6 +20,7 @@ export async function setAccountMuted (accountId, mute, toastOnSuccess) {
|
||||||
toast.say('Unmuted account')
|
toast.say('Unmuted account')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
emit('refreshAccountsList')
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
toast.say(`Unable to ${mute ? 'mute' : 'unmute'} account: ` + (e.message || ''))
|
toast.say(`Unable to ${mute ? 'mute' : 'unmute'} account: ` + (e.message || ''))
|
||||||
|
|
29
routes/_actions/requests.js
Normal file
29
routes/_actions/requests.js
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import { store } from '../_store/store'
|
||||||
|
import { approveFollowRequest, rejectFollowRequest } from '../_api/requests'
|
||||||
|
import { emit } from '../_utils/eventBus'
|
||||||
|
import { toast } from '../_utils/toast'
|
||||||
|
|
||||||
|
export async function setFollowRequestApprovedOrRejected (accountId, approved, toastOnSuccess) {
|
||||||
|
let {
|
||||||
|
currentInstance,
|
||||||
|
accessToken
|
||||||
|
} = store.get()
|
||||||
|
try {
|
||||||
|
if (approved) {
|
||||||
|
await approveFollowRequest(currentInstance, accessToken, accountId)
|
||||||
|
} else {
|
||||||
|
await rejectFollowRequest(currentInstance, accessToken, accountId)
|
||||||
|
}
|
||||||
|
if (toastOnSuccess) {
|
||||||
|
if (approved) {
|
||||||
|
toast.say('Approved follow request')
|
||||||
|
} else {
|
||||||
|
toast.say('Rejected follow request')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
emit('refreshAccountsList')
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
toast.say(`Unable to ${approved ? 'approve' : 'reject'} account: ` + (e.message || ''))
|
||||||
|
}
|
||||||
|
}
|
12
routes/_api/requests.js
Normal file
12
routes/_api/requests.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import { postWithTimeout } from '../_utils/ajax'
|
||||||
|
import { auth, basename } from './utils'
|
||||||
|
|
||||||
|
export async function approveFollowRequest (instanceName, accessToken, accountId) {
|
||||||
|
let url = `${basename(instanceName)}/api/v1/follow_requests/${accountId}/authorize`
|
||||||
|
return postWithTimeout(url, null, auth(accessToken))
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function rejectFollowRequest (instanceName, accessToken, accountId) {
|
||||||
|
let url = `${basename(instanceName)}/api/v1/follow_requests/${accountId}/reject`
|
||||||
|
return postWithTimeout(url, null, auth(accessToken))
|
||||||
|
}
|
|
@ -4,7 +4,11 @@
|
||||||
{{elseif accounts && accounts.length}}
|
{{elseif accounts && accounts.length}}
|
||||||
<ul class="accounts-results">
|
<ul class="accounts-results">
|
||||||
{{#each accounts as account}}
|
{{#each accounts as account}}
|
||||||
<AccountSearchResult :account />
|
<AccountSearchResult
|
||||||
|
:account
|
||||||
|
actions={{accountActions}}
|
||||||
|
on:click="onClickAction(event)"
|
||||||
|
/>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</ul>
|
</ul>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
@ -31,19 +35,19 @@
|
||||||
import LoadingPage from '../_components/LoadingPage.html'
|
import LoadingPage from '../_components/LoadingPage.html'
|
||||||
import AccountSearchResult from '../_components/search/AccountSearchResult.html'
|
import AccountSearchResult from '../_components/search/AccountSearchResult.html'
|
||||||
import { toast } from '../_utils/toast'
|
import { toast } from '../_utils/toast'
|
||||||
|
import { on } from '../_utils/eventBus'
|
||||||
|
|
||||||
|
// TODO: paginate
|
||||||
export default {
|
export default {
|
||||||
async oncreate () {
|
async oncreate () {
|
||||||
let { accountsFetcher } = this.get()
|
|
||||||
try {
|
try {
|
||||||
// TODO: paginate
|
await this.refreshAccounts()
|
||||||
let accounts = await accountsFetcher()
|
|
||||||
this.set({ accounts: accounts })
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
toast.say('Error: ' + (e.name || '') + ' ' + (e.message || ''))
|
toast.say('Error: ' + (e.name || '') + ' ' + (e.message || ''))
|
||||||
} finally {
|
} finally {
|
||||||
this.set({loading: false})
|
this.set({loading: false})
|
||||||
}
|
}
|
||||||
|
on('refreshAccountsList', this, () => this.refreshAccounts())
|
||||||
},
|
},
|
||||||
data: () => ({
|
data: () => ({
|
||||||
loading: true,
|
loading: true,
|
||||||
|
@ -53,6 +57,17 @@
|
||||||
components: {
|
components: {
|
||||||
LoadingPage,
|
LoadingPage,
|
||||||
AccountSearchResult
|
AccountSearchResult
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onClickAction (event) {
|
||||||
|
let { action, accountId } = event
|
||||||
|
action.onclick(accountId)
|
||||||
|
},
|
||||||
|
async refreshAccounts () {
|
||||||
|
let { accountsFetcher } = this.get()
|
||||||
|
let accounts = await accountsFetcher()
|
||||||
|
this.set({ accounts: accounts })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
|
@ -9,7 +9,7 @@
|
||||||
{{#if pinnable}}
|
{{#if pinnable}}
|
||||||
<IconButton pressable="true"
|
<IconButton pressable="true"
|
||||||
pressed="{{$pinnedPage === href}}"
|
pressed="{{$pinnedPage === href}}"
|
||||||
label="Pin page"
|
label="{{$pinnedPage === href ? 'Unpin timeline' : 'Pin timeline'}}"
|
||||||
href="#fa-thumb-tack"
|
href="#fa-thumb-tack"
|
||||||
on:click="onPinClick(event)" />
|
on:click="onPinClick(event)" />
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -7,16 +7,28 @@
|
||||||
<div class="search-result-account-username">
|
<div class="search-result-account-username">
|
||||||
{{'@' + account.acct}}
|
{{'@' + account.acct}}
|
||||||
</div>
|
</div>
|
||||||
|
{{#if actions && actions.length}}
|
||||||
|
<div class="search-result-account-buttons">
|
||||||
|
{{#each actions as action}}
|
||||||
|
<IconButton
|
||||||
|
label="{{action.label}}"
|
||||||
|
on:click="onButtonClick(event, action, account.id)"
|
||||||
|
href="{{action.icon}}"
|
||||||
|
big="true"
|
||||||
|
/>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
</SearchResult>
|
</SearchResult>
|
||||||
<style>
|
<style>
|
||||||
.search-result-account {
|
.search-result-account {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-areas:
|
grid-template-areas:
|
||||||
"avatar name"
|
"avatar name buttons"
|
||||||
"avatar username";
|
"avatar username buttons";
|
||||||
grid-column-gap: 20px;
|
grid-column-gap: 20px;
|
||||||
grid-template-columns: max-content 1fr;
|
grid-template-columns: max-content 1fr max-content;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
:global(.search-result-account-avatar) {
|
:global(.search-result-account-avatar) {
|
||||||
|
@ -36,19 +48,45 @@
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
color: var(--deemphasized-text-color);
|
color: var(--deemphasized-text-color);
|
||||||
}
|
}
|
||||||
|
.search-result-account-buttons {
|
||||||
|
grid-area: buttons;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
:global(.search-result-account-buttons .icon-button) {
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
:global(.search-result-account-buttons .icon-button:last-child) {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
@media (max-width: 767px) {
|
@media (max-width: 767px) {
|
||||||
.search-result-account {
|
.search-result-account {
|
||||||
grid-column-gap: 10px;
|
grid-column-gap: 10px;
|
||||||
}
|
}
|
||||||
|
:global(.search-result-account-buttons .icon-button) {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
import Avatar from '../Avatar.html'
|
import Avatar from '../Avatar.html'
|
||||||
import SearchResult from './SearchResult.html'
|
import SearchResult from './SearchResult.html'
|
||||||
|
import IconButton from '../IconButton.html'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Avatar,
|
Avatar,
|
||||||
SearchResult
|
SearchResult,
|
||||||
|
IconButton
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onButtonClick (event, action, accountId) {
|
||||||
|
event.preventDefault()
|
||||||
|
event.stopPropagation()
|
||||||
|
this.fire('click', {
|
||||||
|
action,
|
||||||
|
accountId
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
|
@ -1,12 +1,22 @@
|
||||||
<DynamicPageBanner title="Blocked users" icon="#fa-ban" />
|
<DynamicPageBanner title="Blocked users" icon="#fa-ban" />
|
||||||
<AccountsListPage :accountsFetcher />
|
<AccountsListPage :accountsFetcher :accountActions />
|
||||||
<script>
|
<script>
|
||||||
import AccountsListPage from '.././_components/AccountsListPage.html'
|
import AccountsListPage from '.././_components/AccountsListPage.html'
|
||||||
import { store } from '.././_store/store'
|
import { store } from '.././_store/store'
|
||||||
import { getBlockedAccounts } from '.././_api/blockedAndMuted'
|
import { getBlockedAccounts } from '.././_api/blockedAndMuted'
|
||||||
import DynamicPageBanner from '.././_components/DynamicPageBanner.html'
|
import DynamicPageBanner from '.././_components/DynamicPageBanner.html'
|
||||||
|
import { setAccountBlocked } from '../_actions/block'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
data: () => ({
|
||||||
|
accountActions: [
|
||||||
|
{
|
||||||
|
icon: '#fa-unlock',
|
||||||
|
label: 'Unblock',
|
||||||
|
onclick: (accountId) => setAccountBlocked(accountId, false, true)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}),
|
||||||
computed: {
|
computed: {
|
||||||
accountsFetcher: ($currentInstance, $accessToken) => () => getBlockedAccounts($currentInstance, $accessToken)
|
accountsFetcher: ($currentInstance, $accessToken) => () => getBlockedAccounts($currentInstance, $accessToken)
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,12 +1,22 @@
|
||||||
<DynamicPageBanner title="Muted users" icon="#fa-volume-off" />
|
<DynamicPageBanner title="Muted users" icon="#fa-volume-off" />
|
||||||
<AccountsListPage :accountsFetcher />
|
<AccountsListPage :accountsFetcher :accountActions />
|
||||||
<script>
|
<script>
|
||||||
import AccountsListPage from '.././_components/AccountsListPage.html'
|
import AccountsListPage from '.././_components/AccountsListPage.html'
|
||||||
import { store } from '.././_store/store'
|
import { store } from '.././_store/store'
|
||||||
import { getMutedAccounts } from '.././_api/blockedAndMuted'
|
import { getMutedAccounts } from '.././_api/blockedAndMuted'
|
||||||
import DynamicPageBanner from '.././_components/DynamicPageBanner.html'
|
import DynamicPageBanner from '.././_components/DynamicPageBanner.html'
|
||||||
|
import { setAccountMuted } from '../_actions/mute'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
data: () => ({
|
||||||
|
accountActions: [
|
||||||
|
{
|
||||||
|
icon: '#fa-volume-up',
|
||||||
|
label: 'Unmute',
|
||||||
|
onclick: (accountId) => setAccountMuted(accountId, false, true)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}),
|
||||||
computed: {
|
computed: {
|
||||||
accountsFetcher: ($currentInstance, $accessToken) => () => getMutedAccounts($currentInstance, $accessToken)
|
accountsFetcher: ($currentInstance, $accessToken) => () => getMutedAccounts($currentInstance, $accessToken)
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,15 +1,29 @@
|
||||||
<DynamicPageBanner title="Follow requests" icon="#fa-user-plus" />
|
<DynamicPageBanner title="Follow requests" icon="#fa-user-plus" />
|
||||||
<AccountsListPage :accountsFetcher />
|
<AccountsListPage :accountsFetcher :accountActions />
|
||||||
<script>
|
<script>
|
||||||
import AccountsListPage from '.././_components/AccountsListPage.html'
|
import AccountsListPage from '.././_components/AccountsListPage.html'
|
||||||
import { store } from '.././_store/store'
|
import { store } from '.././_store/store'
|
||||||
import { getFollowRequests } from '../_actions/followRequests'
|
import { getFollowRequests } from '../_actions/followRequests'
|
||||||
import DynamicPageBanner from '.././_components/DynamicPageBanner.html'
|
import DynamicPageBanner from '.././_components/DynamicPageBanner.html'
|
||||||
|
import { setFollowRequestApprovedOrRejected } from '../_actions/requests'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
data: () => ({
|
||||||
|
accountActions: [
|
||||||
|
{
|
||||||
|
icon: '#fa-check',
|
||||||
|
label: 'Approve',
|
||||||
|
onclick: (accountId) => setFollowRequestApprovedOrRejected(accountId, true, true)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: '#fa-times',
|
||||||
|
label: 'Reject',
|
||||||
|
onclick: (accountId) => setFollowRequestApprovedOrRejected(accountId, false, true)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}),
|
||||||
computed: {
|
computed: {
|
||||||
statusId: params => params.statusId,
|
accountsFetcher: ($currentInstance, $accessToken) => () => getFollowRequests($currentInstance, $accessToken)
|
||||||
accountsFetcher: ($currentInstance, $accessToken, statusId) => () => getFollowRequests($currentInstance, $accessToken, statusId)
|
|
||||||
},
|
},
|
||||||
store: () => store,
|
store: () => store,
|
||||||
components: {
|
components: {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import {
|
||||||
authorizeInput, emailInput, getUrl, instanceInput, mastodonLogInButton,
|
authorizeInput, emailInput, getUrl, instanceInput, mastodonLogInButton,
|
||||||
passwordInput
|
passwordInput
|
||||||
} from './utils'
|
} from './utils'
|
||||||
|
import { users } from './users'
|
||||||
|
|
||||||
function login (t, username, password) {
|
function login (t, username, password) {
|
||||||
return t.typeText(instanceInput, 'localhost:3000', {paste: true})
|
return t.typeText(instanceInput, 'localhost:3000', {paste: true})
|
||||||
|
@ -18,5 +19,9 @@ function login (t, username, password) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const foobarRole = Role('http://localhost:4002/settings/instances/add', async t => {
|
export const foobarRole = Role('http://localhost:4002/settings/instances/add', async t => {
|
||||||
await login(t, 'foobar@localhost:3000', 'foobarfoobar')
|
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)
|
||||||
})
|
})
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { users } from './users'
|
||||||
import { postStatus } from '../routes/_api/statuses'
|
import { postStatus } from '../routes/_api/statuses'
|
||||||
import { deleteStatus } from '../routes/_api/delete'
|
import { deleteStatus } from '../routes/_api/delete'
|
||||||
import { authorizeFollowRequest, getFollowRequests } from '../routes/_actions/followRequests'
|
import { authorizeFollowRequest, getFollowRequests } from '../routes/_actions/followRequests'
|
||||||
|
import { followAccount, unfollowAccount } from '../routes/_api/follow'
|
||||||
|
|
||||||
global.fetch = fetch
|
global.fetch = fetch
|
||||||
global.File = FileApi.File
|
global.File = FileApi.File
|
||||||
|
@ -37,3 +38,11 @@ export async function getFollowRequestsAs (username) {
|
||||||
export async function authorizeFollowRequestAs (username, id) {
|
export async function authorizeFollowRequestAs (username, id) {
|
||||||
return authorizeFollowRequest(instanceName, users[username].accessToken, id)
|
return authorizeFollowRequest(instanceName, users[username].accessToken, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function followAs (username, userToFollow) {
|
||||||
|
return followAccount(instanceName, users[username].accessToken, users[userToFollow].id)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function unfollowAs (username, userToFollow) {
|
||||||
|
return unfollowAccount(instanceName, users[username].accessToken, users[userToFollow].id)
|
||||||
|
}
|
||||||
|
|
76
tests/spec/116-follow-requests.js
Normal file
76
tests/spec/116-follow-requests.js
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
import { lockedAccountRole } from '../roles'
|
||||||
|
import { followAs, unfollowAs } from '../serverActions'
|
||||||
|
import {
|
||||||
|
communityNavButton, followersButton, getNthSearchResult, getSearchResultByHref, getUrl, goBack,
|
||||||
|
homeNavButton, sleep
|
||||||
|
} from '../utils'
|
||||||
|
import { users } from '../users'
|
||||||
|
import { Selector as $ } from 'testcafe'
|
||||||
|
|
||||||
|
fixture`116-follow-requests.js`
|
||||||
|
.page`http://localhost:4002`
|
||||||
|
|
||||||
|
const timeout = 30000
|
||||||
|
|
||||||
|
test('Can approve and reject follow requests', async t => {
|
||||||
|
await t.useRole(lockedAccountRole)
|
||||||
|
|
||||||
|
// necessary for re-running this test in local testing
|
||||||
|
await Promise.all([
|
||||||
|
unfollowAs('admin', 'LockedAccount'),
|
||||||
|
unfollowAs('baz', 'LockedAccount'),
|
||||||
|
unfollowAs('quux', 'LockedAccount')
|
||||||
|
])
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
followAs('admin', 'LockedAccount'),
|
||||||
|
followAs('baz', 'LockedAccount'),
|
||||||
|
followAs('quux', 'LockedAccount')
|
||||||
|
])
|
||||||
|
|
||||||
|
await sleep(2000)
|
||||||
|
|
||||||
|
const approveAdminButton = () => getSearchResultByHref(`/accounts/${users.admin.id}`).find('button:nth-child(1)')
|
||||||
|
const rejectBazButton = () => getSearchResultByHref(`/accounts/${users.baz.id}`).find('button:nth-child(2)')
|
||||||
|
const approveQuuxButton = () => getSearchResultByHref(`/accounts/${users.quux.id}`).find('button:nth-child(1)')
|
||||||
|
|
||||||
|
await t.click(communityNavButton)
|
||||||
|
.click($('a[href="/requests"]'))
|
||||||
|
// no guaranteed order on these
|
||||||
|
.expect(getNthSearchResult(1).innerText).match(/(@admin|@baz|@quux)/)
|
||||||
|
.expect(getNthSearchResult(2).innerText).match(/(@admin|@baz|@quux)/)
|
||||||
|
.expect(getNthSearchResult(3).innerText).match(/(@admin|@baz|@quux)/)
|
||||||
|
.expect(getNthSearchResult(4).exists).notOk()
|
||||||
|
// approve admin
|
||||||
|
.expect(approveAdminButton().getAttribute('aria-label')).eql('Approve')
|
||||||
|
.hover(approveAdminButton())
|
||||||
|
.click(approveAdminButton())
|
||||||
|
.expect(getNthSearchResult(1).innerText).match(/(@baz|@quux)/, {timeout})
|
||||||
|
.expect(getNthSearchResult(2).innerText).match(/(@baz|@quux)/)
|
||||||
|
.expect(getNthSearchResult(3).exists).notOk()
|
||||||
|
await goBack()
|
||||||
|
await t
|
||||||
|
.click($('a[href="/requests"]'))
|
||||||
|
// reject baz
|
||||||
|
.expect(rejectBazButton().getAttribute('aria-label')).eql('Reject')
|
||||||
|
.hover(rejectBazButton())
|
||||||
|
.click(rejectBazButton())
|
||||||
|
.expect(getNthSearchResult(1).innerText).contains('@quux', {timeout})
|
||||||
|
.expect(getNthSearchResult(2).exists).notOk()
|
||||||
|
await goBack()
|
||||||
|
await t
|
||||||
|
.click($('a[href="/requests"]'))
|
||||||
|
// approve quux
|
||||||
|
.expect(approveQuuxButton().getAttribute('aria-label')).eql('Approve')
|
||||||
|
.hover(approveQuuxButton())
|
||||||
|
.click(approveQuuxButton())
|
||||||
|
.expect(getNthSearchResult(1).exists).notOk({timeout})
|
||||||
|
// check our follow list to make sure they follow us
|
||||||
|
.click(homeNavButton)
|
||||||
|
.click($('.compose-box-avatar'))
|
||||||
|
.expect(getUrl()).contains(`/accounts/${users.LockedAccount.id}`)
|
||||||
|
.click(followersButton)
|
||||||
|
.expect(getNthSearchResult(1).innerText).match(/(@admin|@quux)/)
|
||||||
|
.expect(getNthSearchResult(2).innerText).match(/(@admin|@quux)/)
|
||||||
|
.expect(getNthSearchResult(3).exists).notOk()
|
||||||
|
})
|
|
@ -1,36 +1,42 @@
|
||||||
export const users = {
|
export const users = {
|
||||||
admin: {
|
admin: {
|
||||||
username: 'admin',
|
username: 'admin',
|
||||||
|
email: 'admin@localhost:3000',
|
||||||
password: 'mastodonadmin',
|
password: 'mastodonadmin',
|
||||||
accessToken: 'f954c8de1fcc0080ff706fa2516d05b60de0d8f5b536255a85ef85a6c32e4afb',
|
accessToken: 'f954c8de1fcc0080ff706fa2516d05b60de0d8f5b536255a85ef85a6c32e4afb',
|
||||||
id: 1
|
id: 1
|
||||||
},
|
},
|
||||||
foobar: {
|
foobar: {
|
||||||
username: 'foobar',
|
username: 'foobar',
|
||||||
|
email: 'foobar@localhost:3000',
|
||||||
password: 'foobarfoobar',
|
password: 'foobarfoobar',
|
||||||
accessToken: 'b48d72074a467e77a18eafc0d52e373dcf2492bcb3fefadc302a81300ec69002',
|
accessToken: 'b48d72074a467e77a18eafc0d52e373dcf2492bcb3fefadc302a81300ec69002',
|
||||||
id: 2
|
id: 2
|
||||||
},
|
},
|
||||||
quux: {
|
quux: {
|
||||||
username: 'quux',
|
username: 'quux',
|
||||||
|
email: 'quux@localhost:3000',
|
||||||
password: 'quuxquuxquux',
|
password: 'quuxquuxquux',
|
||||||
accessToken: '894d3583dbf7d0f4f4784a06db86bdadb6ef0d99453d15afbc03e0c103bd78af',
|
accessToken: '894d3583dbf7d0f4f4784a06db86bdadb6ef0d99453d15afbc03e0c103bd78af',
|
||||||
id: 3
|
id: 3
|
||||||
},
|
},
|
||||||
ExternalLinks: {
|
ExternalLinks: {
|
||||||
username: 'ExternalLinks',
|
username: 'ExternalLinks',
|
||||||
|
email: 'ExternalLinks@localhost:3000',
|
||||||
password: 'ExternalLinksExternalLink',
|
password: 'ExternalLinksExternalLink',
|
||||||
accessToken: 'e9a463ba1729ae0049a97a312af702cb3d08d84de1cc8d6da3fad90af068117b',
|
accessToken: 'e9a463ba1729ae0049a97a312af702cb3d08d84de1cc8d6da3fad90af068117b',
|
||||||
id: 4
|
id: 4
|
||||||
},
|
},
|
||||||
baz: {
|
baz: {
|
||||||
username: 'baz',
|
username: 'baz',
|
||||||
|
email: 'baz@localhost:3000',
|
||||||
password: 'bazbazbaz',
|
password: 'bazbazbaz',
|
||||||
accessToken: '0639238783efdfde849304bc89ec0c4b60b5ef5f261f60859fcd597de081cfdc',
|
accessToken: '0639238783efdfde849304bc89ec0c4b60b5ef5f261f60859fcd597de081cfdc',
|
||||||
id: 5
|
id: 5
|
||||||
},
|
},
|
||||||
LockedAccount: {
|
LockedAccount: {
|
||||||
username: 'LockedAccount',
|
username: 'LockedAccount',
|
||||||
|
email: 'LockedAccount@localhost:3000',
|
||||||
password: 'LockedAccountLockedAccount',
|
password: 'LockedAccountLockedAccount',
|
||||||
accessToken: '39ed9aeffa4b25eda4940f22f29fea66e625c6282c2a8bf0430203c9779e9e98',
|
accessToken: '39ed9aeffa4b25eda4940f22f29fea66e625c6282c2a8bf0430203c9779e9e98',
|
||||||
id: 6
|
id: 6
|
||||||
|
|
|
@ -132,6 +132,10 @@ export function getNthAutosuggestionResult (n) {
|
||||||
return $(`.compose-autosuggest-list-item:nth-child(${n}) button`)
|
return $(`.compose-autosuggest-list-item:nth-child(${n}) button`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getSearchResultByHref (href) {
|
||||||
|
return $(`.search-result a[href="${href}"]`)
|
||||||
|
}
|
||||||
|
|
||||||
export function getNthSearchResult (n) {
|
export function getNthSearchResult (n) {
|
||||||
return $(`.search-result:nth-child(${n}) a`)
|
return $(`.search-result:nth-child(${n}) a`)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue