fix: add tests for polls, improve a11y of poll form (#1239)

This commit is contained in:
Nolan Lawson 2019-05-27 12:31:35 -07:00 committed by GitHub
parent b45868bbfd
commit 37d3cac7d2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 236 additions and 20 deletions

View file

@ -1,6 +1,6 @@
<div class={computedClass} aria-busy={loading} > <div class={computedClass} aria-busy={loading} >
{#if voted || expired } {#if voted || expired }
<ul class="options" aria-label="Poll results"> <ul aria-label="Poll results">
{#each options as option} {#each options as option}
<li class="option"> <li class="option">
<div class="option-text"> <div class="option-text">
@ -14,19 +14,21 @@
</ul> </ul>
{:else} {:else}
<form class="poll-form" aria-label="Vote on poll" on:submit="onSubmit(event)" ref:form> <form class="poll-form" aria-label="Vote on poll" on:submit="onSubmit(event)" ref:form>
{#each options as option, i} <ul aria-label="Poll choices">
<div class="poll-form-option"> {#each options as option, i}
<input type="{multiple ? 'checkbox' : 'radio'}" <li class="poll-form-option">
id="poll-choice-{uuid}-{i}" <input type="{multiple ? 'checkbox' : 'radio'}"
name="poll-choice-{uuid}" id="poll-choice-{uuid}-{i}"
value="{i}" name="poll-choice-{uuid}"
on:change="onChange()" value="{i}"
> on:change="onChange()"
<label for="poll-choice-{uuid}-{i}"> >
{option.title} <label for="poll-choice-{uuid}-{i}">
</label> {option.title}
</div> </label>
{/each} </li>
{/each}
</ul>
<button disabled={formDisabled} type="submit">Vote</button> <button disabled={formDisabled} type="submit">Vote</button>
</form> </form>
{/if} {/if}
@ -71,13 +73,18 @@
pointer-events: none; pointer-events: none;
} }
ul.options { ul {
list-style: none; list-style: none;
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
li.option { li {
margin: 0;
padding: 0;
}
.option {
margin: 0 0 10px 0; margin: 0 0 10px 0;
padding: 0; padding: 0;
display: flex; display: flex;
@ -86,10 +93,6 @@
stroke-width: 10px; stroke-width: 10px;
} }
li.option:last-child {
margin: 0;
}
.option-text { .option-text {
word-wrap: break-word; word-wrap: break-word;
white-space: pre-wrap; white-space: pre-wrap;

View file

@ -9,6 +9,8 @@ import { followAccount, unfollowAccount } from '../src/routes/_api/follow'
import { updateCredentials } from '../src/routes/_api/updateCredentials' import { updateCredentials } from '../src/routes/_api/updateCredentials'
import { reblogStatus } from '../src/routes/_api/reblog' import { reblogStatus } from '../src/routes/_api/reblog'
import { submitMedia } from './submitMedia' import { submitMedia } from './submitMedia'
import { voteOnPoll } from '../src/routes/_api/polls'
import { POLL_EXPIRY_DEFAULT } from '../src/routes/_static/polls'
global.fetch = fetch global.fetch = fetch
global.File = FileApi.File global.File = FileApi.File
@ -68,3 +70,15 @@ export async function unfollowAs (username, userToFollow) {
export async function updateUserDisplayNameAs (username, displayName) { export async function updateUserDisplayNameAs (username, displayName) {
return updateCredentials(instanceName, users[username].accessToken, { display_name: 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
View 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')
})

View 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')
})

View file

@ -22,6 +22,7 @@ export const composeButton = $('.compose-box-button')
export const composeLengthIndicator = $('.compose-box-length') export const composeLengthIndicator = $('.compose-box-length')
export const emojiButton = $('.compose-box-toolbar button:first-child') export const emojiButton = $('.compose-box-toolbar button:first-child')
export const mediaButton = $('.compose-box-toolbar button:nth-child(2)') 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 postPrivacyButton = $('.compose-box-toolbar button:nth-child(4)')
export const contentWarningButton = $('.compose-box-toolbar button:nth-child(5)') export const contentWarningButton = $('.compose-box-toolbar button:nth-child(5)')
export const emailInput = $('input#user_email') 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 composeModalEmojiButton = $('.modal-dialog .compose-box-toolbar button:nth-child(1)')
export const composeModalPostPrivacyButton = $('.modal-dialog .compose-box-toolbar button:nth-child(4)') 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 postPrivacyDialogButtonUnlisted = $('[aria-label="Post privacy dialog"] li:nth-child(2) button')
export const accountProfileFilterStatuses = $('.account-profile-filters li:nth-child(1)') 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)`) 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) { export function getNthAutosuggestionResult (n) {
return $(`.compose-autosuggest-list-item:nth-child(${n})`) return $(`.compose-autosuggest-list-item:nth-child(${n})`)
} }