fix: add tests for polls, improve a11y of poll form (#1239)
This commit is contained in:
parent
b45868bbfd
commit
37d3cac7d2
|
@ -1,6 +1,6 @@
|
|||
<div class={computedClass} aria-busy={loading} >
|
||||
{#if voted || expired }
|
||||
<ul class="options" aria-label="Poll results">
|
||||
<ul aria-label="Poll results">
|
||||
{#each options as option}
|
||||
<li class="option">
|
||||
<div class="option-text">
|
||||
|
@ -14,19 +14,21 @@
|
|||
</ul>
|
||||
{:else}
|
||||
<form class="poll-form" aria-label="Vote on poll" on:submit="onSubmit(event)" ref:form>
|
||||
{#each options as option, i}
|
||||
<div class="poll-form-option">
|
||||
<input type="{multiple ? 'checkbox' : 'radio'}"
|
||||
id="poll-choice-{uuid}-{i}"
|
||||
name="poll-choice-{uuid}"
|
||||
value="{i}"
|
||||
on:change="onChange()"
|
||||
>
|
||||
<label for="poll-choice-{uuid}-{i}">
|
||||
{option.title}
|
||||
</label>
|
||||
</div>
|
||||
{/each}
|
||||
<ul aria-label="Poll choices">
|
||||
{#each options as option, i}
|
||||
<li class="poll-form-option">
|
||||
<input type="{multiple ? 'checkbox' : 'radio'}"
|
||||
id="poll-choice-{uuid}-{i}"
|
||||
name="poll-choice-{uuid}"
|
||||
value="{i}"
|
||||
on:change="onChange()"
|
||||
>
|
||||
<label for="poll-choice-{uuid}-{i}">
|
||||
{option.title}
|
||||
</label>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
<button disabled={formDisabled} type="submit">Vote</button>
|
||||
</form>
|
||||
{/if}
|
||||
|
@ -71,13 +73,18 @@
|
|||
pointer-events: none;
|
||||
}
|
||||
|
||||
ul.options {
|
||||
ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
li.option {
|
||||
li {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.option {
|
||||
margin: 0 0 10px 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
|
@ -86,10 +93,6 @@
|
|||
stroke-width: 10px;
|
||||
}
|
||||
|
||||
li.option:last-child {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.option-text {
|
||||
word-wrap: break-word;
|
||||
white-space: pre-wrap;
|
||||
|
|
|
@ -9,6 +9,8 @@ import { followAccount, unfollowAccount } from '../src/routes/_api/follow'
|
|||
import { updateCredentials } from '../src/routes/_api/updateCredentials'
|
||||
import { reblogStatus } from '../src/routes/_api/reblog'
|
||||
import { submitMedia } from './submitMedia'
|
||||
import { voteOnPoll } from '../src/routes/_api/polls'
|
||||
import { POLL_EXPIRY_DEFAULT } from '../src/routes/_static/polls'
|
||||
|
||||
global.fetch = fetch
|
||||
global.File = FileApi.File
|
||||
|
@ -68,3 +70,15 @@ export async function unfollowAs (username, userToFollow) {
|
|||
export async function updateUserDisplayNameAs (username, displayName) {
|
||||
return updateCredentials(instanceName, users[username].accessToken, { display_name: displayName })
|
||||
}
|
||||
|
||||
export async function createPollAs (username, content, options, multiple) {
|
||||
return postStatus(instanceName, users[username].accessToken, content, null, null, false, null, 'public', {
|
||||
options,
|
||||
multiple,
|
||||
expires_in: POLL_EXPIRY_DEFAULT
|
||||
})
|
||||
}
|
||||
|
||||
export async function voteOnPollAs (username, pollId, choices) {
|
||||
return voteOnPoll(instanceName, users[username].accessToken, pollId, choices.map(_ => _.toString()))
|
||||
}
|
||||
|
|
94
tests/spec/126-polls.js
Normal file
94
tests/spec/126-polls.js
Normal file
|
@ -0,0 +1,94 @@
|
|||
import {
|
||||
getNthStatusContent,
|
||||
getNthStatusPollOption,
|
||||
getNthStatusPollVoteButton,
|
||||
getNthStatusPollForm,
|
||||
getNthStatusPollResult,
|
||||
sleep,
|
||||
getNthStatusPollRefreshButton,
|
||||
getNthStatusPollVoteCount,
|
||||
getNthStatusRelativeDate, getUrl, goBack
|
||||
} from '../utils'
|
||||
import { loginAsFoobar } from '../roles'
|
||||
import { createPollAs, voteOnPollAs } from '../serverActions'
|
||||
|
||||
fixture`126-polls.js`
|
||||
.page`http://localhost:4002`
|
||||
|
||||
test('Can vote on polls', async t => {
|
||||
await loginAsFoobar(t)
|
||||
await createPollAs('admin', 'vote on my cool poll', ['yes', 'no'], false)
|
||||
await t
|
||||
.expect(getNthStatusContent(1).innerText).contains('vote on my cool poll')
|
||||
.expect(getNthStatusPollVoteCount(1).innerText).eql('0 votes')
|
||||
.click(getNthStatusPollOption(1, 2))
|
||||
.click(getNthStatusPollVoteButton(1))
|
||||
.expect(getNthStatusPollForm(1).exists).notOk({ timeout: 20000 })
|
||||
.expect(getNthStatusPollResult(1, 1).innerText).eql('0% yes')
|
||||
.expect(getNthStatusPollResult(1, 2).innerText).eql('100% no')
|
||||
.expect(getNthStatusPollVoteCount(1).innerText).eql('1 vote')
|
||||
})
|
||||
|
||||
test('Can vote on multiple-choice polls', async t => {
|
||||
await loginAsFoobar(t)
|
||||
await createPollAs('admin', 'vote on my other poll', ['yes', 'no', 'maybe'], true)
|
||||
await t
|
||||
.expect(getNthStatusContent(1).innerText).contains('vote on my other poll')
|
||||
.click(getNthStatusPollOption(1, 1))
|
||||
.click(getNthStatusPollOption(1, 3))
|
||||
.click(getNthStatusPollVoteButton(1))
|
||||
.expect(getNthStatusPollForm(1).exists).notOk({ timeout: 20000 })
|
||||
.expect(getNthStatusPollResult(1, 1).innerText).eql('50% yes')
|
||||
.expect(getNthStatusPollResult(1, 2).innerText).eql('0% no')
|
||||
.expect(getNthStatusPollResult(1, 3).innerText).eql('50% maybe')
|
||||
.expect(getNthStatusPollVoteCount(1).innerText).eql('2 votes')
|
||||
})
|
||||
|
||||
test('Can update poll results', async t => {
|
||||
const { poll } = await createPollAs('admin', 'vote on this poll', ['yes', 'no', 'maybe'], false)
|
||||
const { id: pollId } = poll
|
||||
await voteOnPollAs('baz', pollId, [1])
|
||||
await voteOnPollAs('ExternalLinks', pollId, [1])
|
||||
await voteOnPollAs('foobar', pollId, [2])
|
||||
await sleep(1000)
|
||||
await loginAsFoobar(t)
|
||||
await t
|
||||
.expect(getNthStatusContent(1).innerText).contains('vote on this poll')
|
||||
.expect(getNthStatusPollForm(1).exists).notOk()
|
||||
.expect(getNthStatusPollResult(1, 1).innerText).eql('0% yes')
|
||||
.expect(getNthStatusPollResult(1, 2).innerText).eql('67% no')
|
||||
.expect(getNthStatusPollResult(1, 3).innerText).eql('33% maybe')
|
||||
.expect(getNthStatusPollVoteCount(1).innerText).eql('3 votes')
|
||||
await sleep(1000)
|
||||
await voteOnPollAs('quux', pollId, [0])
|
||||
await sleep(1000)
|
||||
await t
|
||||
.click(getNthStatusPollRefreshButton(1))
|
||||
.expect(getNthStatusPollResult(1, 1).innerText).eql('25% yes', { timeout: 20000 })
|
||||
.expect(getNthStatusPollResult(1, 2).innerText).eql('50% no')
|
||||
.expect(getNthStatusPollResult(1, 3).innerText).eql('25% maybe')
|
||||
.expect(getNthStatusPollVoteCount(1).innerText).eql('4 votes')
|
||||
})
|
||||
|
||||
test('Poll results refresh everywhere', async t => {
|
||||
await loginAsFoobar(t)
|
||||
await createPollAs('admin', 'another poll', ['yes', 'no'], false)
|
||||
await t
|
||||
.expect(getNthStatusContent(1).innerText).contains('another poll')
|
||||
.click(getNthStatusRelativeDate(1))
|
||||
.expect(getUrl()).contains('/statuses')
|
||||
.expect(getNthStatusContent(1).innerText).contains('another poll')
|
||||
.click(getNthStatusPollOption(1, 1))
|
||||
.click(getNthStatusPollVoteButton(1))
|
||||
.expect(getNthStatusPollForm(1).exists).notOk({ timeout: 20000 })
|
||||
.expect(getNthStatusPollResult(1, 1).innerText).eql('100% yes')
|
||||
.expect(getNthStatusPollResult(1, 2).innerText).eql('0% no')
|
||||
.expect(getNthStatusPollVoteCount(1).innerText).eql('1 vote')
|
||||
await goBack()
|
||||
await t
|
||||
.expect(getUrl()).eql('http://localhost:4002/')
|
||||
.expect(getNthStatusPollForm(1).exists).notOk({ timeout: 20000 })
|
||||
.expect(getNthStatusPollResult(1, 1).innerText).eql('100% yes')
|
||||
.expect(getNthStatusPollResult(1, 2).innerText).eql('0% no')
|
||||
.expect(getNthStatusPollVoteCount(1).innerText).eql('1 vote')
|
||||
})
|
67
tests/spec/127-compose-polls.js
Normal file
67
tests/spec/127-compose-polls.js
Normal file
|
@ -0,0 +1,67 @@
|
|||
import {
|
||||
getNthStatusContent,
|
||||
getNthStatusPollForm,
|
||||
getNthStatusPollResult,
|
||||
getNthStatusPollVoteCount,
|
||||
pollButton,
|
||||
getComposePollNthInput,
|
||||
composePoll,
|
||||
composePollMultipleChoice,
|
||||
composePollExpiry, composePollAddButton, getComposePollRemoveNthButton, postStatusButton, composeInput
|
||||
} from '../utils'
|
||||
import { loginAsFoobar } from '../roles'
|
||||
import { POLL_EXPIRY_DEFAULT } from '../../src/routes/_static/polls'
|
||||
|
||||
fixture`127-compose-polls.js`
|
||||
.page`http://localhost:4002`
|
||||
|
||||
test('Can add and remove poll', async t => {
|
||||
await loginAsFoobar(t)
|
||||
await t
|
||||
.expect(composePoll.exists).notOk()
|
||||
.expect(pollButton.getAttribute('aria-label')).eql('Add poll')
|
||||
.click(pollButton)
|
||||
.expect(composePoll.exists).ok()
|
||||
.expect(getComposePollNthInput(1).value).eql('')
|
||||
.expect(getComposePollNthInput(2).value).eql('')
|
||||
.expect(getComposePollNthInput(3).exists).notOk()
|
||||
.expect(getComposePollNthInput(4).exists).notOk()
|
||||
.expect(composePollMultipleChoice.checked).notOk()
|
||||
.expect(composePollExpiry.value).eql(POLL_EXPIRY_DEFAULT.toString())
|
||||
.expect(pollButton.getAttribute('aria-label')).eql('Remove poll')
|
||||
.click(pollButton)
|
||||
.expect(composePoll.exists).notOk()
|
||||
})
|
||||
|
||||
test('Can add and remove poll options', async t => {
|
||||
await loginAsFoobar(t)
|
||||
await t
|
||||
.expect(composePoll.exists).notOk()
|
||||
.expect(pollButton.getAttribute('aria-label')).eql('Add poll')
|
||||
.click(pollButton)
|
||||
.expect(composePoll.exists).ok()
|
||||
.typeText(getComposePollNthInput(1), 'first', { paste: true })
|
||||
.typeText(getComposePollNthInput(2), 'second', { paste: true })
|
||||
.click(composePollAddButton)
|
||||
.typeText(getComposePollNthInput(3), 'third', { paste: true })
|
||||
.expect(getComposePollNthInput(1).value).eql('first')
|
||||
.expect(getComposePollNthInput(2).value).eql('second')
|
||||
.expect(getComposePollNthInput(3).value).eql('third')
|
||||
.expect(getComposePollNthInput(4).exists).notOk()
|
||||
.click(getComposePollRemoveNthButton(2))
|
||||
.expect(getComposePollNthInput(1).value).eql('first')
|
||||
.expect(getComposePollNthInput(2).value).eql('third')
|
||||
.expect(getComposePollNthInput(3).exists).notOk()
|
||||
.expect(getComposePollNthInput(4).exists).notOk()
|
||||
.click(composePollAddButton)
|
||||
.typeText(getComposePollNthInput(3), 'fourth', { paste: true })
|
||||
.typeText(composeInput, 'Vote on my poll!!!', { paste: true })
|
||||
.click(postStatusButton)
|
||||
.expect(getNthStatusContent(1).innerText).contains('Vote on my poll!!!')
|
||||
.expect(getNthStatusPollForm(1).exists).notOk()
|
||||
.expect(getNthStatusPollResult(1, 1).innerText).eql('0% first')
|
||||
.expect(getNthStatusPollResult(1, 2).innerText).eql('0% third')
|
||||
.expect(getNthStatusPollResult(1, 3).innerText).eql('0% fourth')
|
||||
.expect(getNthStatusPollResult(1, 4).exists).notOk()
|
||||
.expect(getNthStatusPollVoteCount(1).innerText).eql('0 votes')
|
||||
})
|
|
@ -22,6 +22,7 @@ export const composeButton = $('.compose-box-button')
|
|||
export const composeLengthIndicator = $('.compose-box-length')
|
||||
export const emojiButton = $('.compose-box-toolbar button:first-child')
|
||||
export const mediaButton = $('.compose-box-toolbar button:nth-child(2)')
|
||||
export const pollButton = $('.compose-box-toolbar button:nth-child(3)')
|
||||
export const postPrivacyButton = $('.compose-box-toolbar button:nth-child(4)')
|
||||
export const contentWarningButton = $('.compose-box-toolbar button:nth-child(5)')
|
||||
export const emailInput = $('input#user_email')
|
||||
|
@ -58,6 +59,11 @@ export const composeModalContentWarningInput = $('.modal-dialog .content-warning
|
|||
export const composeModalEmojiButton = $('.modal-dialog .compose-box-toolbar button:nth-child(1)')
|
||||
export const composeModalPostPrivacyButton = $('.modal-dialog .compose-box-toolbar button:nth-child(4)')
|
||||
|
||||
export const composePoll = $('.compose-poll')
|
||||
export const composePollMultipleChoice = $('.compose-poll input[type="checkbox"]')
|
||||
export const composePollExpiry = $('.compose-poll select')
|
||||
export const composePollAddButton = $('.compose-poll button:last-of-type')
|
||||
|
||||
export const postPrivacyDialogButtonUnlisted = $('[aria-label="Post privacy dialog"] li:nth-child(2) button')
|
||||
|
||||
export const accountProfileFilterStatuses = $('.account-profile-filters li:nth-child(1)')
|
||||
|
@ -220,6 +226,38 @@ export function getNthPostPrivacyButton (n) {
|
|||
return $(`${getNthStatusSelector(n)} .compose-box-toolbar button:nth-child(4)`)
|
||||
}
|
||||
|
||||
export function getNthStatusPollOption (n, i) {
|
||||
return $(`${getNthStatusSelector(n)} .poll li:nth-child(${i}) input`)
|
||||
}
|
||||
|
||||
export function getNthStatusPollVoteButton (n) {
|
||||
return $(`${getNthStatusSelector(n)} .poll button`)
|
||||
}
|
||||
|
||||
export function getNthStatusPollForm (n) {
|
||||
return $(`${getNthStatusSelector(n)} .poll form`)
|
||||
}
|
||||
|
||||
export function getNthStatusPollResult (n, i) {
|
||||
return $(`${getNthStatusSelector(n)} .poll li:nth-child(${i})`)
|
||||
}
|
||||
|
||||
export function getNthStatusPollRefreshButton (n) {
|
||||
return $(`${getNthStatusSelector(n)} button.poll-stat`)
|
||||
}
|
||||
|
||||
export function getNthStatusPollVoteCount (n) {
|
||||
return $(`${getNthStatusSelector(n)} .poll .poll-stat:nth-child(1) .poll-stat-text`)
|
||||
}
|
||||
|
||||
export function getComposePollNthInput (n) {
|
||||
return $(`.compose-poll input[type="text"]:nth-of-type(${n})`)
|
||||
}
|
||||
|
||||
export function getComposePollRemoveNthButton (n) {
|
||||
return $(`.compose-poll button:nth-of-type(${n})`)
|
||||
}
|
||||
|
||||
export function getNthAutosuggestionResult (n) {
|
||||
return $(`.compose-autosuggest-list-item:nth-child(${n})`)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue