add test for compose, and emoji

This commit is contained in:
Nolan Lawson 2018-02-28 18:45:29 -08:00
parent b26fdc7f55
commit 2614e451b2
9 changed files with 107 additions and 59 deletions

Binary file not shown.

Binary file not shown.

View file

@ -2,6 +2,7 @@ import { cacheFirstUpdateAfter } from '../_utils/sync'
import { database } from '../_database/database' import { database } from '../_database/database'
import { getCustomEmoji } from '../_api/emoji' import { getCustomEmoji } from '../_api/emoji'
import { store } from '../_store/store' import { store } from '../_store/store'
import { substring } from 'stringz'
export async function updateCustomEmojiForInstance (instanceName) { export async function updateCustomEmojiForInstance (instanceName) {
await cacheFirstUpdateAfter( await cacheFirstUpdateAfter(
@ -17,5 +18,12 @@ export async function updateCustomEmojiForInstance (instanceName) {
} }
export function insertEmoji (emoji) { export function insertEmoji (emoji) {
store.set({emojiToInsert: emoji}) let idx = store.get('composeSelectionStart') || 0
let oldText = store.get('rawComposeText')
let pre = substring(oldText, 0, idx)
let post = substring(oldText, idx)
let newText = `${pre}:${emoji.shortcode}: ${post}`
store.set({
rawComposeText: newText
})
} }

View file

@ -3,6 +3,7 @@
placeholder="What's on your mind?" placeholder="What's on your mind?"
ref:textarea ref:textarea
bind:value=$rawComposeText bind:value=$rawComposeText
on:blur="onBlur()"
></textarea> ></textarea>
<style> <style>
.compose-box-input { .compose-box-input {
@ -48,34 +49,21 @@
this.store.set({composeText: composeText}) this.store.set({composeText: composeText})
saveText() saveText()
}, {init: false}) }, {init: false})
this.observe('emojiToInsert', emojiToInsert => {
if (!emojiToInsert) {
return
}
requestAnimationFrame(() => {
let idx = this.refs.textarea.selectionStart || 0
let oldText = this.store.get('rawComposeText')
let newText = oldText.substring(0, idx) +
':' + emojiToInsert.shortcode + ': ' +
oldText.substring(idx)
this.store.set({
rawComposeText: newText,
emojiToInsert: null
})
})
}, {init: false})
}, },
ondestroy() { ondestroy() {
mark('autosize.destroy()') mark('autosize.destroy()')
autosize.destroy(this.refs.textarea) autosize.destroy(this.refs.textarea)
stop('autosize.destroy()') stop('autosize.destroy()')
}, },
methods: {
onBlur() {
this.store.set({composeSelectionStart: this.refs.textarea.selectionStart})
}
},
store: () => store, store: () => store,
computed: { computed: {
rawComposeText: ($rawComposeText) => $rawComposeText, rawComposeText: ($rawComposeText) => $rawComposeText,
currentComposeText: ($currentComposeText) => $currentComposeText, currentComposeText: ($currentComposeText) => $currentComposeText
emojiToInsert: ($emojiToInsert) => $emojiToInsert
} }
} }
</script> </script>

View file

@ -34,15 +34,13 @@
store: () => store, store: () => store,
computed: { computed: {
inputLengthToDisplay: ($rawComposeTextLength) => { inputLengthToDisplay: ($rawComposeTextLength) => {
return ($rawComposeTextLength <= CHAR_LIMIT return CHAR_LIMIT - $rawComposeTextLength
? $rawComposeTextLength
: CHAR_LIMIT - $rawComposeTextLength)
}, },
inputLengthLabel: ($rawComposeTextOverLimit, inputLengthToDisplay) => { inputLengthLabel: ($rawComposeTextOverLimit, inputLengthToDisplay) => {
if ($rawComposeTextOverLimit) { if ($rawComposeTextOverLimit) {
return `${inputLengthToDisplay} characters over limit` return `${inputLengthToDisplay} characters over limit`
} else { } else {
return `${inputLengthToDisplay} characters` return `${inputLengthToDisplay} characters remaining`
} }
} }
} }

View file

@ -32,7 +32,7 @@
.custom-emoji-list { .custom-emoji-list {
list-style: none; list-style: none;
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(48px, 1fr)); grid-template-columns: repeat(auto-fill, minmax(48px, 1fr));
grid-gap: 5px; grid-gap: 5px;
padding: 20px 10px; padding: 20px 10px;
} }

View file

@ -1,34 +0,0 @@
import {
composeButton, composeInput, composeLengthIndicator, getUrl, homeNavButton, notificationsNavButton
} from '../utils'
import { foobarRole } from '../roles'
import times from 'lodash/times'
fixture`12-compose-limits.js`
.page`http://localhost:4002`
test('shows compose limits', async t => {
await t.useRole(foobarRole)
.hover(composeInput)
.expect(composeLengthIndicator.innerText).eql('0')
.expect(composeButton.getAttribute('disabled')).eql('')
.typeText(composeInput, 'typing some text')
.expect(composeLengthIndicator.innerText).eql('16')
.expect(composeButton.hasAttribute('disabled')).notOk()
.typeText(composeInput, times(50, () => 'hello world').join(' '), {replace: true, paste: true})
.expect(composeLengthIndicator.innerText).eql('-99')
.expect(composeButton.getAttribute('disabled')).eql('')
.typeText(composeInput, 'hello world', {replace: true})
.click(notificationsNavButton)
.expect(getUrl()).contains('/notifications')
.click(homeNavButton)
.expect(getUrl()).eql('http://localhost:4002/')
.expect(composeInput.value).eql('hello world')
.expect(composeLengthIndicator.innerText).eql('11')
.expect(composeButton.hasAttribute('disabled')).notOk()
.selectText(composeInput)
.pressKey('delete')
.expect(composeInput.value).eql('')
.expect(composeLengthIndicator.innerText).eql('0')
.expect(composeButton.getAttribute('disabled')).eql('')
})

83
tests/spec/12-compose.js Normal file
View file

@ -0,0 +1,83 @@
import { Selector as $ } from 'testcafe'
import {
composeButton, composeInput, composeLengthIndicator, emojiButton, getComposeSelectionStart, getUrl,
homeNavButton,
notificationsNavButton
} from '../utils'
import { foobarRole } from '../roles'
import times from 'lodash/times'
fixture`12-compose.js`
.page`http://localhost:4002`
test('shows compose limits', async t => {
await t.useRole(foobarRole)
.hover(composeInput)
.expect(composeLengthIndicator.innerText).eql('500')
.expect(composeButton.getAttribute('disabled')).eql('')
.typeText(composeInput, 'typing some text')
.expect(composeLengthIndicator.innerText).eql('484')
.expect(composeButton.hasAttribute('disabled')).notOk()
.typeText(composeInput, times(50, () => 'hello world').join(' '), {replace: true, paste: true})
.expect(composeLengthIndicator.innerText).eql('-99')
.expect(composeButton.getAttribute('disabled')).eql('')
.typeText(composeInput, 'hello world', {replace: true})
.click(notificationsNavButton)
.expect(getUrl()).contains('/notifications')
.click(homeNavButton)
.expect(getUrl()).eql('http://localhost:4002/')
.expect(composeInput.value).eql('hello world')
.expect(composeLengthIndicator.innerText).eql('489')
.expect(composeButton.hasAttribute('disabled')).notOk()
.selectText(composeInput)
.pressKey('delete')
.expect(composeInput.value).eql('')
.expect(composeLengthIndicator.innerText).eql('500')
.expect(composeButton.getAttribute('disabled')).eql('')
})
test('shows compose limits for URLs/handles', async t => {
await t.useRole(foobarRole)
.expect(composeLengthIndicator.innerText).eql('500')
.expect(composeButton.getAttribute('disabled')).eql('')
.typeText(composeInput, 'hello world ' +
'http://foo.bar.baz.whatever.example.com/hello ' +
'@reallylongnamethatstretchesonandon@foo.example.com', {paste: true})
.expect(composeLengthIndicator.innerText).eql('429')
.expect(composeButton.hasAttribute('disabled')).notOk()
})
test('shows compose limits for emoji', async t => {
await t.useRole(foobarRole)
.typeText(composeInput, 'hello world \ud83c\ude01 \ud83d\udc6a')
.expect(composeLengthIndicator.innerText).eql('485')
.expect(composeButton.hasAttribute('disabled')).notOk()
})
test('shows compose limits for custom emoji', async t => {
await t.useRole(foobarRole)
.typeText(composeInput, 'hello world ')
.click(emojiButton)
.click($('button img[title=":blobnom:"]'))
.expect(composeInput.value).eql('hello world :blobnom: ')
.expect(composeLengthIndicator.innerText).eql('478')
})
test('inserts custom emoji correctly', async t => {
await t.useRole(foobarRole)
.typeText(composeInput, 'hello world')
.selectText(composeInput, 6, 6)
.expect(getComposeSelectionStart()).eql(6)
.click(emojiButton)
.click($('button img[title=":blobpats:"]'))
.expect(composeInput.value).eql('hello :blobpats: world')
.selectText(composeInput, 0, 0)
.expect(getComposeSelectionStart()).eql(0)
.click(emojiButton)
.click($('button img[title=":blobnom:"]'))
.expect(composeInput.value).eql(':blobnom: hello :blobpats: world')
.typeText(composeInput, ' foobar ')
.click(emojiButton)
.click($('button img[title=":blobpeek:"]'))
.expect(composeInput.value).eql(':blobnom: hello :blobpats: world foobar :blobpeek: ')
})

View file

@ -13,6 +13,7 @@ export const formError = $('.form-error-user-error')
export const composeInput = $('.compose-box-input') export const composeInput = $('.compose-box-input')
export const composeButton = $('.compose-box-button') 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 favoritesCountElement = $('.status-favs-reblogs:nth-child(3)').addCustomDOMProperties({ export const favoritesCountElement = $('.status-favs-reblogs:nth-child(3)').addCustomDOMProperties({
innerCount: el => parseInt(el.innerText, 10) innerCount: el => parseInt(el.innerText, 10)
@ -30,6 +31,10 @@ export const getActiveElementClass = exec(() =>
export const goBack = exec(() => window.history.back()) export const goBack = exec(() => window.history.back())
export const getComposeSelectionStart = exec(() => composeInput().selectionStart, {
dependencies: { composeInput }
})
export function getNthStatus (n) { export function getNthStatus (n) {
return $(`div[aria-hidden="false"] > article[aria-posinset="${n}"]`) return $(`div[aria-hidden="false"] > article[aria-posinset="${n}"]`)
} }