Pinafore-Web-Client-Frontend/tests/utils.js

392 lines
13 KiB
JavaScript
Raw Normal View History

2018-02-20 02:04:37 +01:00
import { ClientFunction as exec, Selector as $ } from 'testcafe'
2018-03-03 02:54:38 +01:00
import * as images from './images'
import * as blobUtils from './blobUtils'
2018-02-20 02:04:37 +01:00
export const settingsButton = $('nav a[aria-label=Settings]')
export const instanceInput = $('#instanceInput')
2018-04-01 07:08:24 +02:00
export const modalDialog = $('.modal-dialog')
2018-02-21 18:26:22 +01:00
export const modalDialogContents = $('.modal-dialog-contents')
export const closeDialogButton = $('.close-dialog-button')
2018-02-24 23:49:28 +01:00
export const notificationsNavButton = $('nav a[href="/notifications"]')
export const homeNavButton = $('nav a[href="/"]')
2018-03-19 18:09:05 +01:00
export const localTimelineNavButton = $('nav a[href="/local"]')
2018-03-09 03:08:14 +01:00
export const searchNavButton = $('nav a[href="/search"]')
export const communityNavButton = $('nav a[href="/community"]')
2018-04-21 09:33:42 +02:00
export const settingsNavButton = $('nav a[href="/settings"]')
2018-02-24 23:49:28 +01:00
export const formError = $('.form-error-user-error')
2018-02-28 06:20:48 +01:00
export const composeInput = $('.compose-box-input')
2018-03-04 01:12:48 +01:00
export const composeContentWarning = $('.content-warning-input')
2018-02-28 06:20:48 +01:00
export const composeButton = $('.compose-box-button')
export const composeLengthIndicator = $('.compose-box-length')
2018-03-01 03:45:29 +01:00
export const emojiButton = $('.compose-box-toolbar button:first-child')
2018-03-03 06:55:04 +01:00
export const mediaButton = $('.compose-box-toolbar button:nth-child(2)')
2018-03-03 22:23:26 +01:00
export const postPrivacyButton = $('.compose-box-toolbar button:nth-child(3)')
2018-03-04 01:12:48 +01:00
export const contentWarningButton = $('.compose-box-toolbar button:nth-child(4)')
2018-03-01 07:45:42 +01:00
export const emailInput = $('input#user_email')
export const passwordInput = $('input#user_password')
export const authorizeInput = $('button[type=submit]:not(.negative)')
2018-03-07 08:57:06 +01:00
export const logInToInstanceLink = $('a[href="/settings/instances/add"]')
2018-03-09 03:08:14 +01:00
export const searchInput = $('.search-input')
2018-03-10 07:31:26 +01:00
export const postStatusButton = $('.compose-box-button')
2018-03-10 19:54:16 +01:00
export const showMoreButton = $('.more-items-header button')
2018-03-15 06:14:06 +01:00
export const accountProfileName = $('.account-profile .account-profile-name')
export const accountProfileUsername = $('.account-profile .account-profile-username')
export const accountProfileFollowedBy = $('.account-profile .account-profile-followed-by')
export const accountProfileFollowButton = $('.account-profile .account-profile-follow button')
2018-03-16 04:31:58 +01:00
export const goBackButton = $('.dynamic-page-go-back')
export const accountProfileMoreOptionsButton = $('.account-profile-more-options button')
2018-04-21 22:06:46 +02:00
export const addInstanceButton = $('#submitButton')
export const mastodonLogInButton = $('button[type="submit"]')
2018-04-27 07:05:55 +02:00
export const followsButton = $('.account-profile-details > *:nth-child(2)')
export const followersButton = $('.account-profile-details > *:nth-child(3)')
export const avatarInComposeBox = $('.compose-box-avatar')
export const displayNameInComposeBox = $('.compose-box-display-name')
export const generalSettingsButton = $('a[href="/settings/general"]')
export const markMediaSensitiveInput = $('#choice-mark-media-sensitive')
export const neverMarkMediaSensitiveInput = $('#choice-never-mark-media-sensitive')
export const removeEmojiFromDisplayNamesInput = $('#choice-omit-emoji-in-display-names')
export const dialogOptionsOption = $(`.modal-dialog button`)
export const emojiSearchInput = $('.emoji-mart-search input')
export const confirmationDialogOKButton = $('.confirmation-dialog-form-flex button:nth-child(1)')
export const confirmationDialogCancelButton = $('.confirmation-dialog-form-flex button:nth-child(2)')
export const composeModalInput = $('.modal-dialog .compose-box-input')
export const composeModalComposeButton = $('.modal-dialog .compose-box-button')
export const composeModalContentWarningInput = $('.modal-dialog .content-warning-input')
export const composeModalEmojiButton = $('.modal-dialog .compose-box-toolbar button:nth-child(1)')
export const composeModalPostPrivacyButton = $('.modal-dialog .compose-box-toolbar button:nth-child(3)')
export function getComposeModalNthMediaAltInput (n) {
return $(`.modal-dialog .compose-media:nth-child(${n}) .compose-media-alt input`)
}
export function getComposeModalNthMediaImg (n) {
return $(`.modal-dialog .compose-media:nth-child(${n}) img`)
}
2018-02-24 23:49:28 +01:00
export const favoritesCountElement = $('.status-favs').addCustomDOMProperties({
2018-02-24 23:49:28 +01:00
innerCount: el => parseInt(el.innerText, 10)
})
export const reblogsCountElement = $('.status-reblogs').addCustomDOMProperties({
2018-02-24 23:49:28 +01:00
innerCount: el => parseInt(el.innerText, 10)
})
2018-02-20 02:04:37 +01:00
2018-03-10 19:54:16 +01:00
export const sleep = timeout => new Promise(resolve => setTimeout(resolve, timeout))
2018-02-20 02:04:37 +01:00
export const getUrl = exec(() => window.location.href)
export const getActiveElementClassList = exec(() =>
(document.activeElement && (document.activeElement.getAttribute('class') || '').split(/\s+/)) || []
2018-02-21 18:26:22 +01:00
)
export const getActiveElementTagName = exec(() =>
(document.activeElement && document.activeElement.tagName) || ''
)
2018-03-16 04:31:58 +01:00
export const getActiveElementInnerText = exec(() =>
2018-04-11 05:56:42 +02:00
(document.activeElement && document.activeElement.innerText) || ''
2018-03-16 04:31:58 +01:00
)
export const getActiveElementRectTop = exec(() => (
(document.activeElement && document.activeElement.getBoundingClientRect().top) || -1
))
2018-03-17 03:04:48 +01:00
export const getActiveElementInsideNthStatus = exec(() => {
let element = document.activeElement
while (element) {
if (element.hasAttribute('aria-posinset')) {
return element.getAttribute('aria-posinset')
}
element = element.parentElement
}
2018-04-11 05:56:42 +02:00
return ''
2018-03-17 03:04:48 +01:00
})
export const getTitleText = exec(() => document.head.querySelector('title') && document.head.querySelector('title').innerHTML)
2018-02-21 18:26:22 +01:00
export const goBack = exec(() => window.history.back())
export const goForward = exec(() => window.history.forward())
export const reload = exec(() => window.location.reload())
export const forceOffline = exec(() => window.__forceOnline(false))
2018-03-09 03:08:14 +01:00
export const forceOnline = exec(() => window.__forceOnline(true))
2018-03-09 03:08:14 +01:00
2018-03-01 03:45:29 +01:00
export const getComposeSelectionStart = exec(() => composeInput().selectionStart, {
dependencies: { composeInput }
})
export const getOpacity = selector => exec(() => window.getComputedStyle(document.querySelector(selector)).opacity, {
dependencies: { selector }
})
export const getCurrentTheme = exec(() => {
let themeLink = document.head.querySelector('link[rel=stylesheet][href^="/theme-"]')
if (themeLink) {
return themeLink.getAttribute('href').match(/^\/theme-(.*)\.css$/, '')[1]
}
return 'default'
})
2018-03-03 06:55:04 +01:00
export const uploadKittenImage = i => (exec(() => {
let image = images[`kitten${i}`]
let blob = blobUtils.base64StringToBlob(image.data, 'image/png')
blob.name = image.name
let fileDrop = document.querySelector('file-drop')
let event = new Event('filedrop', { bubbles: false })
event.files = [blob]
fileDrop.dispatchEvent(event)
2018-03-03 02:54:38 +01:00
}, {
dependencies: {
images,
2018-03-03 06:55:04 +01:00
blobUtils,
i
2018-03-03 02:54:38 +01:00
}
2018-03-03 06:55:04 +01:00
}))
export const focus = (selector) => (exec(() => {
document.querySelector(selector).focus()
}, {
dependencies: {
selector
}
}))
export const isNthStatusActive = (idx) => (exec(() => {
return document.activeElement &&
document.activeElement.getAttribute('aria-posinset') === idx.toString()
}, {
dependencies: { idx }
}))
export const isActiveStatusPinned = exec(() => {
let el = document.activeElement
return el &&
(
(el.parentElement.getAttribute('class') || '').includes('pinned') ||
(el.parentElement.parentElement.getAttribute('class') || '').includes('pinned')
)
})
2018-11-22 07:08:37 +01:00
export const scrollToBottom = exec(() => {
document.scrollingElement.scrollTop = document.scrollingElement.scrollHeight
})
export const scrollToTop = exec(() => {
document.scrollingElement.scrollTop = 0
})
export function getNthMediaAltInput (n) {
return $(`.compose-box .compose-media:nth-child(${n}) .compose-media-alt input`)
}
2018-03-30 10:06:17 +02:00
export function getNthComposeReplyInput (n) {
return $(`${getNthStatusSelector(n)} .compose-box-input`)
2018-03-30 10:06:17 +02:00
}
export function getNthComposeReplyButton (n) {
return $(`${getNthStatusSelector(n)} .compose-box-button`)
2018-03-30 10:06:17 +02:00
}
2018-03-30 19:04:35 +02:00
export function getNthPostPrivacyButton (n) {
return $(`${getNthStatusSelector(n)} .compose-box-toolbar button:nth-child(3)`)
2018-03-30 19:04:35 +02:00
}
2018-03-25 21:24:38 +02:00
export function getNthAutosuggestionResult (n) {
return $(`.compose-autosuggest-list-item:nth-child(${n}) button`)
}
export function getSearchResultByHref (href) {
return $(`.search-result a[href="${href}"]`)
}
2018-03-09 03:08:14 +01:00
export function getNthSearchResult (n) {
return $(`.search-result:nth-child(${n}) a`)
}
2018-03-03 06:55:04 +01:00
export function getNthMedia (n) {
return $(`.compose-media:nth-child(${n}) img`)
}
2018-03-03 02:54:38 +01:00
2018-03-03 19:27:14 +01:00
export function getNthDeleteMediaButton (n) {
return $(`.compose-media:nth-child(${n}) .compose-media-delete-button`)
}
export function getAriaSetSize () {
return getNthStatus(1 + 0).getAttribute('aria-setsize')
}
2018-02-21 06:08:26 +01:00
export function getNthStatus (n) {
return $(getNthStatusSelector(n))
}
export function getNthStatusSelector (n) {
2018-06-11 04:49:39 +02:00
return `.list-item > article[aria-posinset="${n}"]`
2018-02-21 06:08:26 +01:00
}
export function getNthStatusContent (n) {
return $(`${getNthStatusSelector(n)} .status-content`)
}
export function getNthStatusSpoiler (n) {
return $(`${getNthStatusSelector(n)} .status-spoiler`)
}
export function getNthStatusSensitiveMediaButton (n) {
return $(`${getNthStatusSelector(n)} .status-sensitive-media-button`)
}
export function getNthStatusMedia (n) {
return $(`${getNthStatusSelector(n)} .status-media`)
}
export function getNthStatusRelativeDate (n) {
return $(`${getNthStatusSelector(n)} .status-relative-date`)
}
export function getNthStatusMediaImg (n) {
return $(`${getNthStatusSelector(n)} .status-media img`)
}
export function getNthStatusHeader (n) {
return $(`${getNthStatusSelector(n)} .status-header`)
}
export function getNthStatusAndImage (nStatus, nImage) {
return $(`${getNthStatusSelector(nStatus)} .status-media .show-image-button:nth-child(${nImage + 1}) img`)
}
2018-02-24 23:49:28 +01:00
export function getFirstVisibleStatus () {
2018-06-11 04:49:39 +02:00
return $(`.list-item > article[aria-posinset]`).nth(0)
2018-02-24 23:49:28 +01:00
}
2018-03-09 17:45:12 +01:00
export function getNthReplyButton (n) {
return $(`${getNthStatusSelector(n)} .status-toolbar button:nth-child(1)`)
2018-03-09 17:45:12 +01:00
}
2018-04-08 22:42:31 +02:00
export function getNthReplyContentWarningInput (n) {
return $(`${getNthStatusSelector(n)} .content-warning-input`)
2018-04-08 22:42:31 +02:00
}
export function getNthReplyContentWarningButton (n) {
return $(`${getNthStatusSelector(n)} .compose-box-toolbar button:nth-child(4)`)
2018-04-08 22:42:31 +02:00
}
export function getNthReplyPostPrivacyButton (n) {
return $(`${getNthStatusSelector(n)} .compose-box-toolbar button:nth-child(3)`)
2018-04-08 22:42:31 +02:00
}
export function getNthPostPrivacyOptionInDialog (n) {
return $(`.generic-dialog-list li:nth-child(${n}) button`)
}
2018-02-24 23:49:28 +01:00
export function getNthFavoriteButton (n) {
return $(`${getNthStatusSelector(n)} .status-toolbar button:nth-child(3)`)
2018-02-24 23:49:28 +01:00
}
export function getNthStatusOptionsButton (n) {
return $(`${getNthStatusSelector(n)} .status-toolbar button:nth-child(4)`)
}
2018-02-24 23:49:28 +01:00
export function getNthFavorited (n) {
return getNthFavoriteButton(n).getAttribute('aria-pressed')
}
export function getNthShowOrHideButton (n) {
return $(`${getNthStatusSelector(n)} .status-spoiler-button button`)
}
2018-02-24 23:49:28 +01:00
export function getFavoritesCount () {
return favoritesCountElement.innerCount
}
2018-02-25 03:20:33 +01:00
export function getNthReblogButton (n) {
return $(`${getNthStatusSelector(n)} .status-toolbar button:nth-child(2)`)
2018-02-25 03:20:33 +01:00
}
export function getNthReblogged (n) {
return getNthReblogButton(n).getAttribute('aria-pressed')
}
export function getNthDialogOptionsOption (n) {
return $(`.modal-dialog li:nth-child(${n}) button`)
}
2018-02-24 23:49:28 +01:00
export function getReblogsCount () {
return reblogsCountElement.innerCount
2018-02-20 02:04:37 +01:00
}
function getNthPinnedStatusSelector (n) {
return `.pinned-statuses article[aria-posinset="${n}"]`
}
export function getNthPinnedStatus (n) {
return $(getNthPinnedStatusSelector(n))
}
export function getNthPinnedStatusFavoriteButton (n) {
return $(`${getNthPinnedStatusSelector(n)} .status-toolbar button:nth-child(3)`)
}
2018-02-20 03:25:59 +01:00
export async function validateTimeline (t, timeline) {
const timeout = 30000
2018-02-20 02:04:37 +01:00
for (let i = 0; i < timeline.length; i++) {
let status = timeline[i]
// hovering forces TestCafé to scroll to that element: https://git.io/vABV2
await t.hover(getNthStatus(1 + i))
2018-02-20 02:04:37 +01:00
if (status.content) {
await t.expect(getNthStatusContent(1 + i).innerText)
.contains(status.content, { timeout })
2018-02-20 02:04:37 +01:00
}
if (status.spoiler) {
await t.expect(getNthStatusSpoiler(1 + i).innerText)
.contains(status.spoiler, { timeout })
2018-02-20 02:04:37 +01:00
}
if (status.followedBy) {
await t.expect(getNthStatusHeader(1 + i).innerText)
.match(new RegExp(status.followedBy + '\\s+followed you'), { timeout })
2018-02-20 02:04:37 +01:00
}
if (status.rebloggedBy) {
await t.expect(getNthStatusHeader(1 + i).innerText)
.match(new RegExp(status.rebloggedBy + '\\s+boosted your status'), { timeout })
2018-02-20 02:04:37 +01:00
}
if (status.favoritedBy) {
await t.expect(getNthStatusHeader(1 + i).innerText)
.match(new RegExp(status.favoritedBy + '\\s+favorited your status'), { timeout })
2018-02-20 02:04:37 +01:00
}
}
2018-02-20 03:25:59 +01:00
}
2018-02-21 06:08:26 +01:00
2018-02-21 18:26:22 +01:00
export async function scrollToStatus (t, n) {
let timeout = 20000
for (let i = 1; i < n; i++) {
await t.expect(getNthStatus(i).exists).ok({ timeout })
.hover(getNthStatus(i))
.expect($('.loading-footer').exist).notOk({ timeout })
.expect($(`${getNthStatusSelector(i)} .status-toolbar`).exists).ok({ timeout })
.hover($(`${getNthStatusSelector(i)} .status-toolbar`))
.expect($('.loading-footer').exist).notOk({ timeout })
2018-02-21 18:26:22 +01:00
}
await t.hover(getNthStatus(n))
}
export async function clickToNotificationsAndBackHome (t) {
await t.click(notificationsNavButton)
.expect(getUrl()).eql('http://localhost:4002/notifications')
.click(homeNavButton)
.expect(getUrl()).eql('http://localhost:4002/')
}
2018-04-19 05:43:13 +02:00
// like lodash.times but I don't want to try to figure out esm
// just to import lodash-es
export function times (n, cb) {
let arr = []
for (let i = 0; i < n; i++) {
arr.push(cb(i))
}
return arr
}