add emoji autosuggest
This commit is contained in:
parent
143d80c94e
commit
7ce9a39119
|
@ -25,3 +25,21 @@ export function insertEmoji (realm, emoji) {
|
||||||
let newText = `${pre}:${emoji.shortcode}: ${post}`
|
let newText = `${pre}:${emoji.shortcode}: ${post}`
|
||||||
store.setComposeData(realm, {text: newText})
|
store.setComposeData(realm, {text: newText})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function insertEmojiAtPosition (realm, emoji, startIndex, endIndex) {
|
||||||
|
let oldText = store.getComposeData(realm, 'text')
|
||||||
|
let pre = oldText ? substring(oldText, 0, startIndex) : ''
|
||||||
|
let post = oldText ? substring(oldText, endIndex) : ''
|
||||||
|
let newText = `${pre}:${emoji.shortcode}: ${post}`
|
||||||
|
store.setComposeData(realm, {text: newText})
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function clickSelectedAutosuggestionEmoji (realm) {
|
||||||
|
let selectionStart = store.get('composeSelectionStart')
|
||||||
|
let searchText = store.get('composeAutosuggestionSearchText')
|
||||||
|
let selection = store.get('composeAutosuggestionSelected') || 0
|
||||||
|
let emoji = store.get('composeAutosuggestionSearchResults')[selection]
|
||||||
|
let startIndex = selectionStart - searchText.length
|
||||||
|
let endIndex = selectionStart
|
||||||
|
await insertEmojiAtPosition(realm, emoji, startIndex, endIndex)
|
||||||
|
}
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
aria-hidden="true" >
|
aria-hidden="true" >
|
||||||
<ComposeAutosuggestionList
|
<ComposeAutosuggestionList
|
||||||
items="{{searchResults}}"
|
items="{{searchResults}}"
|
||||||
on:click="onUserSelected(event)"
|
on:click="onClick(event)"
|
||||||
|
:type
|
||||||
:selected
|
:selected
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -32,6 +33,7 @@
|
||||||
import { store } from '../../_store/store'
|
import { store } from '../../_store/store'
|
||||||
import { database } from '../../_database/database'
|
import { database } from '../../_database/database'
|
||||||
import { insertUsername } from '../../_actions/compose'
|
import { insertUsername } from '../../_actions/compose'
|
||||||
|
import { insertEmojiAtPosition } from '../../_actions/emoji'
|
||||||
import { scheduleIdleTask } from '../../_utils/scheduleIdleTask'
|
import { scheduleIdleTask } from '../../_utils/scheduleIdleTask'
|
||||||
import { once } from '../../_utils/once'
|
import { once } from '../../_utils/once'
|
||||||
import ComposeAutosuggestionList from './ComposeAutosuggestionList.html'
|
import ComposeAutosuggestionList from './ComposeAutosuggestionList.html'
|
||||||
|
@ -39,7 +41,8 @@
|
||||||
const SEARCH_RESULTS_LIMIT = 4
|
const SEARCH_RESULTS_LIMIT = 4
|
||||||
const DATABASE_SEARCH_RESULTS_LIMIT = 30
|
const DATABASE_SEARCH_RESULTS_LIMIT = 30
|
||||||
const MIN_PREFIX_LENGTH = 1
|
const MIN_PREFIX_LENGTH = 1
|
||||||
const SEARCH_REGEX = new RegExp(`(?:\\s|^)(@\\S{${MIN_PREFIX_LENGTH},})$`)
|
const ACCOUNT_SEARCH_REGEX = new RegExp(`(?:\\s|^)(@\\S{${MIN_PREFIX_LENGTH},})$`)
|
||||||
|
const EMOJI_SEARCH_REGEX = new RegExp(`(?:\\s|^)(:[^:]{${MIN_PREFIX_LENGTH},})$`)
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
oncreate() {
|
oncreate() {
|
||||||
|
@ -63,7 +66,7 @@
|
||||||
} else {
|
} else {
|
||||||
Promise.race([
|
Promise.race([
|
||||||
new Promise(resolve => setTimeout(resolve, 200)),
|
new Promise(resolve => setTimeout(resolve, 200)),
|
||||||
new Promise(resolve => this.once('userSelected', resolve))
|
new Promise(resolve => this.once('autosuggestItemSelected', resolve))
|
||||||
]).then(updateFocusedState)
|
]).then(updateFocusedState)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -71,11 +74,15 @@
|
||||||
if (!searchText) {
|
if (!searchText) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let results = await this.search(searchText)
|
let type = searchText.startsWith('@') ? 'account' : 'emoji'
|
||||||
|
let results = (type === 'account')
|
||||||
|
? await this.searchAccounts(searchText)
|
||||||
|
: await this.searchEmoji(searchText)
|
||||||
this.store.set({
|
this.store.set({
|
||||||
composeAutosuggestionSelected: 0,
|
composeAutosuggestionSelected: 0,
|
||||||
composeAutosuggestionSearchText: searchText,
|
composeAutosuggestionSearchText: searchText,
|
||||||
composeAutosuggestionSearchResults: results
|
composeAutosuggestionSearchResults: results,
|
||||||
|
composeAutosuggestionType: type,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
this.observe('shown', shown => {
|
this.observe('shown', shown => {
|
||||||
|
@ -84,26 +91,41 @@
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
once: once,
|
once: once,
|
||||||
onUserSelected(account) {
|
onClick(item) {
|
||||||
this.fire('userSelected')
|
this.fire('autosuggestItemSelected')
|
||||||
let realm = this.get('realm')
|
let realm = this.get('realm')
|
||||||
let selectionStart = this.store.get('composeSelectionStart')
|
let selectionStart = this.store.get('composeSelectionStart')
|
||||||
let searchText = this.store.get('composeAutosuggestionSearchText')
|
let searchText = this.store.get('composeAutosuggestionSearchText')
|
||||||
let startIndex = selectionStart - searchText.length
|
let startIndex = selectionStart - searchText.length
|
||||||
let endIndex = selectionStart
|
let endIndex = selectionStart
|
||||||
/* no await */ insertUsername(realm, account.acct, startIndex, endIndex)
|
if (item.acct) {
|
||||||
|
/* no await */ insertUsername(realm, item.acct, startIndex, endIndex)
|
||||||
|
} else {
|
||||||
|
/* no await */ insertEmojiAtPosition(realm, item, startIndex, endIndex)
|
||||||
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
async search(searchText) {
|
async searchAccounts(searchText) {
|
||||||
|
searchText = searchText.substring(1)
|
||||||
let currentInstance = this.store.get('currentInstance')
|
let currentInstance = this.store.get('currentInstance')
|
||||||
let results = await database.searchAccountsByUsername(
|
let results = await database.searchAccountsByUsername(
|
||||||
currentInstance, searchText.substring(1), DATABASE_SEARCH_RESULTS_LIMIT)
|
currentInstance, searchText, DATABASE_SEARCH_RESULTS_LIMIT)
|
||||||
return results.slice(0, SEARCH_RESULTS_LIMIT)
|
return results.slice(0, SEARCH_RESULTS_LIMIT)
|
||||||
|
},
|
||||||
|
searchEmoji(searchText) {
|
||||||
|
searchText = searchText.toLowerCase().substring(1)
|
||||||
|
let customEmoji = this.store.get('currentCustomEmoji')
|
||||||
|
let results = customEmoji.filter(emoji => emoji.shortcode.toLowerCase().startsWith(searchText))
|
||||||
|
.sort((a, b) => a.shortcode.toLowerCase() < b.shortcode.toLowerCase() ? -1 : 1)
|
||||||
|
.slice(0, SEARCH_RESULTS_LIMIT)
|
||||||
|
return results
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
composeSelectionStart: ($composeSelectionStart) => $composeSelectionStart,
|
composeSelectionStart: ($composeSelectionStart) => $composeSelectionStart,
|
||||||
composeFocused: ($composeFocused) => $composeFocused,
|
composeFocused: ($composeFocused) => $composeFocused,
|
||||||
searchResults: ($composeAutosuggestionSearchResults) => $composeAutosuggestionSearchResults || [],
|
searchResults: ($composeAutosuggestionSearchResults) => $composeAutosuggestionSearchResults || [],
|
||||||
|
type: ($composeAutosuggestionType) => $composeAutosuggestionType || 'account',
|
||||||
selected: ($composeAutosuggestionSelected) => $composeAutosuggestionSelected || 0,
|
selected: ($composeAutosuggestionSelected) => $composeAutosuggestionSelected || 0,
|
||||||
searchText: (text, composeSelectionStartDeferred) => {
|
searchText: (text, composeSelectionStartDeferred) => {
|
||||||
let selectionStart = composeSelectionStartDeferred || 0
|
let selectionStart = composeSelectionStartDeferred || 0
|
||||||
|
@ -111,7 +133,8 @@
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let match = text.substring(0, selectionStart).match(SEARCH_REGEX)
|
let textUpToCursor = text.substring(0, selectionStart)
|
||||||
|
let match = textUpToCursor.match(ACCOUNT_SEARCH_REGEX) || textUpToCursor.match(EMOJI_SEARCH_REGEX)
|
||||||
return match && match[1]
|
return match && match[1]
|
||||||
},
|
},
|
||||||
shown: (composeFocusedDeferred, searchText, searchResults) => {
|
shown: (composeFocusedDeferred, searchText, searchResults) => {
|
||||||
|
|
|
@ -1,61 +1,76 @@
|
||||||
<ul class="generic-user-list">
|
<ul class="compose-autosuggest-list">
|
||||||
{{#each items as account, i @id}}
|
{{#each items as item, i}}
|
||||||
<li class="generic-user-list-item">
|
<li class="compose-autosuggest-list-item">
|
||||||
<button class="generic-user-list-button {{i === selected ? 'selected' : ''}}"
|
<button class="compose-autosuggest-list-button {{i === selected ? 'selected' : ''}}"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
on:click="fire('click', account)">
|
on:click="fire('click', item)">
|
||||||
<div class="generic-user-list-grid">
|
<div class="compose-autosuggest-list-grid">
|
||||||
|
{{#if type === 'account'}}
|
||||||
<Avatar
|
<Avatar
|
||||||
className="generic-user-list-item-avatar"
|
className="compose-autosuggest-list-item-avatar"
|
||||||
size="small"
|
size="small"
|
||||||
:account
|
account="{{item}}"
|
||||||
/>
|
/>
|
||||||
<span class="generic-user-list-display-name">
|
<span class="compose-autosuggest-list-display-name">
|
||||||
{{account.display_name || account.acct}}
|
{{item.display_name || item.acct}}
|
||||||
</span>
|
</span>
|
||||||
<span class="generic-user-list-username">
|
<span class="compose-autosuggest-list-username">
|
||||||
{{'@' + account.acct}}
|
{{'@' + item.acct}}
|
||||||
</span>
|
</span>
|
||||||
|
{{else}}
|
||||||
|
<img src="{{$autoplayGifs ? item.url : item.static_url}}"
|
||||||
|
class="compose-autosuggest-list-item-icon"
|
||||||
|
alt="{{':' + item.shortcode + ':'}}"
|
||||||
|
/>
|
||||||
|
<span class="compose-autosuggest-list-display-name">
|
||||||
|
{{':' + item.shortcode + ':'}}
|
||||||
|
</span>
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</ul>
|
</ul>
|
||||||
<style>
|
<style>
|
||||||
.generic-user-list {
|
.compose-autosuggest-list {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
border: 1px solid var(--compose-autosuggest-outline);
|
border: 1px solid var(--compose-autosuggest-outline);
|
||||||
}
|
}
|
||||||
.generic-user-list-item {
|
.compose-autosuggest-list-item {
|
||||||
border-bottom: 1px solid var(--compose-autosuggest-outline);
|
border-bottom: 1px solid var(--compose-autosuggest-outline);
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
.generic-user-list-item:last-child {
|
.compose-autosuggest-list-item:last-child {
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
.generic-user-list-button {
|
.compose-autosuggest-list-button {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
background: var(--settings-list-item-bg);
|
background: var(--settings-list-item-bg);
|
||||||
border: none;
|
border: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
.generic-user-list-grid {
|
.compose-autosuggest-list-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
grid-template-areas: "avatar display-name"
|
grid-template-areas: "icon display-name"
|
||||||
"avatar username";
|
"icon username";
|
||||||
grid-template-columns: min-content 1fr;
|
grid-template-columns: min-content 1fr;
|
||||||
grid-column-gap: 10px;
|
grid-column-gap: 10px;
|
||||||
grid-row-gap: 5px;
|
grid-row-gap: 5px;
|
||||||
}
|
}
|
||||||
:global(.generic-user-list-item-avatar) {
|
:global(.compose-autosuggest-list-item-avatar) {
|
||||||
grid-area: avatar;
|
grid-area: icon;
|
||||||
}
|
}
|
||||||
.generic-user-list-display-name {
|
.compose-autosuggest-list-item-icon {
|
||||||
|
grid-area: icon;
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
}
|
||||||
|
.compose-autosuggest-list-display-name {
|
||||||
grid-area: display-name;
|
grid-area: display-name;
|
||||||
font-size: 1.1em;
|
font-size: 1.1em;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
@ -64,7 +79,7 @@
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
.generic-user-list-username {
|
.compose-autosuggest-list-username {
|
||||||
grid-area: username;
|
grid-area: username;
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
color: var(--deemphasized-text-color);
|
color: var(--deemphasized-text-color);
|
||||||
|
@ -73,16 +88,19 @@
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
.generic-user-list-button:hover, .generic-user-list-button.selected {
|
.compose-autosuggest-list-button:hover, .compose-autosuggest-list-button.selected {
|
||||||
background: var(--compose-autosuggest-item-hover);
|
background: var(--compose-autosuggest-item-hover);
|
||||||
}
|
}
|
||||||
.generic-user-list-button:active {
|
.compose-autosuggest-list-button:active {
|
||||||
background: var(--compose-autosuggest-item-active);
|
background: var(--compose-autosuggest-item-active);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
import Avatar from '../Avatar.html'
|
import Avatar from '../Avatar.html'
|
||||||
|
import { store } from '../../_store/store'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
store: () => store,
|
||||||
components: {
|
components: {
|
||||||
Avatar
|
Avatar
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
import { mark, stop } from '../../_utils/marks'
|
import { mark, stop } from '../../_utils/marks'
|
||||||
import { selectionChange } from '../../_utils/events'
|
import { selectionChange } from '../../_utils/events'
|
||||||
import { clickSelectedAutosuggestionUsername } from '../../_actions/compose'
|
import { clickSelectedAutosuggestionUsername } from '../../_actions/compose'
|
||||||
|
import { clickSelectedAutosuggestionEmoji } from '../../_actions/emoji'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
oncreate() {
|
oncreate() {
|
||||||
|
@ -49,16 +50,19 @@
|
||||||
let textarea = this.refs.textarea
|
let textarea = this.refs.textarea
|
||||||
let firstTime = true
|
let firstTime = true
|
||||||
this.observe('text', text => {
|
this.observe('text', text => {
|
||||||
|
if (this.get('rawText') !== text) {
|
||||||
this.set({rawText: text})
|
this.set({rawText: text})
|
||||||
|
// this next autosize is required to resize after
|
||||||
|
// the user clicks the "toot" button
|
||||||
|
mark('autosize.update()')
|
||||||
|
autosize.update(textarea)
|
||||||
|
stop('autosize.update()')
|
||||||
|
}
|
||||||
if (firstTime) {
|
if (firstTime) {
|
||||||
firstTime = false
|
firstTime = false
|
||||||
if (this.get('autoFocus')) {
|
if (this.get('autoFocus')) {
|
||||||
textarea.focus()
|
textarea.focus()
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
mark('autosize.update()')
|
|
||||||
autosize.update(textarea)
|
|
||||||
stop('autosize.update()')
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -116,7 +120,12 @@
|
||||||
if (!autosuggestionShown) {
|
if (!autosuggestionShown) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
clickSelectedAutosuggestionUsername(this.get('realm'))
|
let type = this.store.get('composeAutosuggestionType')
|
||||||
|
if (type === 'account') {
|
||||||
|
/* no await */ clickSelectedAutosuggestionUsername(this.get('realm'))
|
||||||
|
} else { // emoji
|
||||||
|
/* no await */ clickSelectedAutosuggestionEmoji(this.get('realm'))
|
||||||
|
}
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
},
|
},
|
||||||
incrementAutosuggestSelected(increment, event) {
|
incrementAutosuggestSelected(increment, event) {
|
||||||
|
|
|
@ -54,7 +54,6 @@
|
||||||
<script>
|
<script>
|
||||||
import IconButton from '../IconButton.html'
|
import IconButton from '../IconButton.html'
|
||||||
import { store } from '../../_store/store'
|
import { store } from '../../_store/store'
|
||||||
import { updateCustomEmojiForInstance } from '../../_actions/emoji'
|
|
||||||
import { importDialogs } from '../../_utils/asyncModules'
|
import { importDialogs } from '../../_utils/asyncModules'
|
||||||
import { doMediaUpload } from '../../_actions/media'
|
import { doMediaUpload } from '../../_actions/media'
|
||||||
import { toggleContentWarningShown } from '../../_actions/contentWarnings'
|
import { toggleContentWarningShown } from '../../_actions/contentWarnings'
|
||||||
|
@ -79,7 +78,6 @@
|
||||||
store: () => store,
|
store: () => store,
|
||||||
methods: {
|
methods: {
|
||||||
async onEmojiClick() {
|
async onEmojiClick() {
|
||||||
/* no await */ updateCustomEmojiForInstance(this.store.get('currentInstance'))
|
|
||||||
let dialogs = await importDialogs()
|
let dialogs = await importDialogs()
|
||||||
dialogs.showEmojiDialog(this.get('realm'))
|
dialogs.showEmojiDialog(this.get('realm'))
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { updateInstanceInfo, updateVerifyCredentialsForInstance } from '../../_actions/instances'
|
import { updateInstanceInfo, updateVerifyCredentialsForInstance } from '../../_actions/instances'
|
||||||
import { updateLists } from '../../_actions/lists'
|
import { updateLists } from '../../_actions/lists'
|
||||||
import { createStream } from '../../_actions/streaming'
|
import { createStream } from '../../_actions/streaming'
|
||||||
|
import { updateCustomEmojiForInstance } from '../../_actions/emoji'
|
||||||
|
|
||||||
export function instanceObservers (store) {
|
export function instanceObservers (store) {
|
||||||
// stream to watch for home timeline updates and notifications
|
// stream to watch for home timeline updates and notifications
|
||||||
|
@ -22,6 +23,7 @@ export function instanceObservers (store) {
|
||||||
}
|
}
|
||||||
updateVerifyCredentialsForInstance(currentInstance)
|
updateVerifyCredentialsForInstance(currentInstance)
|
||||||
updateInstanceInfo(currentInstance)
|
updateInstanceInfo(currentInstance)
|
||||||
|
updateCustomEmojiForInstance(currentInstance)
|
||||||
updateLists()
|
updateLists()
|
||||||
|
|
||||||
await updateInstanceInfo(currentInstance)
|
await updateInstanceInfo(currentInstance)
|
||||||
|
|
|
@ -10,7 +10,7 @@ function login (t, username, password) {
|
||||||
.pressKey('enter')
|
.pressKey('enter')
|
||||||
.expect(getUrl()).contains('/oauth/authorize')
|
.expect(getUrl()).contains('/oauth/authorize')
|
||||||
.click(authorizeInput)
|
.click(authorizeInput)
|
||||||
.expect(getUrl()).eql('http://localhost:4002/')
|
.expect(getUrl()).eql('http://localhost:4002/', {timeout: 20000})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const foobarRole = Role('http://localhost:4002/settings/instances/add', async t => {
|
export const foobarRole = Role('http://localhost:4002/settings/instances/add', async t => {
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
import {
|
||||||
|
composeInput, getNthAutosuggestionResult
|
||||||
|
} from '../utils'
|
||||||
|
import { foobarRole } from '../roles'
|
||||||
|
|
||||||
|
fixture`018-compose-autosuggest.js`
|
||||||
|
.page`http://localhost:4002`
|
||||||
|
|
||||||
|
test('autosuggests user handles', async t => {
|
||||||
|
await t.useRole(foobarRole)
|
||||||
|
.hover(composeInput)
|
||||||
|
.typeText(composeInput, 'hey @qu')
|
||||||
|
.click(getNthAutosuggestionResult(1))
|
||||||
|
.expect(composeInput.value).eql('hey @quux ')
|
||||||
|
.typeText(composeInput, 'and also @adm')
|
||||||
|
.click(getNthAutosuggestionResult(1))
|
||||||
|
.expect(composeInput.value).eql('hey @quux and also @admin ')
|
||||||
|
.typeText(composeInput, 'and also @AdM')
|
||||||
|
.expect(getNthAutosuggestionResult(1).innerText).contains('@admin')
|
||||||
|
.pressKey('tab')
|
||||||
|
.expect(composeInput.value).eql('hey @quux and also @admin and also @admin ')
|
||||||
|
.typeText(composeInput, 'and @QU')
|
||||||
|
.expect(getNthAutosuggestionResult(1).innerText).contains('@quux')
|
||||||
|
.pressKey('enter')
|
||||||
|
.expect(composeInput.value).eql('hey @quux and also @admin and also @admin and @quux ')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('autosuggests custom emoji', async t => {
|
||||||
|
await t.useRole(foobarRole)
|
||||||
|
.hover(composeInput)
|
||||||
|
.typeText(composeInput, ':blob')
|
||||||
|
.click(getNthAutosuggestionResult(1))
|
||||||
|
.expect(composeInput.value).eql(':blobnom: ')
|
||||||
|
.typeText(composeInput, 'and :blob')
|
||||||
|
.expect(getNthAutosuggestionResult(1).innerText).contains(':blobnom:')
|
||||||
|
.expect(getNthAutosuggestionResult(2).innerText).contains(':blobpats:')
|
||||||
|
.expect(getNthAutosuggestionResult(3).innerText).contains(':blobpeek:')
|
||||||
|
.pressKey('down')
|
||||||
|
.pressKey('down')
|
||||||
|
.pressKey('enter')
|
||||||
|
.expect(composeInput.value).eql(':blobnom: and :blobpeek: ')
|
||||||
|
.typeText(composeInput, 'and also :blobpa')
|
||||||
|
.expect(getNthAutosuggestionResult(1).innerText).contains(':blobpats:')
|
||||||
|
.pressKey('tab')
|
||||||
|
.expect(composeInput.value).eql(':blobnom: and :blobpeek: and also :blobpats: ')
|
||||||
|
})
|
|
@ -95,6 +95,10 @@ export const uploadKittenImage = i => (exec(() => {
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
export function getNthAutosuggestionResult (n) {
|
||||||
|
return $(`.compose-autosuggest-list-item:nth-child(${n}) button`)
|
||||||
|
}
|
||||||
|
|
||||||
export function getNthSearchResult (n) {
|
export function getNthSearchResult (n) {
|
||||||
return $(`.search-result:nth-child(${n}) a`)
|
return $(`.search-result:nth-child(${n}) a`)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue