@@ -14,19 +14,21 @@
{:else}
{/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;
diff --git a/tests/serverActions.js b/tests/serverActions.js
index 34bc7d3..c55e24d 100644
--- a/tests/serverActions.js
+++ b/tests/serverActions.js
@@ -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()))
+}
diff --git a/tests/spec/126-polls.js b/tests/spec/126-polls.js
new file mode 100644
index 0000000..5df6267
--- /dev/null
+++ b/tests/spec/126-polls.js
@@ -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')
+})
diff --git a/tests/spec/127-compose-polls.js b/tests/spec/127-compose-polls.js
new file mode 100644
index 0000000..785009d
--- /dev/null
+++ b/tests/spec/127-compose-polls.js
@@ -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')
+})
diff --git a/tests/utils.js b/tests/utils.js
index 43e7878..8dc14cc 100644
--- a/tests/utils.js
+++ b/tests/utils.js
@@ -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})`)
}