Merge remote-tracking branch 'upstream/develop' into develop

This commit is contained in:
FloatingGhost 2022-06-08 18:09:16 +01:00
commit f6cf509a04
229 changed files with 9798 additions and 5400 deletions

View file

@ -1,45 +1,40 @@
import Vuex from 'vuex'
import routes from 'src/boot/routes'
import { createLocalVue } from '@vue/test-utils'
import VueRouter from 'vue-router'
import { createRouter, createMemoryHistory } from 'vue-router'
import { createStore } from 'vuex'
const localVue = createLocalVue()
localVue.use(Vuex)
localVue.use(VueRouter)
const store = new Vuex.Store({
const store = createStore({
state: {
instance: {}
}
})
describe('routes', () => {
const router = new VueRouter({
mode: 'abstract',
const router = createRouter({
history: createMemoryHistory(),
routes: routes(store)
})
it('root path', () => {
router.push('/main/all')
it('root path', async () => {
await router.push('/main/all')
const matchedComponents = router.getMatchedComponents()
const matchedComponents = router.currentRoute.value.matched
expect(matchedComponents[0].components.hasOwnProperty('Timeline')).to.eql(true)
expect(matchedComponents[0].components.default.components.hasOwnProperty('Timeline')).to.eql(true)
})
it('user\'s profile', () => {
router.push('/fake-user-name')
it('user\'s profile', async () => {
await router.push('/fake-user-name')
const matchedComponents = router.getMatchedComponents()
const matchedComponents = router.currentRoute.value.matched
expect(matchedComponents[0].components.hasOwnProperty('UserCard')).to.eql(true)
expect(matchedComponents[0].components.default.components.hasOwnProperty('UserCard')).to.eql(true)
})
it('user\'s profile at /users', () => {
router.push('/users/fake-user-name')
it('user\'s profile at /users', async () => {
await router.push('/users/fake-user-name')
const matchedComponents = router.getMatchedComponents()
const matchedComponents = router.currentRoute.value.matched
expect(matchedComponents[0].components.hasOwnProperty('UserCard')).to.eql(true)
expect(matchedComponents[0].components.default.components.hasOwnProperty('UserCard')).to.eql(true)
})
})

View file

@ -1,108 +1,116 @@
import { shallowMount, createLocalVue } from '@vue/test-utils'
import { h } from 'vue'
import { shallowMount } from '@vue/test-utils'
import EmojiInput from 'src/components/emoji_input/emoji_input.vue'
import vClickOutside from 'click-outside-vue3'
const generateInput = (value, padEmoji = true) => {
const localVue = createLocalVue()
localVue.directive('click-outside', () => {})
const wrapper = shallowMount(EmojiInput, {
propsData: {
suggest: () => [],
enableEmojiPicker: true,
value
},
mocks: {
$store: {
getters: {
mergedConfig: {
padEmoji
global: {
renderStubDefaultSlot: true,
mocks: {
$store: {
getters: {
mergedConfig: {
padEmoji
}
}
}
},
stubs: {
FAIcon: true
},
directives: {
'click-outside': vClickOutside
}
},
slots: {
default: '<input />'
props: {
suggest: () => [],
enableEmojiPicker: true,
modelValue: value
},
localVue
slots: {
'default': () => h('input', '')
}
})
return [wrapper, localVue]
return wrapper
}
describe('EmojiInput', () => {
describe('insertion mechanism', () => {
it('inserts string at the end with trailing space', () => {
const initialString = 'Testing'
const [wrapper] = generateInput(initialString)
const wrapper = generateInput(initialString)
const input = wrapper.find('input')
input.setValue(initialString)
wrapper.setData({ caret: initialString.length })
wrapper.vm.insert({ insertion: '(test)', keepOpen: false })
const inputEvents = wrapper.emitted().input
const inputEvents = wrapper.emitted()['update:modelValue']
expect(inputEvents[inputEvents.length - 1][0]).to.eql('Testing (test) ')
})
it('inserts string at the end with trailing space (source has a trailing space)', () => {
const initialString = 'Testing '
const [wrapper] = generateInput(initialString)
const wrapper = generateInput(initialString)
const input = wrapper.find('input')
input.setValue(initialString)
wrapper.setData({ caret: initialString.length })
wrapper.vm.insert({ insertion: '(test)', keepOpen: false })
const inputEvents = wrapper.emitted().input
const inputEvents = wrapper.emitted()['update:modelValue']
expect(inputEvents[inputEvents.length - 1][0]).to.eql('Testing (test) ')
})
it('inserts string at the begginning without leading space', () => {
const initialString = 'Testing'
const [wrapper] = generateInput(initialString)
const wrapper = generateInput(initialString)
const input = wrapper.find('input')
input.setValue(initialString)
wrapper.setData({ caret: 0 })
wrapper.vm.insert({ insertion: '(test)', keepOpen: false })
const inputEvents = wrapper.emitted().input
const inputEvents = wrapper.emitted()['update:modelValue']
expect(inputEvents[inputEvents.length - 1][0]).to.eql('(test) Testing')
})
it('inserts string between words without creating extra spaces', () => {
const initialString = 'Spurdo Sparde'
const [wrapper] = generateInput(initialString)
const wrapper = generateInput(initialString)
const input = wrapper.find('input')
input.setValue(initialString)
wrapper.setData({ caret: 6 })
wrapper.vm.insert({ insertion: ':ebin:', keepOpen: false })
const inputEvents = wrapper.emitted().input
const inputEvents = wrapper.emitted()['update:modelValue']
expect(inputEvents[inputEvents.length - 1][0]).to.eql('Spurdo :ebin: Sparde')
})
it('inserts string between words without creating extra spaces (other caret)', () => {
const initialString = 'Spurdo Sparde'
const [wrapper] = generateInput(initialString)
const wrapper = generateInput(initialString)
const input = wrapper.find('input')
input.setValue(initialString)
wrapper.setData({ caret: 7 })
wrapper.vm.insert({ insertion: ':ebin:', keepOpen: false })
const inputEvents = wrapper.emitted().input
const inputEvents = wrapper.emitted()['update:modelValue']
expect(inputEvents[inputEvents.length - 1][0]).to.eql('Spurdo :ebin: Sparde')
})
it('inserts string without any padding if padEmoji setting is set to false', () => {
const initialString = 'Eat some spam!'
const [wrapper] = generateInput(initialString, false)
const wrapper = generateInput(initialString, false)
const input = wrapper.find('input')
input.setValue(initialString)
wrapper.setData({ caret: initialString.length, keepOpen: false })
wrapper.vm.insert({ insertion: ':spam:' })
const inputEvents = wrapper.emitted().input
const inputEvents = wrapper.emitted()['update:modelValue']
expect(inputEvents[inputEvents.length - 1][0]).to.eql('Eat some spam!:spam:')
})
it('correctly sets caret after insertion at beginning', (done) => {
const initialString = '1234'
const [wrapper, vue] = generateInput(initialString)
const wrapper = generateInput(initialString)
const input = wrapper.find('input')
input.setValue(initialString)
wrapper.setData({ caret: 0 })
wrapper.vm.insert({ insertion: '1234', keepOpen: false })
vue.nextTick(() => {
wrapper.vm.$nextTick(() => {
expect(wrapper.vm.caret).to.eql(5)
done()
})
@ -110,12 +118,12 @@ describe('EmojiInput', () => {
it('correctly sets caret after insertion at end', (done) => {
const initialString = '1234'
const [wrapper, vue] = generateInput(initialString)
const wrapper = generateInput(initialString)
const input = wrapper.find('input')
input.setValue(initialString)
wrapper.setData({ caret: initialString.length })
wrapper.vm.insert({ insertion: '1234', keepOpen: false })
vue.nextTick(() => {
wrapper.vm.$nextTick(() => {
expect(wrapper.vm.caret).to.eql(10)
done()
})
@ -123,12 +131,12 @@ describe('EmojiInput', () => {
it('correctly sets caret after insertion if padEmoji setting is set to false', (done) => {
const initialString = '1234'
const [wrapper, vue] = generateInput(initialString, false)
const wrapper = generateInput(initialString, false)
const input = wrapper.find('input')
input.setValue(initialString)
wrapper.setData({ caret: initialString.length })
wrapper.vm.insert({ insertion: '1234', keepOpen: false })
vue.nextTick(() => {
wrapper.vm.$nextTick(() => {
expect(wrapper.vm.caret).to.eql(8)
done()
})

View file

@ -1,8 +1,15 @@
import { mount, shallowMount, createLocalVue } from '@vue/test-utils'
import { mount, shallowMount } from '@vue/test-utils'
import RichContent from 'src/components/rich_content/rich_content.jsx'
const localVue = createLocalVue()
const attentions = []
const global = {
mocks: {
'$store': null
},
stubs: {
FAIcon: true
}
}
const makeMention = (who) => {
attentions.push({ statusnet_profile_url: `https://fake.tld/@${who}` })
@ -11,17 +18,17 @@ const makeMention = (who) => {
const p = (...data) => `<p>${data.join('')}</p>`
const compwrap = (...data) => `<span class="RichContent">${data.join('')}</span>`
const mentionsLine = (times) => [
'<mentionsline-stub mentions="',
'<mentions-line-stub mentions="',
new Array(times).fill('[object Object]').join(','),
'"></mentionsline-stub>'
'"></mentions-line-stub>'
].join('')
describe('RichContent', () => {
it('renders simple post without exploding', () => {
const html = p('Hello world!')
const wrapper = shallowMount(RichContent, {
localVue,
propsData: {
global,
props: {
attentions,
handleLinks: true,
greentext: true,
@ -30,7 +37,7 @@ describe('RichContent', () => {
}
})
expect(wrapper.html()).to.eql(compwrap(html))
expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(html))
})
it('unescapes everything as needed', () => {
@ -45,8 +52,8 @@ describe('RichContent', () => {
'<a href="http://example.com?a=1&b=2">http://example.com?a=1&b=2</a>'
].join('')
const wrapper = shallowMount(RichContent, {
localVue,
propsData: {
global,
props: {
attentions,
handleLinks: true,
greentext: true,
@ -55,7 +62,7 @@ describe('RichContent', () => {
}
})
expect(wrapper.html()).to.eql(compwrap(expected))
expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(expected))
})
it('replaces mention with mentionsline', () => {
@ -64,8 +71,8 @@ describe('RichContent', () => {
' how are you doing today?'
)
const wrapper = shallowMount(RichContent, {
localVue,
propsData: {
global,
props: {
attentions,
handleLinks: true,
greentext: true,
@ -74,7 +81,7 @@ describe('RichContent', () => {
}
})
expect(wrapper.html()).to.eql(compwrap(p(
expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(p(
mentionsLine(1),
' how are you doing today?'
)))
@ -95,17 +102,17 @@ describe('RichContent', () => {
),
// TODO fix this extra line somehow?
p(
'<mentionsline-stub mentions="',
'<mentions-line-stub mentions="',
'[object Object],',
'[object Object],',
'[object Object]',
'"></mentionsline-stub>'
'"></mentions-line-stub>'
)
].join('')
const wrapper = shallowMount(RichContent, {
localVue,
propsData: {
global,
props: {
attentions,
handleLinks: true,
greentext: true,
@ -114,7 +121,7 @@ describe('RichContent', () => {
}
})
expect(wrapper.html()).to.eql(compwrap(expected))
expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(expected))
})
it('Does not touch links if link handling is disabled', () => {
@ -132,8 +139,8 @@ describe('RichContent', () => {
].join('\n')
const wrapper = shallowMount(RichContent, {
localVue,
propsData: {
global,
props: {
attentions,
handleLinks: false,
greentext: true,
@ -156,8 +163,8 @@ describe('RichContent', () => {
].join('\n')
const wrapper = shallowMount(RichContent, {
localVue,
propsData: {
global,
props: {
attentions,
handleLinks: false,
greentext: true,
@ -176,8 +183,8 @@ describe('RichContent', () => {
].join('\n')
const wrapper = shallowMount(RichContent, {
localVue,
propsData: {
global,
props: {
attentions,
handleLinks: false,
greentext: false,
@ -193,12 +200,12 @@ describe('RichContent', () => {
const html = p('Ebin :DDDD :spurdo:')
const expected = p(
'Ebin :DDDD ',
'<anonymous-stub alt=":spurdo:" src="about:blank" title=":spurdo:" class="emoji img"></anonymous-stub>'
'<anonymous-stub src="about:blank" alt=":spurdo:" class="emoji img" title=":spurdo:"></anonymous-stub>'
)
const wrapper = shallowMount(RichContent, {
localVue,
propsData: {
global,
props: {
attentions,
handleLinks: false,
greentext: false,
@ -207,15 +214,15 @@ describe('RichContent', () => {
}
})
expect(wrapper.html()).to.eql(compwrap(expected))
expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(expected))
})
it('Doesn\'t add nonexistent emoji to post', () => {
const html = p('Lol :lol:')
const wrapper = shallowMount(RichContent, {
localVue,
propsData: {
global,
props: {
attentions,
handleLinks: false,
greentext: false,
@ -224,7 +231,7 @@ describe('RichContent', () => {
}
})
expect(wrapper.html()).to.eql(compwrap(html))
expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(html))
})
it('Greentext + last mentions', () => {
@ -242,8 +249,8 @@ describe('RichContent', () => {
].join('\n')
const wrapper = shallowMount(RichContent, {
localVue,
propsData: {
global,
props: {
attentions,
handleLinks: true,
greentext: true,
@ -274,8 +281,8 @@ describe('RichContent', () => {
].join('<br>')
const wrapper = shallowMount(RichContent, {
localVue,
propsData: {
global,
props: {
attentions,
handleLinks: true,
greentext: true,
@ -284,7 +291,7 @@ describe('RichContent', () => {
}
})
expect(wrapper.html()).to.eql(compwrap(expected))
expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(expected))
})
it('buggy example/hashtags', () => {
@ -302,16 +309,16 @@ describe('RichContent', () => {
'<p>',
'<a href="http://macrochan.org/images/N/H/NHCMDUXJPPZ6M3Z2CQ6D2EBRSWGE7MZY.jpg" target="_blank">',
'NHCMDUXJPPZ6M3Z2CQ6D2EBRSWGE7MZY.jpg</a>',
' <hashtaglink-stub url="https://shitposter.club/tag/nou" content="#nou" tag="nou">',
'</hashtaglink-stub>',
' <hashtaglink-stub url="https://shitposter.club/tag/screencap" content="#screencap" tag="screencap">',
'</hashtaglink-stub>',
' <hashtag-link-stub url="https://shitposter.club/tag/nou" content="#nou" tag="nou">',
'</hashtag-link-stub>',
' <hashtag-link-stub url="https://shitposter.club/tag/screencap" content="#screencap" tag="screencap">',
'</hashtag-link-stub>',
' </p>'
].join('')
const wrapper = shallowMount(RichContent, {
localVue,
propsData: {
global,
props: {
attentions,
handleLinks: true,
greentext: true,
@ -320,7 +327,7 @@ describe('RichContent', () => {
}
})
expect(wrapper.html()).to.eql(compwrap(expected))
expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(expected))
})
it('rich contents of a mention are handled properly', () => {
@ -344,7 +351,8 @@ describe('RichContent', () => {
p(
'<span class="MentionsLine">',
'<span class="MentionLink mention-link">',
'<a href="lol" target="_blank" class="original">',
'<!-- eslint-disable vue/no-v-html -->',
'<a href="lol" class="original" target="_blank">',
'<span>',
'https://</span>',
'<span>',
@ -352,10 +360,10 @@ describe('RichContent', () => {
'<span>',
'</span>',
'</a>',
' ',
'<!---->', // v-if placeholder, mentionlink's "new" (i.e. rich) display
'<!-- eslint-enable vue/no-v-html -->',
'<!--v-if-->', // v-if placeholder, mentionlink's "new" (i.e. rich) display
'</span>',
'<!---->', // v-if placeholder, mentionsline's extra mentions and stuff
'<!--v-if-->', // v-if placeholder, mentionsline's extra mentions and stuff
'</span>'
),
p(
@ -364,8 +372,8 @@ describe('RichContent', () => {
].join('')
const wrapper = mount(RichContent, {
localVue,
propsData: {
global,
props: {
attentions,
handleLinks: true,
greentext: true,
@ -374,7 +382,82 @@ describe('RichContent', () => {
}
})
expect(wrapper.html()).to.eql(compwrap(expected))
expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(expected))
})
it('rich contents of nested mentions are handled properly', () => {
attentions.push({ statusnet_profile_url: 'lol' })
const html = [
'<span class="poast-style">',
'<a href="lol" class="mention">',
'<span>',
'https://</span>',
'<span>',
'lol.tld/</span>',
'<span>',
'</span>',
'</a>',
' ',
'<a href="lol" class="mention">',
'<span>',
'https://</span>',
'<span>',
'lol.tld/</span>',
'<span>',
'</span>',
'</a>',
' ',
'</span>',
'Testing'
].join('')
const expected = [
'<span class="poast-style">',
'<span class="MentionsLine">',
'<span class="MentionLink mention-link">',
'<!-- eslint-disable vue/no-v-html -->',
'<a href="lol" class="original" target="_blank">',
'<span>',
'https://</span>',
'<span>',
'lol.tld/</span>',
'<span>',
'</span>',
'</a>',
'<!-- eslint-enable vue/no-v-html -->',
'<!--v-if-->', // v-if placeholder, mentionlink's "new" (i.e. rich) display
'</span>',
'<span class="MentionLink mention-link">',
'<!-- eslint-disable vue/no-v-html -->',
'<a href="lol" class="original" target="_blank">',
'<span>',
'https://</span>',
'<span>',
'lol.tld/</span>',
'<span>',
'</span>',
'</a>',
'<!-- eslint-enable vue/no-v-html -->',
'<!--v-if-->', // v-if placeholder, mentionlink's "new" (i.e. rich) display
'</span>',
'<!--v-if-->', // v-if placeholder, mentionsline's extra mentions and stuff
'</span>',
' ',
'</span>',
'Testing'
].join('')
const wrapper = mount(RichContent, {
global,
props: {
attentions,
handleLinks: true,
greentext: true,
emoji: [],
html
}
})
expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(expected))
})
it('rich contents of a link are handled properly', () => {
@ -408,8 +491,8 @@ describe('RichContent', () => {
].join('')
const wrapper = shallowMount(RichContent, {
localVue,
propsData: {
global,
props: {
attentions,
handleLinks: true,
greentext: true,
@ -418,7 +501,7 @@ describe('RichContent', () => {
}
})
expect(wrapper.html()).to.eql(compwrap(expected))
expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(expected))
})
it.skip('[INFORMATIVE] Performance testing, 10 000 simple posts', () => {
@ -455,8 +538,8 @@ describe('RichContent', () => {
const t0 = performance.now()
const wrapper = mount(TestComponent, {
localVue,
propsData: {
global,
props: {
attentions,
handleLinks,
vhtml

View file

@ -1,27 +0,0 @@
import { getExcludedStatusIdsByPinning } from 'src/components/timeline/timeline.js'
describe('Timeline', () => {
describe('getExcludedStatusIdsByPinning', () => {
const mockStatuses = (ids) => ids.map(id => ({ id }))
it('should return only members of both pinnedStatusIds and ids of the given statuses', () => {
const statusIds = [1, 2, 3, 4]
const statuses = mockStatuses(statusIds)
const pinnedStatusIds = [1, 3, 5]
const result = getExcludedStatusIdsByPinning(statuses, pinnedStatusIds)
result.forEach(item => {
expect(item).to.be.oneOf(statusIds)
expect(item).to.be.oneOf(pinnedStatusIds)
})
})
it('should return ids of pinned statuses not posted before any unpinned status', () => {
const pinnedStatusIdSet1 = ['PINNED1', 'PINNED2']
const pinnedStatusIdSet2 = ['PINNED3', 'PINNED4']
const pinnedStatusIds = [...pinnedStatusIdSet1, ...pinnedStatusIdSet2]
const statusIds = [...pinnedStatusIdSet1, 'UNPINNED1', ...pinnedStatusIdSet2]
const statuses = mockStatuses(statusIds)
expect(getExcludedStatusIdsByPinning(statuses, pinnedStatusIds)).to.eql(pinnedStatusIdSet1)
})
})
})

View file

@ -1,12 +1,9 @@
import { mount, createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
import { mount } from '@vue/test-utils'
import { createStore } from 'vuex'
import UserProfile from 'src/components/user_profile/user_profile.vue'
import backendInteractorService from 'src/services/backend_interactor_service/backend_interactor_service.js'
import { getters } from 'src/modules/users.js'
const localVue = createLocalVue()
localVue.use(Vuex)
const mutations = {
clearTimeline: () => {}
}
@ -42,7 +39,7 @@ const extUser = {
screen_name_ui: 'testUser@test.instance'
}
const externalProfileStore = new Vuex.Store({
const externalProfileStore = createStore({
mutations,
actions,
getters: testGetters,
@ -104,7 +101,7 @@ const externalProfileStore = new Vuex.Store({
}
})
const localProfileStore = new Vuex.Store({
const localProfileStore = createStore({
mutations,
actions,
getters: testGetters,
@ -173,17 +170,19 @@ const localProfileStore = new Vuex.Store({
}
})
describe('UserProfile', () => {
// https://github.com/vuejs/test-utils/issues/1382
describe.skip('UserProfile', () => {
it('renders external profile', () => {
const wrapper = mount(UserProfile, {
localVue,
store: externalProfileStore,
mocks: {
$route: {
params: { id: 100 },
name: 'external-user-profile'
},
$t: (msg) => msg
global: {
plugins: [ externalProfileStore ],
mocks: {
$route: {
params: { id: 100 },
name: 'external-user-profile'
},
$t: (msg) => msg
}
}
})
@ -192,14 +191,15 @@ describe('UserProfile', () => {
it('renders local profile', () => {
const wrapper = mount(UserProfile, {
localVue,
store: localProfileStore,
mocks: {
$route: {
params: { name: 'testUser' },
name: 'user-profile'
},
$t: (msg) => msg
global: {
plugins: [ localProfileStore ],
mocks: {
$route: {
params: { name: 'testUser' },
name: 'user-profile'
},
$t: (msg) => msg
}
}
})