biome format --write

This commit is contained in:
Henry Jameson 2026-01-06 16:22:52 +02:00
commit 9262e803ec
415 changed files with 54076 additions and 17419 deletions

View file

@ -4,14 +4,14 @@ import { createStore } from 'vuex'
const store = createStore({
state: {
instance: {}
}
instance: {},
},
})
describe('routes', () => {
const router = createRouter({
history: createMemoryHistory(),
routes: routes(store)
routes: routes(store),
})
it('root path', async () => {
@ -20,25 +20,40 @@ describe('routes', () => {
const matchedComponents = router.currentRoute.value.matched
// eslint-disable-next-line no-prototype-builtins
expect(Object.hasOwn(matchedComponents[0].components.default.components, 'Timeline')).to.eql(true)
expect(
Object.hasOwn(
matchedComponents[0].components.default.components,
'Timeline',
),
).to.eql(true)
})
it('user\'s profile', async () => {
it("user's profile", async () => {
await router.push('/fake-user-name')
const matchedComponents = router.currentRoute.value.matched
// eslint-disable-next-line no-prototype-builtins
expect(Object.hasOwn(matchedComponents[0].components.default.components, 'UserCard')).to.eql(true)
expect(
Object.hasOwn(
matchedComponents[0].components.default.components,
'UserCard',
),
).to.eql(true)
})
it('user\'s profile at /users', async () => {
it("user's profile at /users", async () => {
await router.push('/users/fake-user-name')
const matchedComponents = router.currentRoute.value.matched
// eslint-disable-next-line no-prototype-builtins
expect(Object.hasOwn(matchedComponents[0].components.default.components, 'UserCard')).to.eql(true)
expect(
Object.hasOwn(
matchedComponents[0].components.default.components,
'UserCard',
),
).to.eql(true)
})
it('list view', async () => {
@ -46,7 +61,12 @@ describe('routes', () => {
const matchedComponents = router.currentRoute.value.matched
expect(Object.hasOwn(matchedComponents[0].components.default.components, 'ListsCard')).to.eql(true)
expect(
Object.hasOwn(
matchedComponents[0].components.default.components,
'ListsCard',
),
).to.eql(true)
})
it('list timeline', async () => {
@ -54,7 +74,12 @@ describe('routes', () => {
const matchedComponents = router.currentRoute.value.matched
expect(Object.hasOwn(matchedComponents[0].components.default.components, 'Timeline')).to.eql(true)
expect(
Object.hasOwn(
matchedComponents[0].components.default.components,
'Timeline',
),
).to.eql(true)
})
it('list edit', async () => {
@ -62,6 +87,11 @@ describe('routes', () => {
const matchedComponents = router.currentRoute.value.matched
expect(Object.hasOwn(matchedComponents[0].components.default.components, 'BasicUserCard')).to.eql(true)
expect(
Object.hasOwn(
matchedComponents[0].components.default.components,
'BasicUserCard',
),
).to.eql(true)
})
})

View file

@ -14,10 +14,16 @@ const autoSaveOrNot = (caseFn, caseTitle, runFn) => {
}
const saveManually = async (wrapper) => {
const morePostActions = wrapper.findByText('button', $t('post_status.more_post_actions'))
const morePostActions = wrapper.findByText(
'button',
$t('post_status.more_post_actions'),
)
await morePostActions.trigger('click')
const btn = wrapper.findByText('button', $t('post_status.save_to_drafts_button'))
const btn = wrapper.findByText(
'button',
$t('post_status.save_to_drafts_button'),
)
await btn.trigger('click')
}
@ -28,28 +34,34 @@ afterEach(() => {
})
describe('Draft saving', () => {
autoSaveOrNot(it, 'should save when the button is clicked', async (autoSave) => {
const wrapper = mount(PostStatusForm, mountOpts())
await wrapper.vm.$store.dispatch('setOption', {
name: 'autoSaveDraft',
value: autoSave
})
expect(wrapper.vm.$store.getters.draftCount).to.equal(0)
autoSaveOrNot(
it,
'should save when the button is clicked',
async (autoSave) => {
const wrapper = mount(PostStatusForm, mountOpts())
await wrapper.vm.$store.dispatch('setOption', {
name: 'autoSaveDraft',
value: autoSave,
})
expect(wrapper.vm.$store.getters.draftCount).to.equal(0)
const textarea = wrapper.get('textarea')
await textarea.setValue('mew mew')
const textarea = wrapper.get('textarea')
await textarea.setValue('mew mew')
await saveManually(wrapper)
expect(wrapper.vm.$store.getters.draftCount).to.equal(1)
expect(wrapper.vm.$store.getters.draftsArray[0].status).to.equal('mew mew')
})
await saveManually(wrapper)
expect(wrapper.vm.$store.getters.draftCount).to.equal(1)
expect(wrapper.vm.$store.getters.draftsArray[0].status).to.equal(
'mew mew',
)
},
)
it('should auto-save if it is enabled', async function () {
vi.useFakeTimers()
const wrapper = mount(PostStatusForm, mountOpts())
await wrapper.vm.$store.dispatch('setOption', {
name: 'autoSaveDraft',
value: true
value: true,
})
expect(wrapper.vm.$store.getters.draftCount).to.equal(0)
const textarea = wrapper.get('textarea')
@ -62,14 +74,17 @@ describe('Draft saving', () => {
})
it('should auto-save when close if auto-save is on', async () => {
const wrapper = mount(PostStatusForm, mountOpts({
props: {
closeable: true
}
}))
const wrapper = mount(
PostStatusForm,
mountOpts({
props: {
closeable: true,
},
}),
)
await wrapper.vm.$store.dispatch('setOption', {
name: 'autoSaveDraft',
value: true
value: true,
})
expect(wrapper.vm.$store.getters.draftCount).to.equal(0)
const textarea = wrapper.get('textarea')
@ -80,18 +95,21 @@ describe('Draft saving', () => {
})
it('should save when close if auto-save is off, and unsavedPostAction is save', async () => {
const wrapper = mount(PostStatusForm, mountOpts({
props: {
closeable: true
}
}))
const wrapper = mount(
PostStatusForm,
mountOpts({
props: {
closeable: true,
},
}),
)
await wrapper.vm.$store.dispatch('setOption', {
name: 'autoSaveDraft',
value: false
value: false,
})
await wrapper.vm.$store.dispatch('setOption', {
name: 'unsavedPostAction',
value: 'save'
value: 'save',
})
expect(wrapper.vm.$store.getters.draftCount).to.equal(0)
const textarea = wrapper.get('textarea')
@ -102,18 +120,21 @@ describe('Draft saving', () => {
})
it('should discard when close if auto-save is off, and unsavedPostAction is discard', async () => {
const wrapper = mount(PostStatusForm, mountOpts({
props: {
closeable: true
}
}))
const wrapper = mount(
PostStatusForm,
mountOpts({
props: {
closeable: true,
},
}),
)
await wrapper.vm.$store.dispatch('setOption', {
name: 'autoSaveDraft',
value: false
value: false,
})
await wrapper.vm.$store.dispatch('setOption', {
name: 'unsavedPostAction',
value: 'discard'
value: 'discard',
})
expect(wrapper.vm.$store.getters.draftCount).to.equal(0)
const textarea = wrapper.get('textarea')
@ -124,25 +145,31 @@ describe('Draft saving', () => {
})
it('should confirm when close if auto-save is off, and unsavedPostAction is confirm', async () => {
const wrapper = mount(PostStatusForm, mountOpts({
props: {
closeable: true
}
}))
const wrapper = mount(
PostStatusForm,
mountOpts({
props: {
closeable: true,
},
}),
)
await wrapper.vm.$store.dispatch('setOption', {
name: 'autoSaveDraft',
value: false
value: false,
})
await wrapper.vm.$store.dispatch('setOption', {
name: 'unsavedPostAction',
value: 'confirm'
value: 'confirm',
})
expect(wrapper.vm.$store.getters.draftCount).to.equal(0)
const textarea = wrapper.get('textarea')
await textarea.setValue('mew mew')
wrapper.vm.requestClose()
await nextTick()
const saveButton = wrapper.findByText('button', $t('post_status.close_confirm_save_button'))
const saveButton = wrapper.findByText(
'button',
$t('post_status.close_confirm_save_button'),
)
expect(saveButton).to.be.ok
await saveButton.trigger('click')
console.info('clicked')

View file

@ -11,33 +11,33 @@ const generateInput = (value, padEmoji = true) => {
$store: {
getters: {
mergedConfig: {
padEmoji
}
}
padEmoji,
},
},
},
$t: (msg) => msg
$t: (msg) => msg,
},
stubs: {
FAIcon: true,
Popover: {
template: `<div><slot trigger /></div>`,
methods: {
updateStyles () {}
}
}
updateStyles() {},
},
},
},
directives: {
'click-outside': vClickOutside
}
'click-outside': vClickOutside,
},
},
props: {
suggest: () => [],
enableEmojiPicker: true,
modelValue: value
modelValue: value,
},
slots: {
default: () => h('input', '')
}
default: () => h('input', ''),
},
})
return wrapper
}
@ -85,7 +85,9 @@ describe('EmojiInput', () => {
wrapper.setData({ caret: 6 })
wrapper.vm.insert({ insertion: ':ebin:', keepOpen: false })
const inputEvents = wrapper.emitted()['update:modelValue']
expect(inputEvents[inputEvents.length - 1][0]).to.eql('Spurdo :ebin: Sparde')
expect(inputEvents[inputEvents.length - 1][0]).to.eql(
'Spurdo :ebin: Sparde',
)
})
it('inserts string between words without creating extra spaces (other caret)', () => {
@ -96,7 +98,9 @@ describe('EmojiInput', () => {
wrapper.setData({ caret: 7 })
wrapper.vm.insert({ insertion: ':ebin:', keepOpen: false })
const inputEvents = wrapper.emitted()['update:modelValue']
expect(inputEvents[inputEvents.length - 1][0]).to.eql('Spurdo :ebin: Sparde')
expect(inputEvents[inputEvents.length - 1][0]).to.eql(
'Spurdo :ebin: Sparde',
)
})
it('inserts string without any padding if padEmoji setting is set to false', () => {
@ -107,7 +111,9 @@ describe('EmojiInput', () => {
wrapper.setData({ caret: initialString.length, keepOpen: false })
wrapper.vm.insert({ insertion: ':spam:' })
const inputEvents = wrapper.emitted()['update:modelValue']
expect(inputEvents[inputEvents.length - 1][0]).to.eql('Eat some spam!:spam:')
expect(inputEvents[inputEvents.length - 1][0]).to.eql(
'Eat some spam!:spam:',
)
})
it('correctly sets caret after insertion at beginning', async () => {

View file

@ -21,53 +21,44 @@ describe('Gallery', () => {
it('one audio attachment', () => {
local = {
attachments: [
{ mimetype: 'audio/mpeg' }
]
attachments: [{ mimetype: 'audio/mpeg' }],
}
expect(Gallery.computed.rows.call(local)).to.eql([
{ audio: true, items: [{ mimetype: 'audio/mpeg' }] }
{ audio: true, items: [{ mimetype: 'audio/mpeg' }] },
])
})
it('one image attachment', () => {
local = {
attachments: [
{ mimetype: 'image/png' }
]
attachments: [{ mimetype: 'image/png' }],
}
expect(Gallery.computed.rows.call(local)).to.eql([
{ items: [{ mimetype: 'image/png' }] }
{ items: [{ mimetype: 'image/png' }] },
])
})
it('one audio attachment and one image attachment', () => {
local = {
attachments: [
{ mimetype: 'audio/mpeg' },
{ mimetype: 'image/png' }
]
attachments: [{ mimetype: 'audio/mpeg' }, { mimetype: 'image/png' }],
}
expect(Gallery.computed.rows.call(local)).to.eql([
{ audio: true, items: [{ mimetype: 'audio/mpeg' }] },
{ items: [{ mimetype: 'image/png' }] }
{ items: [{ mimetype: 'image/png' }] },
])
})
it('has "size" key set to "hide"', () => {
let local
local = {
attachments: [
{ mimetype: 'audio/mpeg' }
],
size: 'hide'
attachments: [{ mimetype: 'audio/mpeg' }],
size: 'hide',
}
expect(Gallery.computed.rows.call(local)).to.eql([
{ minimal: true, items: [{ mimetype: 'audio/mpeg' }] }
{ minimal: true, items: [{ mimetype: 'audio/mpeg' }] },
])
local = {
@ -80,9 +71,9 @@ describe('Gallery', () => {
{ mimetype: 'audio/mpeg' },
{ mimetype: 'image/jpg' },
{ mimetype: 'image/png' },
{ mimetype: 'image/jpg' }
{ mimetype: 'image/jpg' },
],
size: 'hide'
size: 'hide',
}
// When defining `size: hide`, the `items` aren't
@ -96,7 +87,7 @@ describe('Gallery', () => {
{ minimal: true, items: [{ mimetype: 'audio/mpeg' }] },
{ minimal: true, items: [{ mimetype: 'image/jpg' }] },
{ minimal: true, items: [{ mimetype: 'image/png' }] },
{ minimal: true, items: [{ mimetype: 'image/jpg' }] }
{ minimal: true, items: [{ mimetype: 'image/jpg' }] },
])
})
@ -104,12 +95,10 @@ describe('Gallery', () => {
it('non-image/audio', () => {
let local
local = {
attachments: [
{ mimetype: 'plain/text' }
]
attachments: [{ mimetype: 'plain/text' }],
}
expect(Gallery.computed.rows.call(local)).to.eql([
{ minimal: true, items: [{ mimetype: 'plain/text' }] }
{ minimal: true, items: [{ mimetype: 'plain/text' }] },
])
// No grouping of non-image/audio items
@ -117,13 +106,13 @@ describe('Gallery', () => {
attachments: [
{ mimetype: 'plain/text' },
{ mimetype: 'plain/text' },
{ mimetype: 'plain/text' }
]
{ mimetype: 'plain/text' },
],
}
expect(Gallery.computed.rows.call(local)).to.eql([
{ minimal: true, items: [{ mimetype: 'plain/text' }] },
{ minimal: true, items: [{ mimetype: 'plain/text' }] },
{ minimal: true, items: [{ mimetype: 'plain/text' }] }
{ minimal: true, items: [{ mimetype: 'plain/text' }] },
])
local = {
@ -131,8 +120,8 @@ describe('Gallery', () => {
{ mimetype: 'image/png' },
{ mimetype: 'plain/text' },
{ mimetype: 'image/jpg' },
{ mimetype: 'audio/mpeg' }
]
{ mimetype: 'audio/mpeg' },
],
}
// NOTE / TODO: When defining `size: hide`, the `items` aren't
// grouped and `audio` isn't set
@ -140,7 +129,7 @@ describe('Gallery', () => {
{ items: [{ mimetype: 'image/png' }] },
{ minimal: true, items: [{ mimetype: 'plain/text' }] },
{ items: [{ mimetype: 'image/jpg' }] },
{ audio: true, items: [{ mimetype: 'audio/mpeg' }] }
{ audio: true, items: [{ mimetype: 'audio/mpeg' }] },
])
})
@ -153,15 +142,22 @@ describe('Gallery', () => {
{ mimetype: 'image/jpg' },
{ mimetype: 'image/png' },
{ mimetype: 'image/jpg' },
{ mimetype: 'image/jpg' }
]
{ mimetype: 'image/jpg' },
],
}
expect(Gallery.computed.rows.call(local)).to.eql([
{ audio: true, items: [{ mimetype: 'audio/mpeg' }] },
{ items: [{ mimetype: 'image/png' }] },
{ audio: true, items: [{ mimetype: 'audio/mpeg' }] },
{ items: [{ mimetype: 'image/jpg' }, { mimetype: 'image/png' }, { mimetype: 'image/jpg' }, { mimetype: 'image/jpg' }] }
{
items: [
{ mimetype: 'image/jpg' },
{ mimetype: 'image/png' },
{ mimetype: 'image/jpg' },
{ mimetype: 'image/jpg' },
],
},
])
local = {
@ -172,16 +168,22 @@ describe('Gallery', () => {
{ mimetype: 'image/jpg' },
{ mimetype: 'audio/mpeg' },
{ mimetype: 'image/png' },
{ mimetype: 'audio/mpeg' }
]
{ mimetype: 'audio/mpeg' },
],
}
expect(Gallery.computed.rows.call(local)).to.eql([
{ items: [{ mimetype: 'image/jpg' }, { mimetype: 'image/png' }, { mimetype: 'image/jpg' }] },
{
items: [
{ mimetype: 'image/jpg' },
{ mimetype: 'image/png' },
{ mimetype: 'image/jpg' },
],
},
{ items: [{ mimetype: 'image/jpg' }] },
{ audio: true, items: [{ mimetype: 'audio/mpeg' }] },
{ items: [{ mimetype: 'image/png' }] },
{ audio: true, items: [{ mimetype: 'audio/mpeg' }] }
{ audio: true, items: [{ mimetype: 'audio/mpeg' }] },
])
local = {
@ -192,15 +194,28 @@ describe('Gallery', () => {
{ mimetype: 'image/jpg' },
{ mimetype: 'image/png' },
{ mimetype: 'image/png' },
{ mimetype: 'image/jpg' }
]
{ mimetype: 'image/jpg' },
],
}
// Group by three-per-row, unless there's one dangling, then stick it on the end of the last row
// https://git.pleroma.social/pleroma/pleroma-fe/-/merge_requests/1785#note_98514
expect(Gallery.computed.rows.call(local)).to.eql([
{ items: [{ mimetype: 'image/jpg' }, { mimetype: 'image/png' }, { mimetype: 'image/jpg' }] },
{ items: [{ mimetype: 'image/jpg' }, { mimetype: 'image/png' }, { mimetype: 'image/png' }, { mimetype: 'image/jpg' }] }
{
items: [
{ mimetype: 'image/jpg' },
{ mimetype: 'image/png' },
{ mimetype: 'image/jpg' },
],
},
{
items: [
{ mimetype: 'image/jpg' },
{ mimetype: 'image/png' },
{ mimetype: 'image/png' },
{ mimetype: 'image/jpg' },
],
},
])
local = {
@ -212,14 +227,26 @@ describe('Gallery', () => {
{ mimetype: 'image/png' },
{ mimetype: 'image/png' },
{ mimetype: 'image/jpg' },
{ mimetype: 'image/png' }
]
{ mimetype: 'image/png' },
],
}
expect(Gallery.computed.rows.call(local)).to.eql([
{ items: [{ mimetype: 'image/jpg' }, { mimetype: 'image/png' }, { mimetype: 'image/jpg' }] },
{ items: [{ mimetype: 'image/jpg' }, { mimetype: 'image/png' }, { mimetype: 'image/png' }] },
{ items: [{ mimetype: 'image/jpg' }, { mimetype: 'image/png' }] }
{
items: [
{ mimetype: 'image/jpg' },
{ mimetype: 'image/png' },
{ mimetype: 'image/jpg' },
],
},
{
items: [
{ mimetype: 'image/jpg' },
{ mimetype: 'image/png' },
{ mimetype: 'image/png' },
],
},
{ items: [{ mimetype: 'image/jpg' }, { mimetype: 'image/png' }] },
])
})
@ -231,13 +258,13 @@ describe('Gallery', () => {
{ mimetype: 'image/jpg' },
{ mimetype: 'image/png' },
{ mimetype: 'image/jpg' },
{ mimetype: 'image/jpg' }
{ mimetype: 'image/jpg' },
]
local = { grid: true, attachments }
expect(Gallery.computed.rows.call(local)).to.eql([
{ grid: true, items: attachments }
{ grid: true, items: attachments },
])
})
@ -247,7 +274,7 @@ describe('Gallery', () => {
{ mimetype: 'image/png' },
{ mimetype: 'image/jpg' },
{ mimetype: 'audio/mpeg' },
{ mimetype: 'image/jpg' }
{ mimetype: 'image/jpg' },
]
let local
@ -255,14 +282,14 @@ describe('Gallery', () => {
expect(Gallery.computed.rows.call(local)).to.eql([
{ audio: true, items: [{ mimetype: 'audio/mpeg' }] },
{ items: [{ mimetype: 'image/png' }] }
{ items: [{ mimetype: 'image/png' }] },
])
local = { attachments, limit: 3 }
expect(Gallery.computed.rows.call(local)).to.eql([
{ audio: true, items: [{ mimetype: 'audio/mpeg' }] },
{ items: [{ mimetype: 'image/png' }, { mimetype: 'image/jpg' }] }
{ items: [{ mimetype: 'image/png' }, { mimetype: 'image/jpg' }] },
])
local = { attachments, limit: 4 }
@ -270,7 +297,7 @@ describe('Gallery', () => {
expect(Gallery.computed.rows.call(local)).to.eql([
{ audio: true, items: [{ mimetype: 'audio/mpeg' }] },
{ items: [{ mimetype: 'image/png' }, { mimetype: 'image/jpg' }] },
{ audio: true, items: [{ mimetype: 'audio/mpeg' }] }
{ audio: true, items: [{ mimetype: 'audio/mpeg' }] },
])
})
})

View file

@ -8,15 +8,15 @@ const global = {
state: {},
getters: {
mergedConfig: () => ({
mentionLinkShowTooltip: true
mentionLinkShowTooltip: true,
}),
findUserByUrl: () => null
}
}
findUserByUrl: () => null,
},
},
},
stubs: {
FAIcon: true
}
FAIcon: true,
},
}
const makeMention = (who, noClass) => {
@ -26,12 +26,14 @@ const makeMention = (who, noClass) => {
: `<span class="h-card"><a class="u-url mention" href="https://fake.tld/@${who}">@<span>${who}</span></a></span>`
}
const p = (...data) => `<p>${data.join('')}</p>`
const compwrap = (...data) => `<span class="RichContent">${data.join('')}</span>`
const mentionsLine = (times) => [
'<mentions-line-stub mentions="',
new Array(times).fill('[object Object]').join(','),
'"></mentions-line-stub>'
].join('')
const compwrap = (...data) =>
`<span class="RichContent">${data.join('')}</span>`
const mentionsLine = (times) =>
[
'<mentions-line-stub mentions="',
new Array(times).fill('[object Object]').join(','),
'"></mentions-line-stub>',
].join('')
describe('RichContent', () => {
it('renders simple post without exploding', () => {
@ -43,22 +45,16 @@ describe('RichContent', () => {
handleLinks: true,
greentext: true,
emoji: [],
html
}
html,
},
})
expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(html))
})
it('unescapes everything as needed', () => {
const html = [
p('Testing &#39;em all'),
'Testing &#39;em all'
].join('')
const expected = [
p('Testing \'em all'),
'Testing \'em all'
].join('')
const html = [p('Testing &#39;em all'), 'Testing &#39;em all'].join('')
const expected = [p("Testing 'em all"), "Testing 'em all"].join('')
const wrapper = shallowMount(RichContent, {
global,
props: {
@ -66,18 +62,15 @@ describe('RichContent', () => {
handleLinks: true,
greentext: true,
emoji: [],
html
}
html,
},
})
expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(expected))
})
it('replaces mention with mentionsline', () => {
const html = p(
makeMention('John'),
' how are you doing today?'
)
const html = p(makeMention('John'), ' how are you doing today?')
const wrapper = shallowMount(RichContent, {
global,
props: {
@ -85,37 +78,30 @@ describe('RichContent', () => {
handleLinks: true,
greentext: true,
emoji: [],
html
}
html,
},
})
expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(p(
mentionsLine(1),
' how are you doing today?'
)))
expect(wrapper.html().replace(/\n/g, '')).to.eql(
compwrap(p(mentionsLine(1), ' how are you doing today?')),
)
})
it('replaces mentions at the end of the hellpost', () => {
const html = [
p('How are you doing today, fine gentlemen?'),
p(
makeMention('John'),
makeMention('Josh'),
makeMention('Jeremy')
)
p(makeMention('John'), makeMention('Josh'), makeMention('Jeremy')),
].join('')
const expected = [
p(
'How are you doing today, fine gentlemen?'
),
p('How are you doing today, fine gentlemen?'),
// TODO fix this extra line somehow?
p(
'<mentions-line-stub mentions="',
'[object Object],',
'[object Object],',
'[object Object]',
'"></mentions-line-stub>'
)
'"></mentions-line-stub>',
),
].join('')
const wrapper = shallowMount(RichContent, {
@ -125,8 +111,8 @@ describe('RichContent', () => {
handleLinks: true,
greentext: true,
emoji: [],
html
}
html,
},
})
expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(expected))
@ -134,26 +120,24 @@ describe('RichContent', () => {
it('Does not touch links if link handling is disabled', () => {
const html = [
[
makeMention('Jack'),
'let\'s meet up with ',
makeMention('Janet')
].join(''),
[
makeMention('John'),
makeMention('Josh'), makeMention('Jeremy')
].join('')
[makeMention('Jack'), "let's meet up with ", makeMention('Janet')].join(
'',
),
[makeMention('John'), makeMention('Josh'), makeMention('Jeremy')].join(
'',
),
].join('\n')
const strippedHtml = [
[
makeMention('Jack', true),
'let\'s meet up with ',
makeMention('Janet', true)
"let's meet up with ",
makeMention('Janet', true),
].join(''),
[
makeMention('John', true),
makeMention('Josh', true), makeMention('Jeremy', true)
].join('')
makeMention('Josh', true),
makeMention('Jeremy', true),
].join(''),
].join('\n')
const wrapper = shallowMount(RichContent, {
@ -163,21 +147,18 @@ describe('RichContent', () => {
handleLinks: false,
greentext: true,
emoji: [],
html
}
html,
},
})
expect(wrapper.html()).to.eql(compwrap(strippedHtml))
})
it('Adds greentext and cyantext to the post', () => {
const html = [
'&gt;preordering videogames',
'&gt;any year'
].join('\n')
const html = ['&gt;preordering videogames', '&gt;any year'].join('\n')
const expected = [
'<span class="greentext">&gt;preordering videogames</span>',
'<span class="greentext">&gt;any year</span>'
'<span class="greentext">&gt;any year</span>',
].join('\n')
const wrapper = shallowMount(RichContent, {
@ -187,18 +168,15 @@ describe('RichContent', () => {
handleLinks: false,
greentext: true,
emoji: [],
html
}
html,
},
})
expect(wrapper.html()).to.eql(compwrap(expected))
})
it('Does not add greentext and cyantext if setting is set to false', () => {
const html = [
'&gt;preordering videogames',
'&gt;any year'
].join('\n')
const html = ['&gt;preordering videogames', '&gt;any year'].join('\n')
const wrapper = shallowMount(RichContent, {
global,
@ -207,8 +185,8 @@ describe('RichContent', () => {
handleLinks: false,
greentext: false,
emoji: [],
html
}
html,
},
})
expect(wrapper.html()).to.eql(compwrap(html))
@ -218,7 +196,7 @@ describe('RichContent', () => {
const html = p('Ebin :DDDD :spurdo:')
const expected = p(
'Ebin :DDDD ',
'<anonymous-stub shortcode="spurdo" islocal="true" class="emoji img" src="about:blank" title=":spurdo:" alt=":spurdo:"></anonymous-stub>'
'<anonymous-stub shortcode="spurdo" islocal="true" class="emoji img" src="about:blank" title=":spurdo:" alt=":spurdo:"></anonymous-stub>',
)
const wrapper = shallowMount(RichContent, {
@ -228,14 +206,14 @@ describe('RichContent', () => {
handleLinks: false,
greentext: false,
emoji: [{ url: 'about:blank', shortcode: 'spurdo' }],
html
}
html,
},
})
expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(expected))
})
it('Doesn\'t add nonexistent emoji to post', () => {
it("Doesn't add nonexistent emoji to post", () => {
const html = p('Lol :lol:')
const wrapper = shallowMount(RichContent, {
@ -245,8 +223,8 @@ describe('RichContent', () => {
handleLinks: false,
greentext: false,
emoji: [],
html
}
html,
},
})
expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(html))
@ -257,13 +235,13 @@ describe('RichContent', () => {
'&gt;quote',
makeMention('lol'),
'&gt;quote',
'&gt;quote'
'&gt;quote',
].join('\n')
const expected = [
'<span class="greentext">&gt;quote</span>',
mentionsLine(1),
'<span class="greentext">&gt;quote</span>',
'<span class="greentext">&gt;quote</span>'
'<span class="greentext">&gt;quote</span>',
].join('\n')
const wrapper = shallowMount(RichContent, {
@ -273,8 +251,8 @@ describe('RichContent', () => {
handleLinks: true,
greentext: true,
emoji: [],
html
}
html,
},
})
expect(wrapper.html()).to.eql(compwrap(expected))
@ -284,19 +262,10 @@ describe('RichContent', () => {
const html = [
'Bruh',
'Bruh',
[
makeMention('foo'),
makeMention('bar'),
makeMention('baz')
].join(''),
'Bruh'
].join('<br>')
const expected = [
[makeMention('foo'), makeMention('bar'), makeMention('baz')].join(''),
'Bruh',
'Bruh',
mentionsLine(3),
'Bruh'
].join('<br>')
const expected = ['Bruh', 'Bruh', mentionsLine(3), 'Bruh'].join('<br>')
const wrapper = shallowMount(RichContent, {
global,
@ -305,8 +274,8 @@ describe('RichContent', () => {
handleLinks: true,
greentext: true,
emoji: [],
html
}
html,
},
})
expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(expected))
@ -321,7 +290,7 @@ describe('RichContent', () => {
'#nou</a>',
' <a class="hashtag" data-tag="screencap" href="https://shitposter.club/tag/screencap">',
'#screencap</a>',
' </p>'
' </p>',
].join('')
const expected = [
'<p>',
@ -331,7 +300,7 @@ describe('RichContent', () => {
'</hashtag-link-stub>',
' <hashtag-link-stub url="https://shitposter.club/tag/screencap" content="#screencap" tag="screencap">',
'</hashtag-link-stub>',
' </p>'
' </p>',
].join('')
const wrapper = shallowMount(RichContent, {
@ -341,8 +310,8 @@ describe('RichContent', () => {
handleLinks: true,
greentext: true,
emoji: [],
html
}
html,
},
})
expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(expected))
@ -359,11 +328,9 @@ describe('RichContent', () => {
'lol.tld/</span>',
'<span>',
'</span>',
'</a>'
'</a>',
),
p(
'Testing'
)
p('Testing'),
].join('')
const expected = [
p(
@ -378,11 +345,9 @@ describe('RichContent', () => {
'</span>',
'</a>',
'</span>',
'</span>'
'</span>',
),
p(
'Testing'
)
p('Testing'),
].join('')
const wrapper = mount(RichContent, {
@ -392,11 +357,16 @@ describe('RichContent', () => {
handleLinks: true,
greentext: true,
emoji: [],
html
}
html,
},
})
expect(wrapper.html().replace(/\n/g, '').replace(/<!--.*?-->/g, '')).to.eql(compwrap(expected))
expect(
wrapper
.html()
.replace(/\n/g, '')
.replace(/<!--.*?-->/g, ''),
).to.eql(compwrap(expected))
})
it('rich contents of nested mentions are handled properly', () => {
@ -422,7 +392,7 @@ describe('RichContent', () => {
'</a>',
' ',
'</span>',
'Testing'
'Testing',
].join('')
const expected = [
'<span>',
@ -450,7 +420,7 @@ describe('RichContent', () => {
'</span>',
' ',
'</span>',
'Testing'
'Testing',
].join('')
const wrapper = mount(RichContent, {
@ -460,11 +430,16 @@ describe('RichContent', () => {
handleLinks: true,
greentext: true,
emoji: [],
html
}
html,
},
})
expect(wrapper.html().replace(/\n/g, '').replace(/<!--.*?-->/g, '')).to.eql(compwrap(expected))
expect(
wrapper
.html()
.replace(/\n/g, '')
.replace(/<!--.*?-->/g, ''),
).to.eql(compwrap(expected))
})
it('rich contents of a link are handled properly', () => {
@ -480,7 +455,7 @@ describe('RichContent', () => {
'<span>',
'</span>',
'</a>',
'</p>'
'</p>',
].join('')
const expected = [
'<p>',
@ -494,7 +469,7 @@ describe('RichContent', () => {
'<span>',
'</span>',
'</a>',
'</p>'
'</p>',
].join('')
const wrapper = shallowMount(RichContent, {
@ -504,8 +479,8 @@ describe('RichContent', () => {
handleLinks: true,
greentext: true,
emoji: [],
html
}
html,
},
})
expect(wrapper.html().replace(/\n/g, '')).to.eql(compwrap(expected))
@ -525,7 +500,7 @@ describe('RichContent', () => {
makeMention('Lain'),
makeMention('Lain'),
makeMention('Lain'),
' i just landed in l a where are you'
' i just landed in l a where are you',
)
const TestComponent = {
@ -537,7 +512,7 @@ describe('RichContent', () => {
${new Array(amount).fill(`<div v-html="${onePost}"/>`)}
</div>
`,
props: ['handleLinks', 'attentions', 'vhtml']
props: ['handleLinks', 'attentions', 'vhtml'],
}
const ptest = (handleLinks, vhtml) => {
@ -548,8 +523,8 @@ describe('RichContent', () => {
props: {
attentions,
handleLinks,
vhtml
}
vhtml,
},
})
const t1 = performance.now()

View file

@ -5,39 +5,39 @@ import backendInteractorService from 'src/services/backend_interactor_service/ba
import { getters } from 'src/modules/users.js'
const mutations = {
clearTimeline: () => {}
clearTimeline: () => {},
}
const actions = {
fetchUser: () => {},
fetchUserByScreenName: () => {}
fetchUserByScreenName: () => {},
}
const testGetters = {
findUser: state => getters.findUser(state.users),
findUserByName: state => getters.findUserByName(state.users),
relationship: state => getters.relationship(state.users),
findUser: (state) => getters.findUser(state.users),
findUserByName: (state) => getters.findUserByName(state.users),
relationship: (state) => getters.relationship(state.users),
mergedConfig: () => ({
colors: '',
highlight: {},
customTheme: {
colors: []
}
})
colors: [],
},
}),
}
const localUser = {
id: 100,
is_local: true,
screen_name: 'testUser',
screen_name_ui: 'testUser'
screen_name_ui: 'testUser',
}
const extUser = {
id: 100,
is_local: false,
screen_name: 'testUser@test.instance',
screen_name_ui: 'testUser@test.instance'
screen_name_ui: 'testUser@test.instance',
}
const externalProfileStore = createStore({
@ -47,13 +47,13 @@ const externalProfileStore = createStore({
state: {
api: {
fetchers: {},
backendInteractor: backendInteractorService('')
backendInteractor: backendInteractorService(''),
},
interface: {
browserSupport: ''
browserSupport: '',
},
instance: {
hideUserStats: true
hideUserStats: true,
},
statuses: {
timelines: {
@ -71,7 +71,7 @@ const externalProfileStore = createStore({
friends: [],
viewing: 'statuses',
userId: 100,
flushMarker: 0
flushMarker: 0,
},
media: {
statuses: [],
@ -87,20 +87,20 @@ const externalProfileStore = createStore({
friends: [],
viewing: 'statuses',
userId: 100,
flushMarker: 0
}
}
flushMarker: 0,
},
},
},
users: {
currentUser: {
credentials: ''
credentials: '',
},
usersObject: { 100: extUser },
usersByNameObject: {},
users: [extUser],
relationships: {}
}
}
relationships: {},
},
},
})
const localProfileStore = createStore({
@ -110,20 +110,20 @@ const localProfileStore = createStore({
state: {
api: {
fetchers: {},
backendInteractor: backendInteractorService('')
backendInteractor: backendInteractorService(''),
},
interface: {
browserSupport: ''
browserSupport: '',
},
config: {
colors: '',
highlight: {},
customTheme: {
colors: []
}
colors: [],
},
},
instance: {
hideUserStats: true
hideUserStats: true,
},
statuses: {
timelines: {
@ -141,7 +141,7 @@ const localProfileStore = createStore({
friends: [],
viewing: 'statuses',
userId: 100,
flushMarker: 0
flushMarker: 0,
},
media: {
statuses: [],
@ -157,20 +157,20 @@ const localProfileStore = createStore({
friends: [],
viewing: 'statuses',
userId: 100,
flushMarker: 0
}
}
flushMarker: 0,
},
},
},
users: {
currentUser: {
credentials: ''
credentials: '',
},
usersObject: { 100: localUser },
usersByNameObject: { testuser: localUser },
users: [localUser],
relationships: {}
}
}
relationships: {},
},
},
})
// https://github.com/vuejs/test-utils/issues/1382
@ -182,14 +182,16 @@ describe.skip('UserProfile', () => {
mocks: {
$route: {
params: { id: 100 },
name: 'external-user-profile'
name: 'external-user-profile',
},
$t: (msg) => msg
}
}
$t: (msg) => msg,
},
},
})
expect(wrapper.find('.user-screen-name').text()).to.eql('@testUser@test.instance')
expect(wrapper.find('.user-screen-name').text()).to.eql(
'@testUser@test.instance',
)
})
it('renders local profile', () => {
@ -199,11 +201,11 @@ describe.skip('UserProfile', () => {
mocks: {
$route: {
params: { name: 'testUser' },
name: 'user-profile'
name: 'user-profile',
},
$t: (msg) => msg
}
}
$t: (msg) => msg,
},
},
})
expect(wrapper.find('.user-screen-name').text()).to.eql('@testUser')

View file

@ -9,7 +9,7 @@ const getMockStorage = () => {
let state = {}
return {
getItem: vi.fn(async key => {
getItem: vi.fn(async (key) => {
console.info('get:', key, state[key])
return state[key]
}),
@ -19,7 +19,7 @@ const getMockStorage = () => {
}),
_clear: () => {
state = {}
}
},
}
}
@ -38,7 +38,7 @@ describe('piniaPersistPlugin', () => {
await mockStorage.setItem('pinia-local-test', { a: 3 })
const useTestStore = defineStore('test', {
state: () => ({ a: 1, b: 2 })
state: () => ({ a: 1, b: 2 }),
})
const test = useTestStore()
@ -55,7 +55,7 @@ describe('piniaPersistPlugin', () => {
const useTestStore = defineStore('test', {
state: () => ({ a: 1, b: 2, c: { d: 4, e: 5 } }),
persist: {}
persist: {},
})
const test = useTestStore()
@ -68,7 +68,7 @@ describe('piniaPersistPlugin', () => {
const useTestStore = defineStore('test', {
state: () => ({ a: 1, b: 2, c: { d: 4, e: 5 } }),
persist: {}
persist: {},
})
const test = useTestStore()
@ -84,7 +84,7 @@ describe('piniaPersistPlugin', () => {
const useTestStore = defineStore('test', {
state: () => ({ a: 1, b: 2, c: { d: 4, e: 5 } }),
persist: {}
persist: {},
})
const test = useTestStore()
@ -101,18 +101,21 @@ describe('piniaPersistPlugin', () => {
const useTestStore = defineStore('test', {
state: () => ({ a: 1, b: 2, c: { d: 4, e: 5 } }),
persist: {
afterLoad (state) {
afterLoad(state) {
return {
...state,
a: 5
a: 5,
}
}
}
},
},
})
const test = useTestStore()
await test.$persistLoaded
expect(await mockStorage.getItem('pinia-local-test')).to.eql({ a: 4, c: { d: 0 } })
expect(await mockStorage.getItem('pinia-local-test')).to.eql({
a: 4,
c: { d: 0 },
})
expect(test.a).to.eql(5)
expect(test.b).to.eql(2)
expect(test.c.d).to.eql(0)
@ -124,7 +127,7 @@ describe('piniaPersistPlugin', () => {
const useTestStore = defineStore('test', {
state: () => ({ a: 1, b: 2 }),
persist: {}
persist: {},
})
const test = useTestStore()
@ -138,29 +141,35 @@ describe('piniaPersistPlugin', () => {
test('it saves everything if paths is unspecified', async () => {
const useTestStore = defineStore('test', {
state: () => ({ a: 1, b: 2 }),
persist: {}
persist: {},
})
const test = useTestStore()
await test.$persistLoaded
test.$patch({ a: 3 })
await flushPromises()
expect(await mockStorage.getItem('pinia-local-test')).to.eql({ a: 3, b: 2 })
expect(await mockStorage.getItem('pinia-local-test')).to.eql({
a: 3,
b: 2,
})
})
test('it saves only specified paths', async () => {
const useTestStore = defineStore('test', {
state: () => ({ a: 1, b: 2, c: { d: 4, e: 5 } }),
persist: {
paths: ['a', 'c.d']
}
paths: ['a', 'c.d'],
},
})
const test = useTestStore()
await test.$persistLoaded
test.$patch({ a: 3 })
await flushPromises()
expect(await mockStorage.getItem('pinia-local-test')).to.eql({ a: 3, c: { d: 4 } })
expect(await mockStorage.getItem('pinia-local-test')).to.eql({
a: 3,
c: { d: 4 },
})
})
})
@ -171,8 +180,8 @@ describe('piniaPersistPlugin', () => {
state: () => ({ a: 1, b: 2 }),
persist: {
onSaveSuccess,
onSaveError
}
onSaveError,
},
})
const test = useTestStore()
@ -194,26 +203,35 @@ describe('piniaPersistPlugin', () => {
const useTestStore = defineStore('test', {
state: () => ({ a: 1, b: 2 }),
actions: {
increaseA () {
increaseA() {
++this.a
},
increaseB () {
increaseB() {
++this.b
}
},
},
persist: {
saveImmediatelyActions: ['increaseA']
}
saveImmediatelyActions: ['increaseA'],
},
})
const test = useTestStore()
await test.$persistLoaded
await test.increaseA()
expect(await mockStorage.getItem('pinia-local-test')).to.eql({ a: 2, b: 2 })
expect(await mockStorage.getItem('pinia-local-test')).to.eql({
a: 2,
b: 2,
})
await test.increaseB()
expect(await mockStorage.getItem('pinia-local-test')).to.eql({ a: 2, b: 2 })
expect(await mockStorage.getItem('pinia-local-test')).to.eql({
a: 2,
b: 2,
})
await test.increaseA()
expect(await mockStorage.getItem('pinia-local-test')).to.eql({ a: 3, b: 3 })
expect(await mockStorage.getItem('pinia-local-test')).to.eql({
a: 3,
b: 3,
})
})
})
@ -225,8 +243,8 @@ describe('piniaPersistPlugin', () => {
state: () => ({ a: 1, b: 2 }),
persist: {
onSaveSuccess,
onSaveError
}
onSaveError,
},
})
const test = useTestStore()
@ -251,8 +269,8 @@ describe('piniaPersistPlugin', () => {
state: () => ({ a: 1, b: 2 }),
persist: {
onSaveSuccess,
onSaveError
}
onSaveError,
},
})
const test = useTestStore()
@ -269,15 +287,15 @@ describe('piniaPersistPlugin', () => {
describe('afterLoad', () => {
test('it is called with the saved state object', async () => {
await mockStorage.setItem('pinia-local-test', { a: 2 })
const afterLoad = vi.fn(async orig => {
const afterLoad = vi.fn(async (orig) => {
return { a: orig.a + 1 }
})
const useTestStore = defineStore('test', {
state: () => ({ a: 1, b: 2 }),
persist: {
afterLoad
}
afterLoad,
},
})
const test = useTestStore()
await test.$persistLoaded
@ -294,8 +312,8 @@ describe('piniaPersistPlugin', () => {
const useTestStore = defineStore('test', {
state: () => ({ a: 1, b: 2 }),
persist: {
afterLoad
}
afterLoad,
},
})
const test = useTestStore()
await test.$persistLoaded

View file

@ -25,7 +25,7 @@ describe('The serverSideStorage module', () => {
describe('setServerSideStorage', () => {
const user = {
created_at: new Date('1999-02-09'),
storage: {}
storage: {},
}
it('should initialize storage if none present', () => {
@ -51,29 +51,27 @@ describe('The serverSideStorage module', () => {
store.cache = {
_timestamp: Date.now(),
_version: VERSION,
...cloneDeep(defaultState)
...cloneDeep(defaultState),
}
store.setServerSideStorage(
{
...user,
storage: {
_timestamp: 123,
_version: VERSION,
flagStorage: {
...defaultState.flagStorage,
updateCounter: 1
},
prefsStorage: {
...defaultState.prefsStorage
}
}
}
)
store.setServerSideStorage({
...user,
storage: {
_timestamp: 123,
_version: VERSION,
flagStorage: {
...defaultState.flagStorage,
updateCounter: 1,
},
prefsStorage: {
...defaultState.prefsStorage,
},
},
})
expect(store.cache.flagStorage).to.eql({
...defaultState.flagStorage,
updateCounter: 1
updateCounter: 1,
})
})
@ -81,19 +79,17 @@ describe('The serverSideStorage module', () => {
const store = useServerSideStorageStore()
store.cache = null
store.setServerSideStorage(
{
...user,
storage: {
_timestamp: 123,
_version: VERSION,
flagStorage: {
...defaultState.flagStorage,
updateCounter: 999
}
}
}
)
store.setServerSideStorage({
...user,
storage: {
_timestamp: 123,
_version: VERSION,
flagStorage: {
...defaultState.flagStorage,
updateCounter: 999,
},
},
})
expect(store.cache._timestamp).to.eql(123)
expect(store.flagStorage.updateCounter).to.eql(999)
expect(store.cache.flagStorage.updateCounter).to.eql(999)
@ -118,7 +114,7 @@ describe('The serverSideStorage module', () => {
operation: 'set',
args: [1],
// should have A timestamp, we don't really care what it is
timestamp: store.prefsStorage._journal[0].timestamp
timestamp: store.prefsStorage._journal[0].timestamp,
})
})
@ -127,7 +123,10 @@ describe('The serverSideStorage module', () => {
store.setPreference({ path: 'simple.testing', value: 1 })
store.setPreference({ path: 'simple.testing', value: 2 })
store.addCollectionPreference({ path: 'collections.testing', value: 2 })
store.removeCollectionPreference({ path: 'collections.testing', value: 2 })
store.removeCollectionPreference({
path: 'collections.testing',
value: 2,
})
store.updateCache({ username: 'test' })
expect(store.prefsStorage.simple.testing).to.eql(2)
expect(store.prefsStorage.collections.testing).to.eql([])
@ -137,14 +136,14 @@ describe('The serverSideStorage module', () => {
operation: 'set',
args: [2],
// should have A timestamp, we don't really care what it is
timestamp: store.prefsStorage._journal[0].timestamp
timestamp: store.prefsStorage._journal[0].timestamp,
})
expect(store.prefsStorage._journal[1]).to.eql({
path: 'collections.testing',
operation: 'removeFromCollection',
args: [2],
// should have A timestamp, we don't really care what it is
timestamp: store.prefsStorage._journal[1].timestamp
timestamp: store.prefsStorage._journal[1].timestamp,
})
})
@ -173,16 +172,26 @@ describe('The serverSideStorage module', () => {
const store = useServerSideStorageStore()
store.setPreference({ path: 'simple.object.foo', value: 1 })
expect(() => store.unsetPreference({ path: 'simple' })).to.throw()
expect(() => store.unsetPreference({ path: 'simple.object' })).to.throw()
expect(() =>
store.unsetPreference({ path: 'simple.object' }),
).to.throw()
})
it('should not allow (un)setting depth > 3', () => {
const store = useServerSideStorageStore()
store.setPreference({ path: 'simple.object', value: {} })
expect(() => store.setPreference({ path: 'simple.object.lv3', value: 1 })).to.not.throw()
expect(() => store.setPreference({ path: 'simple.object.lv3.lv4', value: 1})).to.throw()
expect(() => store.unsetPreference({ path: 'simple.object.lv3', value: 1 })).to.not.throw()
expect(() => store.unsetPreference({ path: 'simple.object.lv3.lv4', value: 1})).to.throw()
expect(() =>
store.setPreference({ path: 'simple.object.lv3', value: 1 }),
).to.not.throw()
expect(() =>
store.setPreference({ path: 'simple.object.lv3.lv4', value: 1 }),
).to.throw()
expect(() =>
store.unsetPreference({ path: 'simple.object.lv3', value: 1 }),
).to.not.throw()
expect(() =>
store.unsetPreference({ path: 'simple.object.lv3.lv4', value: 1 }),
).to.throw()
})
})
})
@ -200,33 +209,65 @@ describe('The serverSideStorage module', () => {
})
describe('_getRecentData', () => {
it('should handle nulls correctly', () => {
expect(_getRecentData(null, null, true)).to.eql({ recent: null, stale: null, needUpload: true })
expect(_getRecentData(null, null, true)).to.eql({
recent: null,
stale: null,
needUpload: true,
})
})
it('doesn\'t choke on invalid data', () => {
expect(_getRecentData({ a: 1 }, { b: 2 }, true)).to.eql({ recent: null, stale: null, needUpload: true })
it("doesn't choke on invalid data", () => {
expect(_getRecentData({ a: 1 }, { b: 2 }, true)).to.eql({
recent: null,
stale: null,
needUpload: true,
})
})
it('should prefer the valid non-null correctly, needUpload works properly', () => {
const nonNull = { _version: VERSION, _timestamp: 1 }
expect(_getRecentData(nonNull, null, true)).to.eql({ recent: nonNull, stale: null, needUpload: true })
expect(_getRecentData(null, nonNull, true)).to.eql({ recent: nonNull, stale: null, needUpload: false })
expect(_getRecentData(nonNull, null, true)).to.eql({
recent: nonNull,
stale: null,
needUpload: true,
})
expect(_getRecentData(null, nonNull, true)).to.eql({
recent: nonNull,
stale: null,
needUpload: false,
})
})
it('should prefer the one with higher timestamp', () => {
const a = { _version: VERSION, _timestamp: 1 }
const b = { _version: VERSION, _timestamp: 2 }
expect(_getRecentData(a, b, true)).to.eql({ recent: b, stale: a, needUpload: false })
expect(_getRecentData(b, a, true)).to.eql({ recent: b, stale: a, needUpload: false })
expect(_getRecentData(a, b, true)).to.eql({
recent: b,
stale: a,
needUpload: false,
})
expect(_getRecentData(b, a, true)).to.eql({
recent: b,
stale: a,
needUpload: false,
})
})
it('case where both are same', () => {
const a = { _version: VERSION, _timestamp: 3 }
const b = { _version: VERSION, _timestamp: 3 }
expect(_getRecentData(a, b, true)).to.eql({ recent: b, stale: a, needUpload: false })
expect(_getRecentData(b, a, true)).to.eql({ recent: b, stale: a, needUpload: false })
expect(_getRecentData(a, b, true)).to.eql({
recent: b,
stale: a,
needUpload: false,
})
expect(_getRecentData(b, a, true)).to.eql({
recent: b,
stale: a,
needUpload: false,
})
})
})
@ -235,10 +276,17 @@ describe('The serverSideStorage module', () => {
expect(_getAllFlags(null, null)).to.eql([])
})
it('should output list of keys if passed single object', () => {
expect(_getAllFlags({ flagStorage: { a: 1, b: 1, c: 1 } }, null)).to.eql(['a', 'b', 'c'])
expect(
_getAllFlags({ flagStorage: { a: 1, b: 1, c: 1 } }, null),
).to.eql(['a', 'b', 'c'])
})
it('should union keys of both objects', () => {
expect(_getAllFlags({ flagStorage: { a: 1, b: 1, c: 1 } }, { flagStorage: { c: 1, d: 1 } })).to.eql(['a', 'b', 'c', 'd'])
expect(
_getAllFlags(
{ flagStorage: { a: 1, b: 1, c: 1 } },
{ flagStorage: { c: 1, d: 1 } },
),
).to.eql(['a', 'b', 'c', 'd'])
})
})
@ -248,7 +296,8 @@ describe('The serverSideStorage module', () => {
_mergeFlags(
{ flagStorage: { a: 0, b: 3 } },
{ flagStorage: { b: 1, c: 4, d: 9 } },
['a', 'b', 'c', 'd'])
['a', 'b', 'c', 'd'],
),
).to.eql({ a: 0, b: 3, c: 4, d: 9 })
})
})
@ -262,25 +311,30 @@ describe('The serverSideStorage module', () => {
simple: { a: 1, b: 0, c: true },
_journal: [
{ path: 'simple.b', operation: 'set', args: [0], timestamp: 2 },
{ path: 'simple.c', operation: 'set', args: [true], timestamp: 4 }
]
{
path: 'simple.c',
operation: 'set',
args: [true],
timestamp: 4,
},
],
},
// STALE
{
simple: { a: 1, b: 1, c: false },
_journal: [
{ path: 'simple.a', operation: 'set', args: [1], timestamp: 1 },
{ path: 'simple.b', operation: 'set', args: [1], timestamp: 3 }
]
}
)
{ path: 'simple.b', operation: 'set', args: [1], timestamp: 3 },
],
},
),
).to.eql({
simple: { a: 1, b: 1, c: true },
_journal: [
{ path: 'simple.a', operation: 'set', args: [1], timestamp: 1 },
{ path: 'simple.b', operation: 'set', args: [1], timestamp: 3 },
{ path: 'simple.c', operation: 'set', args: [true], timestamp: 4 }
]
{ path: 'simple.c', operation: 'set', args: [true], timestamp: 4 },
],
})
})
@ -292,25 +346,30 @@ describe('The serverSideStorage module', () => {
simple: { a: 1, b: 0, c: false },
_journal: [
{ path: 'simple.b', operation: 'set', args: [0], timestamp: 2 },
{ path: 'simple.c', operation: 'set', args: [false], timestamp: 4 }
]
{
path: 'simple.c',
operation: 'set',
args: [false],
timestamp: 4,
},
],
},
// STALE
{
simple: { a: 0, b: 0, c: true },
_journal: [
{ path: 'simple.a', operation: 'set', args: [0], timestamp: 1 },
{ path: 'simple.b', operation: 'set', args: [0], timestamp: 3 }
]
}
)
{ path: 'simple.b', operation: 'set', args: [0], timestamp: 3 },
],
},
),
).to.eql({
simple: { a: 0, b: 0, c: false },
_journal: [
{ path: 'simple.a', operation: 'set', args: [0], timestamp: 1 },
{ path: 'simple.b', operation: 'set', args: [0], timestamp: 3 },
{ path: 'simple.c', operation: 'set', args: [false], timestamp: 4 }
]
{ path: 'simple.c', operation: 'set', args: [false], timestamp: 4 },
],
})
})
@ -321,22 +380,32 @@ describe('The serverSideStorage module', () => {
{
simple: { a: 'foo' },
_journal: [
{ path: 'simple.a', operation: 'set', args: ['foo'], timestamp: 2 }
]
{
path: 'simple.a',
operation: 'set',
args: ['foo'],
timestamp: 2,
},
],
},
// STALE
{
simple: { a: 'bar' },
_journal: [
{ path: 'simple.a', operation: 'set', args: ['bar'], timestamp: 4 }
]
}
)
{
path: 'simple.a',
operation: 'set',
args: ['bar'],
timestamp: 4,
},
],
},
),
).to.eql({
simple: { a: 'bar' },
_journal: [
{ path: 'simple.a', operation: 'set', args: ['bar'], timestamp: 4 }
]
{ path: 'simple.a', operation: 'set', args: ['bar'], timestamp: 4 },
],
})
})
@ -347,22 +416,37 @@ describe('The serverSideStorage module', () => {
{
simple: { lv2: { lv3: 'foo' } },
_journal: [
{ path: 'simple.lv2.lv3', operation: 'set', args: ['foo'], timestamp: 2 }
]
{
path: 'simple.lv2.lv3',
operation: 'set',
args: ['foo'],
timestamp: 2,
},
],
},
// STALE
{
simple: { lv2: { lv3: 'bar' } },
_journal: [
{ path: 'simple.lv2.lv3', operation: 'set', args: ['bar'], timestamp: 4 }
]
}
)
{
path: 'simple.lv2.lv3',
operation: 'set',
args: ['bar'],
timestamp: 4,
},
],
},
),
).to.eql({
simple: { lv2: { lv3: 'bar' } },
_journal: [
{ path: 'simple.lv2.lv3', operation: 'set', args: ['bar'], timestamp: 4 }
]
{
path: 'simple.lv2.lv3',
operation: 'set',
args: ['bar'],
timestamp: 4,
},
],
})
})
@ -373,22 +457,37 @@ describe('The serverSideStorage module', () => {
{
simple: { lv2: { lv3: 'foo' } },
_journal: [
{ path: 'simple.lv2.lv3', operation: 'set', args: ['foo'], timestamp: 2 }
]
{
path: 'simple.lv2.lv3',
operation: 'set',
args: ['foo'],
timestamp: 2,
},
],
},
// STALE
{
simple: { lv2: {} },
_journal: [
{ path: 'simple.lv2.lv3', operation: 'unset', args: [], timestamp: 4 }
]
}
)
{
path: 'simple.lv2.lv3',
operation: 'unset',
args: [],
timestamp: 4,
},
],
},
),
).to.eql({
simple: { lv2: {} },
_journal: [
{ path: 'simple.lv2.lv3', operation: 'unset', args: [], timestamp: 4 }
]
{
path: 'simple.lv2.lv3',
operation: 'unset',
args: [],
timestamp: 4,
},
],
})
})
})
@ -402,12 +501,25 @@ describe('The serverSideStorage module', () => {
it('should trim all flags to known when reset is set to 1000', () => {
const totalFlags = { a: 0, b: 3, c: 33, reset: COMMAND_TRIM_FLAGS }
expect(_resetFlags(totalFlags, { a: 0, b: 0, reset: 0 })).to.eql({ a: 0, b: 3, reset: 0 })
expect(_resetFlags(totalFlags, { a: 0, b: 0, reset: 0 })).to.eql({
a: 0,
b: 3,
reset: 0,
})
})
it('should trim all flags to known and reset when reset is set to 1001', () => {
const totalFlags = { a: 0, b: 3, c: 33, reset: COMMAND_TRIM_FLAGS_AND_RESET }
const totalFlags = {
a: 0,
b: 3,
c: 33,
reset: COMMAND_TRIM_FLAGS_AND_RESET,
}
expect(_resetFlags(totalFlags, { a: 0, b: 0, reset: 0 })).to.eql({ a: 0, b: 0, reset: 0 })
expect(_resetFlags(totalFlags, { a: 0, b: 0, reset: 0 })).to.eql({
a: 0,
b: 0,
reset: 0,
})
})
})
})

View file

@ -1,6 +1,9 @@
import { defaultState, mutations, prepareStatus } from '../../../../src/modules/statuses.js'
import {
defaultState,
mutations,
prepareStatus,
} from '../../../../src/modules/statuses.js'
const makeMockStatus = ({ id, text, type = 'status' }) => {
return {
id,
@ -10,7 +13,7 @@ const makeMockStatus = ({ id, text, type = 'status' }) => {
fave_num: 0,
uri: '',
type,
attentions: []
attentions: [],
}
}
@ -27,7 +30,10 @@ describe('Statuses module', () => {
const state = defaultState()
const status = makeMockStatus({ id: '1' })
mutations.addNewStatuses(state, { statuses: [status], timeline: 'public' })
mutations.addNewStatuses(state, {
statuses: [status],
timeline: 'public',
})
expect(state.allStatuses).to.eql([status])
expect(state.timelines.public.statuses).to.eql([status])
@ -39,8 +45,14 @@ describe('Statuses module', () => {
const state = defaultState()
const status = makeMockStatus({ id: '1' })
mutations.addNewStatuses(state, { statuses: [status], timeline: 'public' })
mutations.addNewStatuses(state, { statuses: [status], timeline: 'friends' })
mutations.addNewStatuses(state, {
statuses: [status],
timeline: 'public',
})
mutations.addNewStatuses(state, {
statuses: [status],
timeline: 'friends',
})
expect(state.allStatuses).to.eql([status])
expect(state.timelines.public.statuses).to.eql([status])
@ -69,7 +81,11 @@ describe('Statuses module', () => {
const state = defaultState()
const status = makeMockStatus({ id: '1' })
mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' })
mutations.addNewStatuses(state, {
statuses: [status],
showImmediately: true,
timeline: 'public',
})
expect(state.allStatuses).to.eql([status])
expect(state.timelines.public.statuses).to.eql([status])
@ -82,12 +98,24 @@ describe('Statuses module', () => {
const status = makeMockStatus({ id: '1' })
const secondStatus = makeMockStatus({ id: '2' })
mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' })
mutations.addNewStatuses(state, {
statuses: [status],
showImmediately: true,
timeline: 'public',
})
expect(state.timelines.public.maxId).to.eql('1')
mutations.addNewStatuses(state, { statuses: [secondStatus], showImmediately: true, timeline: 'public', noIdUpdate: true })
mutations.addNewStatuses(state, {
statuses: [secondStatus],
showImmediately: true,
timeline: 'public',
noIdUpdate: true,
})
expect(state.timelines.public.statuses).to.eql([secondStatus, status])
expect(state.timelines.public.visibleStatuses).to.eql([secondStatus, status])
expect(state.timelines.public.visibleStatuses).to.eql([
secondStatus,
status,
])
expect(state.timelines.public.maxId).to.eql('1')
})
@ -98,17 +126,42 @@ describe('Statuses module', () => {
const statusTwo = makeMockStatus({ id: '2' })
const statusThree = makeMockStatus({ id: '4' })
mutations.addNewStatuses(state, { statuses: [nonVisibleStatus], showImmediately: false, timeline: 'public' })
mutations.addNewStatuses(state, {
statuses: [nonVisibleStatus],
showImmediately: false,
timeline: 'public',
})
mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' })
mutations.addNewStatuses(state, { statuses: [statusTwo], showImmediately: true, timeline: 'public' })
mutations.addNewStatuses(state, {
statuses: [status],
showImmediately: true,
timeline: 'public',
})
mutations.addNewStatuses(state, {
statuses: [statusTwo],
showImmediately: true,
timeline: 'public',
})
expect(state.timelines.public.minVisibleId).to.equal('2')
mutations.addNewStatuses(state, { statuses: [statusThree], showImmediately: true, timeline: 'public' })
mutations.addNewStatuses(state, {
statuses: [statusThree],
showImmediately: true,
timeline: 'public',
})
expect(state.timelines.public.statuses).to.eql([statusThree, status, statusTwo, nonVisibleStatus])
expect(state.timelines.public.visibleStatuses).to.eql([statusThree, status, statusTwo])
expect(state.timelines.public.statuses).to.eql([
statusThree,
status,
statusTwo,
nonVisibleStatus,
])
expect(state.timelines.public.visibleStatuses).to.eql([
statusThree,
status,
statusTwo,
])
})
it('splits retweets from their status and links them', () => {
@ -120,7 +173,11 @@ describe('Statuses module', () => {
retweet.retweeted_status = status
// It adds both statuses, but only the retweet to visible.
mutations.addNewStatuses(state, { statuses: [retweet], timeline: 'public', showImmediately: true })
mutations.addNewStatuses(state, {
statuses: [retweet],
timeline: 'public',
showImmediately: true,
})
expect(state.timelines.public.visibleStatuses).to.have.length(1)
expect(state.timelines.public.statuses).to.have.length(1)
expect(state.allStatuses).to.have.length(2)
@ -128,7 +185,10 @@ describe('Statuses module', () => {
expect(state.allStatuses[1].id).to.equal('2')
// It refers to the modified status.
mutations.addNewStatuses(state, { statuses: [modStatus], timeline: 'public' })
mutations.addNewStatuses(state, {
statuses: [modStatus],
timeline: 'public',
})
expect(state.allStatuses).to.have.length(2)
expect(state.allStatuses[0].id).to.equal('1')
expect(state.allStatuses[0].text).to.equal(modStatus.text)
@ -142,12 +202,20 @@ describe('Statuses module', () => {
const modStatus = makeMockStatus({ id: '1', text: 'something else' })
// Add original status
mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' })
mutations.addNewStatuses(state, {
statuses: [status],
showImmediately: true,
timeline: 'public',
})
expect(state.timelines.public.visibleStatuses).to.have.length(1)
expect(state.allStatuses).to.have.length(1)
// Add new version of status
mutations.addNewStatuses(state, { statuses: [modStatus], showImmediately: true, timeline: 'public' })
mutations.addNewStatuses(state, {
statuses: [modStatus],
showImmediately: true,
timeline: 'public',
})
expect(state.timelines.public.visibleStatuses).to.have.length(1)
expect(state.allStatuses).to.have.length(1)
expect(state.allStatuses[0].text).to.eql(modStatus.text)
@ -161,12 +229,20 @@ describe('Statuses module', () => {
retweet.retweeted_status = modStatus
// Add original status
mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' })
mutations.addNewStatuses(state, {
statuses: [status],
showImmediately: true,
timeline: 'public',
})
expect(state.timelines.public.visibleStatuses).to.have.length(1)
expect(state.allStatuses).to.have.length(1)
// Add new version of status
mutations.addNewStatuses(state, { statuses: [retweet], showImmediately: false, timeline: 'public' })
mutations.addNewStatuses(state, {
statuses: [retweet],
showImmediately: false,
timeline: 'public',
})
expect(state.timelines.public.visibleStatuses).to.have.length(1)
// Don't add the retweet itself if the tweet is visible
expect(state.timelines.public.statuses).to.have.length(1)
@ -184,18 +260,30 @@ describe('Statuses module', () => {
in_reply_to_status_id: '1', // The API uses strings here...
uri: 'tag:shitposter.club,2016-08-21:fave:3895:note:773501:2016-08-21T16:52:15+00:00',
text: 'a favorited something by b',
user: { id: '99' }
user: { id: '99' },
}
mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' })
mutations.addNewStatuses(state, { statuses: [favorite], showImmediately: true, timeline: 'public' })
mutations.addNewStatuses(state, {
statuses: [status],
showImmediately: true,
timeline: 'public',
})
mutations.addNewStatuses(state, {
statuses: [favorite],
showImmediately: true,
timeline: 'public',
})
expect(state.timelines.public.visibleStatuses.length).to.eql(1)
expect(state.timelines.public.visibleStatuses[0].fave_num).to.eql(1)
expect(state.timelines.public.maxId).to.eq(favorite.id)
// Adding it again does nothing
mutations.addNewStatuses(state, { statuses: [favorite], showImmediately: true, timeline: 'public' })
mutations.addNewStatuses(state, {
statuses: [favorite],
showImmediately: true,
timeline: 'public',
})
expect(state.timelines.public.visibleStatuses.length).to.eql(1)
expect(state.timelines.public.visibleStatuses[0].fave_num).to.eql(1)
@ -203,7 +291,7 @@ describe('Statuses module', () => {
// If something is favorited by the current user, it also sets the 'favorited' property but does not increment counter to avoid over-counting. Counter is incremented (updated, really) via response to the favorite request.
const user = {
id: '1'
id: '1',
}
const ownFavorite = {
@ -212,10 +300,15 @@ describe('Statuses module', () => {
in_reply_to_status_id: '1', // The API uses strings here...
uri: 'tag:shitposter.club,2016-08-21:fave:3895:note:773501:2016-08-21T16:52:15+00:00',
text: 'a favorited something by b',
user
user,
}
mutations.addNewStatuses(state, { statuses: [ownFavorite], showImmediately: true, timeline: 'public', user })
mutations.addNewStatuses(state, {
statuses: [ownFavorite],
showImmediately: true,
timeline: 'public',
user,
})
expect(state.timelines.public.visibleStatuses.length).to.eql(1)
expect(state.timelines.public.visibleStatuses[0].fave_num).to.eql(1)
@ -229,11 +322,21 @@ describe('Statuses module', () => {
const status = makeMockStatus({ id: '1' })
status.emoji_reactions = [{ name: '😂', count: 1, accounts: [] }]
mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' })
mutations.addOwnReaction(state, { id: '1', emoji: '😂', currentUser: { id: 'me' } })
mutations.addNewStatuses(state, {
statuses: [status],
showImmediately: true,
timeline: 'public',
})
mutations.addOwnReaction(state, {
id: '1',
emoji: '😂',
currentUser: { id: 'me' },
})
expect(state.allStatusesObject['1'].emoji_reactions[0].count).to.eql(2)
expect(state.allStatusesObject['1'].emoji_reactions[0].me).to.eql(true)
expect(state.allStatusesObject['1'].emoji_reactions[0].accounts[0].id).to.eql('me')
expect(
state.allStatusesObject['1'].emoji_reactions[0].accounts[0].id,
).to.eql('me')
})
it('adds a new reaction', () => {
@ -241,32 +344,64 @@ describe('Statuses module', () => {
const status = makeMockStatus({ id: '1' })
status.emoji_reactions = []
mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' })
mutations.addOwnReaction(state, { id: '1', emoji: '😂', currentUser: { id: 'me' } })
mutations.addNewStatuses(state, {
statuses: [status],
showImmediately: true,
timeline: 'public',
})
mutations.addOwnReaction(state, {
id: '1',
emoji: '😂',
currentUser: { id: 'me' },
})
expect(state.allStatusesObject['1'].emoji_reactions[0].count).to.eql(1)
expect(state.allStatusesObject['1'].emoji_reactions[0].me).to.eql(true)
expect(state.allStatusesObject['1'].emoji_reactions[0].accounts[0].id).to.eql('me')
expect(
state.allStatusesObject['1'].emoji_reactions[0].accounts[0].id,
).to.eql('me')
})
it('decreases count in existing reaction', () => {
const state = defaultState()
const status = makeMockStatus({ id: '1' })
status.emoji_reactions = [{ name: '😂', count: 2, accounts: [{ id: 'me' }] }]
status.emoji_reactions = [
{ name: '😂', count: 2, accounts: [{ id: 'me' }] },
]
mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' })
mutations.removeOwnReaction(state, { id: '1', emoji: '😂', currentUser: { id: 'me' } })
mutations.addNewStatuses(state, {
statuses: [status],
showImmediately: true,
timeline: 'public',
})
mutations.removeOwnReaction(state, {
id: '1',
emoji: '😂',
currentUser: { id: 'me' },
})
expect(state.allStatusesObject['1'].emoji_reactions[0].count).to.eql(1)
expect(state.allStatusesObject['1'].emoji_reactions[0].me).to.eql(false)
expect(state.allStatusesObject['1'].emoji_reactions[0].accounts).to.eql([])
expect(state.allStatusesObject['1'].emoji_reactions[0].accounts).to.eql(
[],
)
})
it('removes a reaction', () => {
const state = defaultState()
const status = makeMockStatus({ id: '1' })
status.emoji_reactions = [{ name: '😂', count: 1, accounts: [{ id: 'me' }] }]
status.emoji_reactions = [
{ name: '😂', count: 1, accounts: [{ id: 'me' }] },
]
mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' })
mutations.removeOwnReaction(state, { id: '1', emoji: '😂', currentUser: { id: 'me' } })
mutations.addNewStatuses(state, {
statuses: [status],
showImmediately: true,
timeline: 'public',
})
mutations.removeOwnReaction(state, {
id: '1',
emoji: '😂',
currentUser: { id: 'me' },
})
expect(state.allStatusesObject['1'].emoji_reactions.length).to.eql(0)
})
})
@ -275,9 +410,17 @@ describe('Statuses module', () => {
it('resets the minId to the min of the visible statuses when adding new to visible statuses', () => {
const state = defaultState()
const status = makeMockStatus({ id: '10' })
mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' })
mutations.addNewStatuses(state, {
statuses: [status],
showImmediately: true,
timeline: 'public',
})
const newStatus = makeMockStatus({ id: '20' })
mutations.addNewStatuses(state, { statuses: [newStatus], showImmediately: false, timeline: 'public' })
mutations.addNewStatuses(state, {
statuses: [newStatus],
showImmediately: false,
timeline: 'public',
})
state.timelines.public.minId = '5'
mutations.showNewStatuses(state, { timeline: 'public' })

View file

@ -1,6 +1,10 @@
import { cloneDeep } from 'lodash'
import { defaultState, mutations, getters } from '../../../../src/modules/users.js'
import {
defaultState,
mutations,
getters,
} from '../../../../src/modules/users.js'
describe('The users module', () => {
describe('mutations', () => {
@ -23,22 +27,18 @@ describe('The users module', () => {
const state = cloneDeep(defaultState)
const user = {
id: '1',
fields: [
{ name: 'Label 1', value: 'Content 1' }
]
fields: [{ name: 'Label 1', value: 'Content 1' }],
}
const firstModUser = {
id: '1',
fields: [
{ name: 'Label 2', value: 'Content 2' },
{ name: 'Label 3', value: 'Content 3' }
]
{ name: 'Label 3', value: 'Content 3' },
],
}
const secondModUser = {
id: '1',
fields: [
{ name: 'Label 4', value: 'Content 4' }
]
fields: [{ name: 'Label 4', value: 'Content 4' }],
}
mutations.addNewUsers(state, [user])
@ -61,11 +61,11 @@ describe('The users module', () => {
const user = { screen_name: 'Guy', id: '1' }
const state = {
usersObject: {
1: user
1: user,
},
usersByNameObject: {
guy: user
}
guy: user,
},
}
const name = 'Guy'
expect(getters.findUser(state)(name)).to.eql(undefined)
@ -75,11 +75,11 @@ describe('The users module', () => {
const user = { screen_name: 'Guy', id: '1' }
const state = {
usersObject: {
1: user
1: user,
},
usersByNameObject: {
guy: user
}
guy: user,
},
}
const id = '1'
const expected = { screen_name: 'Guy', id: '1' }
@ -92,11 +92,11 @@ describe('The users module', () => {
const user = { screen_name: 'Guy', id: '1' }
const state = {
usersObject: {
1: user
1: user,
},
usersByNameObject: {
guy: user
}
guy: user,
},
}
const name = 'Guy'
const expected = { screen_name: 'Guy', id: '1' }
@ -107,11 +107,11 @@ describe('The users module', () => {
const user = { screen_name: 'Guy', id: '1' }
const state = {
usersObject: {
1: user
1: user,
},
usersByNameObject: {
guy: user
}
guy: user,
},
}
const id = '1'
expect(getters.findUserByName(state)(id)).to.eql(undefined)

View file

@ -3,21 +3,21 @@ import chatService from '../../../../../src/services/chat_service/chat_service.j
const message1 = {
id: '9wLkdcmQXD21Oy8lEX',
idempotency_key: '1',
created_at: (new Date('2020-06-22T18:45:53.000Z'))
created_at: new Date('2020-06-22T18:45:53.000Z'),
}
const message2 = {
id: '9wLkdp6ihaOVdNj8Wu',
idempotency_key: '2',
account_id: '9vmRb29zLQReckr5ay',
created_at: (new Date('2020-06-22T18:45:56.000Z'))
created_at: new Date('2020-06-22T18:45:56.000Z'),
}
const message3 = {
id: '9wLke9zL4Dy4OZR2RM',
idempotency_key: '3',
account_id: '9vmRb29zLQReckr5ay',
created_at: (new Date('2020-07-22T18:45:59.000Z'))
created_at: new Date('2020-07-22T18:45:59.000Z'),
}
describe('chatService', () => {
@ -85,7 +85,13 @@ describe('chatService', () => {
chatService.add(chat, { messages: [message3] })
const view = chatService.getView(chat)
expect(view.map(i => i.type)).to.eql(['date', 'message', 'message', 'date', 'message'])
expect(view.map((i) => i.type)).to.eql([
'date',
'message',
'message',
'date',
'message',
])
})
})
@ -95,7 +101,15 @@ describe('chatService', () => {
for (let i = 100; i > 0; i--) {
// Use decimal values with toFixed to hack together constant length predictable strings
chatService.add(chat, { messages: [{ ...message1, id: 'a' + (i / 1000).toFixed(3), idempotency_key: i }] })
chatService.add(chat, {
messages: [
{
...message1,
id: 'a' + (i / 1000).toFixed(3),
idempotency_key: i,
},
],
})
}
chatService.cullOlderMessages(chat)
expect(chat.messages.length).to.eql(50)

View file

@ -1,4 +1,9 @@
import { replaceWord, addPositionToWords, wordAtPosition, splitByWhitespaceBoundary } from '../../../../../src/services/completion/completion.js'
import {
replaceWord,
addPositionToWords,
wordAtPosition,
splitByWhitespaceBoundary,
} from '../../../../../src/services/completion/completion.js'
describe('addPositiontoWords', () => {
it('adds the position to a word list', () => {
@ -8,38 +13,38 @@ describe('addPositiontoWords', () => {
{
word: 'hey',
start: 0,
end: 3
end: 3,
},
{
word: ' ',
start: 3,
end: 4
end: 4,
},
{
word: 'this',
start: 4,
end: 8
end: 8,
},
{
word: ' ',
start: 8,
end: 9
end: 9,
},
{
word: 'is',
start: 9,
end: 11
end: 11,
},
{
word: ' ',
start: 11,
end: 12
end: 12,
},
{
word: 'fun',
start: 12,
end: 15
}
end: 15,
},
]
const res = addPositionToWords(words)
@ -51,7 +56,23 @@ describe('addPositiontoWords', () => {
describe('splitByWhitespaceBoundary', () => {
it('splits at whitespace boundaries', () => {
const str = 'This is a #nice @test for you, @idiot@idiot.com'
const expected = ['This', ' ', 'is', ' ', 'a', ' ', '#nice', ' ', '@test', ' ', 'for', ' ', 'you,', ' ', '@idiot@idiot.com']
const expected = [
'This',
' ',
'is',
' ',
'a',
' ',
'#nice',
' ',
'@test',
' ',
'for',
' ',
'you,',
' ',
'@idiot@idiot.com',
]
const res = splitByWhitespaceBoundary(str)
expect(res).to.eql(expected)

View file

@ -1,43 +1,68 @@
import * as DateUtils from 'src/services/date_utils/date_utils.js'
beforeEach(() => { vi.useFakeTimers() })
afterEach(() => { vi.useRealTimers() })
beforeEach(() => {
vi.useFakeTimers()
})
afterEach(() => {
vi.useRealTimers()
})
describe('DateUtils', () => {
describe('relativeTime', () => {
it('returns now with low enough amount of seconds', () => {
const futureTime = Date.now() + 20 * DateUtils.SECOND
const pastTime = Date.now() - 20 * DateUtils.SECOND
expect(DateUtils.relativeTime(futureTime, 30)).to.eql({ num: 0, key: 'time.now' })
expect(DateUtils.relativeTime(pastTime, 30)).to.eql({ num: 0, key: 'time.now' })
expect(DateUtils.relativeTime(futureTime, 30)).to.eql({
num: 0,
key: 'time.now',
})
expect(DateUtils.relativeTime(pastTime, 30)).to.eql({
num: 0,
key: 'time.now',
})
})
it('rounds down for past', () => {
const time = Date.now() - 1.8 * DateUtils.HOUR
expect(DateUtils.relativeTime(time)).to.eql({ num: 1, key: 'time.unit.hours' })
expect(DateUtils.relativeTime(time)).to.eql({
num: 1,
key: 'time.unit.hours',
})
})
it('rounds up for future', () => {
const time = Date.now() + 1.8 * DateUtils.HOUR
expect(DateUtils.relativeTime(time)).to.eql({ num: 2, key: 'time.unit.hours' })
expect(DateUtils.relativeTime(time)).to.eql({
num: 2,
key: 'time.unit.hours',
})
})
it('uses plural when necessary', () => {
const time = Date.now() - 3.8 * DateUtils.WEEK
expect(DateUtils.relativeTime(time)).to.eql({ num: 3, key: 'time.unit.weeks' })
expect(DateUtils.relativeTime(time)).to.eql({
num: 3,
key: 'time.unit.weeks',
})
})
it('works with date string', () => {
const time = Date.now() - 4 * DateUtils.MONTH
const dateString = new Date(time).toISOString()
expect(DateUtils.relativeTime(dateString)).to.eql({ num: 4, key: 'time.unit.months' })
expect(DateUtils.relativeTime(dateString)).to.eql({
num: 4,
key: 'time.unit.months',
})
})
})
describe('relativeTimeShort', () => {
it('returns the short version of the same relative time', () => {
const time = Date.now() + 2 * DateUtils.YEAR
expect(DateUtils.relativeTimeShort(time)).to.eql({ num: 2, key: 'time.unit.years_short' })
expect(DateUtils.relativeTimeShort(time)).to.eql({
num: 2,
key: 'time.unit.years_short',
})
})
})
})

View file

@ -1,171 +1,196 @@
import { parseStatus, parseUser, parseNotification, parseLinkHeaderPagination } from '../../../../../src/services/entity_normalizer/entity_normalizer.service.js'
import {
parseStatus,
parseUser,
parseNotification,
parseLinkHeaderPagination,
} from '../../../../../src/services/entity_normalizer/entity_normalizer.service.js'
import mastoapidata from '../../../../fixtures/mastoapi.json'
import qvitterapidata from '../../../../fixtures/statuses.json'
const makeMockStatusQvitter = (overrides = {}) => {
return Object.assign({
activity_type: 'post',
attachments: [],
attentions: [],
created_at: 'Tue Jan 15 13:57:56 +0000 2019',
external_url: 'https://ap.example/whatever',
fave_num: 1,
favorited: false,
id: '10335970',
in_reply_to_ostatus_uri: null,
in_reply_to_profileurl: null,
in_reply_to_screen_name: null,
in_reply_to_status_id: null,
in_reply_to_user_id: null,
is_local: false,
is_post_verb: true,
possibly_sensitive: false,
repeat_num: 0,
repeated: false,
statusnet_conversation_id: '16300488',
summary: null,
tags: [],
text: 'haha benis',
uri: 'https://ap.example/whatever',
user: makeMockUserQvitter(),
visibility: 'public'
}, overrides)
return Object.assign(
{
activity_type: 'post',
attachments: [],
attentions: [],
created_at: 'Tue Jan 15 13:57:56 +0000 2019',
external_url: 'https://ap.example/whatever',
fave_num: 1,
favorited: false,
id: '10335970',
in_reply_to_ostatus_uri: null,
in_reply_to_profileurl: null,
in_reply_to_screen_name: null,
in_reply_to_status_id: null,
in_reply_to_user_id: null,
is_local: false,
is_post_verb: true,
possibly_sensitive: false,
repeat_num: 0,
repeated: false,
statusnet_conversation_id: '16300488',
summary: null,
tags: [],
text: 'haha benis',
uri: 'https://ap.example/whatever',
user: makeMockUserQvitter(),
visibility: 'public',
},
overrides,
)
}
const makeMockUserQvitter = (overrides = {}) => {
return Object.assign({
background_image: null,
cover_photo: '',
created_at: 'Mon Jan 14 13:56:51 +0000 2019',
default_scope: 'public',
description: 'ebin',
description_html: '<p>ebin</p>',
favourites_count: 0,
fields: [],
followers_count: 1,
following: true,
follows_you: true,
friends_count: 1,
id: '60717',
is_local: false,
locked: false,
name: 'Spurdo :ebin:',
name_html: 'Spurdo <img src="whatever">',
no_rich_text: false,
pleroma: { confirmation_pending: false, tags: [] },
profile_image_url: 'https://ap.example/whatever',
profile_image_url_https: 'https://ap.example/whatever',
profile_image_url_original: 'https://ap.example/whatever',
profile_image_url_profile_size: 'https://ap.example/whatever',
rights: { delete_others_notice: false },
screen_name: 'spurdo@ap.example',
statuses_count: 46,
statusnet_blocking: false,
statusnet_profile_url: ''
}, overrides)
return Object.assign(
{
background_image: null,
cover_photo: '',
created_at: 'Mon Jan 14 13:56:51 +0000 2019',
default_scope: 'public',
description: 'ebin',
description_html: '<p>ebin</p>',
favourites_count: 0,
fields: [],
followers_count: 1,
following: true,
follows_you: true,
friends_count: 1,
id: '60717',
is_local: false,
locked: false,
name: 'Spurdo :ebin:',
name_html: 'Spurdo <img src="whatever">',
no_rich_text: false,
pleroma: { confirmation_pending: false, tags: [] },
profile_image_url: 'https://ap.example/whatever',
profile_image_url_https: 'https://ap.example/whatever',
profile_image_url_original: 'https://ap.example/whatever',
profile_image_url_profile_size: 'https://ap.example/whatever',
rights: { delete_others_notice: false },
screen_name: 'spurdo@ap.example',
statuses_count: 46,
statusnet_blocking: false,
statusnet_profile_url: '',
},
overrides,
)
}
const makeMockUserMasto = (overrides = {}) => {
return Object.assign({
acct: 'hj',
avatar:
'https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png',
avatar_static:
'https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png',
bot: false,
created_at: '2017-12-17T21:54:14.000Z',
display_name: 'whatever whatever whatever witch',
emojis: [],
fields: [],
followers_count: 705,
following_count: 326,
header:
'https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png',
header_static:
'https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png',
id: '1',
locked: false,
note:
'Volatile Internet Weirdo. Name pronounced as Hee Jay. JS and Java dark arts mage, Elixir trainee. I love sampo and lain. Matrix is <span><a data-user="1" href="https://shigusegubu.club/users/hj">@<span>hj</span></a></span>:matrix.heldscal.la Pronouns are whatever. Do not DM me unless it\'s truly private matter and you\'re instance\'s admin or you risk your DM to be reposted publicly.Wish i was Finnish girl.',
pleroma: { confirmation_pending: false, tags: null },
source: { note: '', privacy: 'public', sensitive: false },
statuses_count: 41775,
url: 'https://shigusegubu.club/users/hj',
username: 'hj'
}, overrides)
return Object.assign(
{
acct: 'hj',
avatar:
'https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png',
avatar_static:
'https://shigusegubu.club/media/1657b945-8d5b-4ce6-aafb-4c3fc5772120/8ce851029af84d55de9164e30cc7f46d60cbf12eee7e96c5c0d35d9038ddade1.png',
bot: false,
created_at: '2017-12-17T21:54:14.000Z',
display_name: 'whatever whatever whatever witch',
emojis: [],
fields: [],
followers_count: 705,
following_count: 326,
header:
'https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png',
header_static:
'https://shigusegubu.club/media/7ab024d9-2a8a-4fbc-9ce8-da06756ae2db/6aadefe4e264133bc377ab450e6b045b6f5458542a5c59e6c741f86107f0388b.png',
id: '1',
locked: false,
note: 'Volatile Internet Weirdo. Name pronounced as Hee Jay. JS and Java dark arts mage, Elixir trainee. I love sampo and lain. Matrix is <span><a data-user="1" href="https://shigusegubu.club/users/hj">@<span>hj</span></a></span>:matrix.heldscal.la Pronouns are whatever. Do not DM me unless it\'s truly private matter and you\'re instance\'s admin or you risk your DM to be reposted publicly.Wish i was Finnish girl.',
pleroma: { confirmation_pending: false, tags: null },
source: { note: '', privacy: 'public', sensitive: false },
statuses_count: 41775,
url: 'https://shigusegubu.club/users/hj',
username: 'hj',
},
overrides,
)
}
const makeMockStatusMasto = (overrides = {}) => {
return Object.assign({
account: makeMockUserMasto(),
application: { name: 'Web', website: null },
content:
'<span><a data-user="14660" href="https://pleroma.soykaf.com/users/sampo">@<span>sampo</span></a></span> god i wish i was there',
created_at: '2019-01-17T16:29:23.000Z',
emojis: [],
favourited: false,
favourites_count: 1,
id: '10423476',
in_reply_to_account_id: '14660',
in_reply_to_id: '10423197',
language: null,
media_attachments: [],
mentions: [
{
acct: 'sampo@pleroma.soykaf.com',
id: '14660',
url: 'https://pleroma.soykaf.com/users/sampo',
username: 'sampo'
}
],
muted: false,
reblog: null,
reblogged: false,
reblogs_count: 0,
replies_count: 0,
sensitive: false,
spoiler_text: '',
tags: [],
uri: 'https://shigusegubu.club/objects/16033fbb-97c0-4f0e-b834-7abb92fb8639',
url: 'https://shigusegubu.club/objects/16033fbb-97c0-4f0e-b834-7abb92fb8639',
visibility: 'public',
pleroma: {
local: true
}
}, overrides)
return Object.assign(
{
account: makeMockUserMasto(),
application: { name: 'Web', website: null },
content:
'<span><a data-user="14660" href="https://pleroma.soykaf.com/users/sampo">@<span>sampo</span></a></span> god i wish i was there',
created_at: '2019-01-17T16:29:23.000Z',
emojis: [],
favourited: false,
favourites_count: 1,
id: '10423476',
in_reply_to_account_id: '14660',
in_reply_to_id: '10423197',
language: null,
media_attachments: [],
mentions: [
{
acct: 'sampo@pleroma.soykaf.com',
id: '14660',
url: 'https://pleroma.soykaf.com/users/sampo',
username: 'sampo',
},
],
muted: false,
reblog: null,
reblogged: false,
reblogs_count: 0,
replies_count: 0,
sensitive: false,
spoiler_text: '',
tags: [],
uri: 'https://shigusegubu.club/objects/16033fbb-97c0-4f0e-b834-7abb92fb8639',
url: 'https://shigusegubu.club/objects/16033fbb-97c0-4f0e-b834-7abb92fb8639',
visibility: 'public',
pleroma: {
local: true,
},
},
overrides,
)
}
const makeMockNotificationQvitter = (overrides = {}) => {
return Object.assign({
notice: makeMockStatusQvitter(),
ntype: 'follow',
from_profile: makeMockUserQvitter(),
is_seen: 0,
id: 123
}, overrides)
return Object.assign(
{
notice: makeMockStatusQvitter(),
ntype: 'follow',
from_profile: makeMockUserQvitter(),
is_seen: 0,
id: 123,
},
overrides,
)
}
const makeMockEmojiMasto = (overrides = [{}]) => {
return [
Object.assign({
shortcode: 'image',
static_url: 'https://example.com/image.png',
url: 'https://example.com/image.png',
visible_in_picker: false
}, overrides[0]),
Object.assign({
shortcode: 'thinking',
static_url: 'https://example.com/think.png',
url: 'https://example.com/think.png',
visible_in_picker: false
}, overrides[1])
Object.assign(
{
shortcode: 'image',
static_url: 'https://example.com/image.png',
url: 'https://example.com/image.png',
visible_in_picker: false,
},
overrides[0],
),
Object.assign(
{
shortcode: 'thinking',
static_url: 'https://example.com/think.png',
url: 'https://example.com/think.png',
visible_in_picker: false,
},
overrides[1],
),
]
}
describe('API Entities normalizer', () => {
describe('parseStatus', () => {
describe('QVitter preprocessing', () => {
it('doesn\'t blow up', () => {
it("doesn't blow up", () => {
const parsed = qvitterapidata.map(parseStatus)
expect(parsed.length).to.eq(qvitterapidata.length)
})
@ -173,21 +198,34 @@ describe('API Entities normalizer', () => {
it('identifies favorites', () => {
const fav = {
uri: 'tag:soykaf.com,2016-08-21:fave:2558:note:339495:2016-08-21T16:54:04+00:00',
is_post_verb: false
is_post_verb: false,
}
const mastoFav = {
uri: 'tag:mastodon.social,2016-11-27:objectId=73903:objectType=Favourite',
is_post_verb: false
is_post_verb: false,
}
expect(parseStatus(makeMockStatusQvitter(fav))).to.have.property('type', 'favorite')
expect(parseStatus(makeMockStatusQvitter(mastoFav))).to.have.property('type', 'favorite')
expect(parseStatus(makeMockStatusQvitter(fav))).to.have.property(
'type',
'favorite',
)
expect(parseStatus(makeMockStatusQvitter(mastoFav))).to.have.property(
'type',
'favorite',
)
})
it('processes repeats correctly', () => {
const post = makeMockStatusQvitter({ retweeted_status: null, id: 'deadbeef' })
const repeat = makeMockStatusQvitter({ retweeted_status: post, is_post_verb: false, id: 'foobar' })
const post = makeMockStatusQvitter({
retweeted_status: null,
id: 'deadbeef',
})
const repeat = makeMockStatusQvitter({
retweeted_status: post,
is_post_verb: false,
id: 'foobar',
})
const parsedPost = parseStatus(post)
const parsedRepeat = parseStatus(repeat)
@ -195,26 +233,36 @@ describe('API Entities normalizer', () => {
expect(parsedPost).to.have.property('type', 'status')
expect(parsedRepeat).to.have.property('type', 'retweet')
expect(parsedRepeat).to.have.property('retweeted_status')
expect(parsedRepeat).to.have.nested.property('retweeted_status.id', 'deadbeef')
expect(parsedRepeat).to.have.nested.property(
'retweeted_status.id',
'deadbeef',
)
})
it('sets nsfw for statuses with the #nsfw tag', () => {
const safe = makeMockStatusQvitter({ id: '1', text: 'Hello oniichan' })
const nsfw = makeMockStatusQvitter({ id: '1', text: 'Hello oniichan #nsfw' })
const nsfw = makeMockStatusQvitter({
id: '1',
text: 'Hello oniichan #nsfw',
})
expect(parseStatus(safe).nsfw).to.eq(false)
expect(parseStatus(nsfw).nsfw).to.eq(true)
})
it('leaves existing nsfw settings alone', () => {
const nsfw = makeMockStatusQvitter({ id: '1', text: 'Hello oniichan #nsfw', nsfw: false })
const nsfw = makeMockStatusQvitter({
id: '1',
text: 'Hello oniichan #nsfw',
nsfw: false,
})
expect(parseStatus(nsfw).nsfw).to.eq(false)
})
})
describe('Mastoapi preprocessing and converting', () => {
it('doesn\'t blow up', () => {
it("doesn't blow up", () => {
const parsed = mastoapidata.map(parseStatus)
expect(parsed.length).to.eq(mastoapidata.length)
})
@ -229,7 +277,10 @@ describe('API Entities normalizer', () => {
expect(parsedPost).to.have.property('type', 'status')
expect(parsedRepeat).to.have.property('type', 'retweet')
expect(parsedRepeat).to.have.property('retweeted_status')
expect(parsedRepeat).to.have.nested.property('retweeted_status.id', 'deadbeef')
expect(parsedRepeat).to.have.nested.property(
'retweeted_status.id',
'deadbeef',
)
})
})
})
@ -245,7 +296,15 @@ describe('API Entities normalizer', () => {
})
it('removes html tags from user profile fields', () => {
const user = makeMockUserMasto({ emojis: makeMockEmojiMasto(), fields: [{ name: 'user', value: '<a rel="me" href="https://example.com/@user">@user</a>' }] })
const user = makeMockUserMasto({
emojis: makeMockEmojiMasto(),
fields: [
{
name: 'user',
value: '<a rel="me" href="https://example.com/@user">@user</a>',
},
],
})
const parsedUser = parseUser(user)
@ -258,7 +317,14 @@ describe('API Entities normalizer', () => {
})
it('adds hide_follows and hide_followers user settings', () => {
const user = makeMockUserMasto({ pleroma: { hide_followers: true, hide_follows: false, hide_followers_count: false, hide_follows_count: true } })
const user = makeMockUserMasto({
pleroma: {
hide_followers: true,
hide_follows: false,
hide_followers_count: false,
hide_follows_count: true,
},
})
expect(parseUser(user)).to.have.property('hide_followers', true)
expect(parseUser(user)).to.have.property('hide_follows', false)
@ -269,25 +335,38 @@ describe('API Entities normalizer', () => {
it('converts IDN to unicode and marks it as internatonal', () => {
const user = makeMockUserMasto({ acct: 'lain@xn--lin-6cd.com' })
expect(parseUser(user)).to.have.property('screen_name_ui').that.equal('lain@lаin.com')
expect(parseUser(user)).to.have.property('screen_name_ui_contains_non_ascii').that.equal(true)
expect(parseUser(user))
.to.have.property('screen_name_ui')
.that.equal('lain@lаin.com')
expect(parseUser(user))
.to.have.property('screen_name_ui_contains_non_ascii')
.that.equal(true)
})
})
// We currently use QvitterAPI notifications only, and especially due to MastoAPI lacking is_seen, support for MastoAPI
// is more of an afterthought
describe('parseNotifications (QvitterAPI)', () => {
it('correctly normalizes data to FE\'s format', () => {
it("correctly normalizes data to FE's format", () => {
const notif = makeMockNotificationQvitter({
id: 123,
notice: makeMockStatusQvitter({ id: 444 }),
from_profile: makeMockUserQvitter({ id: 'spurdo' })
from_profile: makeMockUserQvitter({ id: 'spurdo' }),
})
expect(parseNotification(notif)).to.have.property('id', 123)
expect(parseNotification(notif)).to.have.property('seen', false)
expect(parseNotification(notif)).to.have.nested.property('status.id', '444')
expect(parseNotification(notif)).to.have.nested.property('action.id', '444')
expect(parseNotification(notif)).to.have.nested.property('from_profile.id', 'spurdo')
expect(parseNotification(notif)).to.have.nested.property(
'status.id',
'444',
)
expect(parseNotification(notif)).to.have.nested.property(
'action.id',
'444',
)
expect(parseNotification(notif)).to.have.nested.property(
'from_profile.id',
'spurdo',
)
})
it('correctly normalizes favorite notifications', () => {
@ -296,36 +375,47 @@ describe('API Entities normalizer', () => {
ntype: 'like',
notice: makeMockStatusQvitter({
id: 444,
favorited_status: makeMockStatusQvitter({ id: 4412 })
favorited_status: makeMockStatusQvitter({ id: 4412 }),
}),
is_seen: 1,
from_profile: makeMockUserQvitter({ id: 'spurdo' })
from_profile: makeMockUserQvitter({ id: 'spurdo' }),
})
expect(parseNotification(notif)).to.have.property('id', 123)
expect(parseNotification(notif)).to.have.property('type', 'like')
expect(parseNotification(notif)).to.have.property('seen', true)
expect(parseNotification(notif)).to.have.nested.property('status.id', '4412')
expect(parseNotification(notif)).to.have.nested.property('action.id', '444')
expect(parseNotification(notif)).to.have.nested.property('from_profile.id', 'spurdo')
expect(parseNotification(notif)).to.have.nested.property(
'status.id',
'4412',
)
expect(parseNotification(notif)).to.have.nested.property(
'action.id',
'444',
)
expect(parseNotification(notif)).to.have.nested.property(
'from_profile.id',
'spurdo',
)
})
})
describe('Link header pagination', () => {
it('Parses min and max ids as integers', () => {
const linkHeader = '<https://example.com/api/v1/notifications?max_id=861676>; rel="next", <https://example.com/api/v1/notifications?min_id=861741>; rel="prev"'
const linkHeader =
'<https://example.com/api/v1/notifications?max_id=861676>; rel="next", <https://example.com/api/v1/notifications?min_id=861741>; rel="prev"'
const result = parseLinkHeaderPagination(linkHeader)
expect(result).to.eql({
maxId: 861676,
minId: 861741
minId: 861741,
})
})
it('Parses min and max ids as flakes', () => {
const linkHeader = '<http://example.com/api/v1/timelines/home?max_id=9waQx5IIS48qVue2Ai>; rel="next", <http://example.com/api/v1/timelines/home?min_id=9wi61nIPnfn674xgie>; rel="prev"'
const linkHeader =
'<http://example.com/api/v1/timelines/home?max_id=9waQx5IIS48qVue2Ai>; rel="next", <http://example.com/api/v1/timelines/home?min_id=9wi61nIPnfn674xgie>; rel="prev"'
const result = parseLinkHeaderPagination(linkHeader, { flakeId: true })
expect(result).to.eql({
maxId: '9waQx5IIS48qVue2Ai',
minId: '9wi61nIPnfn674xgie'
minId: '9wi61nIPnfn674xgie',
})
})
})

View file

@ -5,24 +5,24 @@ describe('fileSizeFormat', () => {
const expected = [
{
num: 1,
unit: 'B'
unit: 'B',
},
{
num: 1,
unit: 'KiB'
unit: 'KiB',
},
{
num: 1,
unit: 'MiB'
unit: 'MiB',
},
{
num: 1,
unit: 'GiB'
unit: 'GiB',
},
{
num: 1,
unit: 'TiB'
}
unit: 'TiB',
},
]
const res = []

View file

@ -4,19 +4,21 @@ const mockTouchEvent = (x, y) => ({
touches: [
{
screenX: x,
screenY: y
}
]
screenY: y,
},
],
})
describe('GestureService', () => {
describe('swipeGesture', () => {
it('calls the callback on a successful swipe', () => {
let swiped = false
const callback = () => { swiped = true }
const callback = () => {
swiped = true
}
const gesture = GestureService.swipeGesture(
GestureService.DIRECTION_RIGHT,
callback
callback,
)
GestureService.beginSwipe(mockTouchEvent(100, 100), gesture)
@ -27,10 +29,12 @@ describe('GestureService', () => {
it('calls the callback only once per begin', () => {
let hits = 0
const callback = () => { hits += 1 }
const callback = () => {
hits += 1
}
const gesture = GestureService.swipeGesture(
GestureService.DIRECTION_RIGHT,
callback
callback,
)
GestureService.beginSwipe(mockTouchEvent(100, 100), gesture)
@ -40,12 +44,14 @@ describe('GestureService', () => {
expect(hits).to.eql(1)
})
it('doesn\'t call the callback on an opposite swipe', () => {
it("doesn't call the callback on an opposite swipe", () => {
let swiped = false
const callback = () => { swiped = true }
const callback = () => {
swiped = true
}
const gesture = GestureService.swipeGesture(
GestureService.DIRECTION_RIGHT,
callback
callback,
)
GestureService.beginSwipe(mockTouchEvent(100, 100), gesture)
@ -54,13 +60,15 @@ describe('GestureService', () => {
expect(swiped).to.eql(false)
})
it('doesn\'t call the callback on a swipe below threshold', () => {
it("doesn't call the callback on a swipe below threshold", () => {
let swiped = false
const callback = () => { swiped = true }
const callback = () => {
swiped = true
}
const gesture = GestureService.swipeGesture(
GestureService.DIRECTION_RIGHT,
callback,
100
100,
)
GestureService.beginSwipe(mockTouchEvent(100, 100), gesture)
@ -69,14 +77,16 @@ describe('GestureService', () => {
expect(swiped).to.eql(false)
})
it('doesn\'t call the callback on a perpendicular swipe', () => {
it("doesn't call the callback on a perpendicular swipe", () => {
let swiped = false
const callback = () => { swiped = true }
const callback = () => {
swiped = true
}
const gesture = GestureService.swipeGesture(
GestureService.DIRECTION_RIGHT,
callback,
30,
0.5
0.5,
)
GestureService.beginSwipe(mockTouchEvent(100, 100), gesture)
@ -87,12 +97,14 @@ describe('GestureService', () => {
it('calls the callback on perpendicular swipe if within tolerance', () => {
let swiped = false
const callback = () => { swiped = true }
const callback = () => {
swiped = true
}
const gesture = GestureService.swipeGesture(
GestureService.DIRECTION_RIGHT,
callback,
30,
2.0
2.0,
)
GestureService.beginSwipe(mockTouchEvent(100, 100), gesture)
@ -103,13 +115,10 @@ describe('GestureService', () => {
it('works with any arbitrary 2d directions', () => {
let swiped = false
const callback = () => { swiped = true }
const gesture = GestureService.swipeGesture(
[-1, -1],
callback,
30,
0.1
)
const callback = () => {
swiped = true
}
const gesture = GestureService.swipeGesture([-1, -1], callback, 30, 0.1)
GestureService.beginSwipe(mockTouchEvent(100, 100), gesture)
GestureService.updateSwipe(mockTouchEvent(60, 60), gesture)

View file

@ -2,7 +2,7 @@ import { convertHtmlToLines } from 'src/services/html_converter/html_line_conver
const greentextHandle = new Set(['p', 'div'])
const mapOnlyText = (processor) => (input) => {
if (input.text && input.level.every(l => greentextHandle.has(l))) {
if (input.text && input.level.every((l) => greentextHandle.has(l))) {
return processor(input.text)
} else if (input.text) {
return input.text
@ -15,7 +15,8 @@ describe('html_line_converter', () => {
describe('with processor that keeps original line should not make any changes to HTML when', () => {
const processorKeep = (line) => line
it('fed with regular HTML with newlines', () => {
const inputOutput = '1<br/>2<p class="lol">3 4</p> 5 \n 6 <p > 7 <br> 8 </p> <br>\n<br/>'
const inputOutput =
'1<br/>2<p class="lol">3 4</p> 5 \n 6 <p > 7 <br> 8 </p> <br>\n<br/>'
const result = convertHtmlToLines(inputOutput)
const comparableResult = result.map(mapOnlyText(processorKeep)).join('')
expect(comparableResult).to.eql(inputOutput)
@ -35,14 +36,14 @@ describe('html_line_converter', () => {
expect(comparableResult).to.eql(inputOutput)
})
it('fed with sorta valid HTML but tags aren\'t closed', () => {
it("fed with sorta valid HTML but tags aren't closed", () => {
const inputOutput = 'just leaving a <div> hanging'
const result = convertHtmlToLines(inputOutput)
const comparableResult = result.map(mapOnlyText(processorKeep)).join('')
expect(comparableResult).to.eql(inputOutput)
})
it('fed with not really HTML at this point... tags that aren\'t finished', () => {
it("fed with not really HTML at this point... tags that aren't finished", () => {
const inputOutput = 'do you expect me to finish this <div class='
const result = convertHtmlToLines(inputOutput)
const comparableResult = result.map(mapOnlyText(processorKeep)).join('')
@ -50,14 +51,16 @@ describe('html_line_converter', () => {
})
it('fed with dubiously valid HTML (p within p and also div inside p)', () => {
const inputOutput = 'look ma <p> p \nwithin <p> p! </p> and a <br/><div>div!</div></p>'
const inputOutput =
'look ma <p> p \nwithin <p> p! </p> and a <br/><div>div!</div></p>'
const result = convertHtmlToLines(inputOutput)
const comparableResult = result.map(mapOnlyText(processorKeep)).join('')
expect(comparableResult).to.eql(inputOutput)
})
it('fed with maybe valid HTML? self-closing divs and ps', () => {
const inputOutput = 'a <div class="what"/> what now <p aria-label="wtf"/> ?'
const inputOutput =
'a <div class="what"/> what now <p aria-label="wtf"/> ?'
const result = convertHtmlToLines(inputOutput)
const comparableResult = result.map(mapOnlyText(processorKeep)).join('')
expect(comparableResult).to.eql(inputOutput)
@ -80,10 +83,13 @@ describe('html_line_converter', () => {
describe('with processor that replaces lines with word "_" should match expected line when', () => {
const processorReplace = () => '_'
it('fed with regular HTML with newlines', () => {
const input = '1<br/>2<p class="lol">3 4</p> 5 \n 6 <p > 7 <br> 8 </p> <br>\n<br/>'
const input =
'1<br/>2<p class="lol">3 4</p> 5 \n 6 <p > 7 <br> 8 </p> <br>\n<br/>'
const output = '_<br/>_<p class="lol">_</p>_\n_<p >_<br>_</p> <br>\n<br/>'
const result = convertHtmlToLines(input)
const comparableResult = result.map(mapOnlyText(processorReplace)).join('')
const comparableResult = result
.map(mapOnlyText(processorReplace))
.join('')
expect(comparableResult).to.eql(output)
})
@ -91,7 +97,9 @@ describe('html_line_converter', () => {
const input = '<feeee dwdwddddddw> <i>ayy<b>lm</i>ao</b> </section>'
const output = '_'
const result = convertHtmlToLines(input)
const comparableResult = result.map(mapOnlyText(processorReplace)).join('')
const comparableResult = result
.map(mapOnlyText(processorReplace))
.join('')
expect(comparableResult).to.eql(output)
})
@ -99,31 +107,40 @@ describe('html_line_converter', () => {
const input = '</p> lmao what </div> whats going on <div> wha <p>'
const output = '_<div>_<p>'
const result = convertHtmlToLines(input)
const comparableResult = result.map(mapOnlyText(processorReplace)).join('')
const comparableResult = result
.map(mapOnlyText(processorReplace))
.join('')
expect(comparableResult).to.eql(output)
})
it('fed with sorta valid HTML but tags aren\'t closed', () => {
it("fed with sorta valid HTML but tags aren't closed", () => {
const input = 'just leaving a <div> hanging'
const output = '_<div>_'
const result = convertHtmlToLines(input)
const comparableResult = result.map(mapOnlyText(processorReplace)).join('')
const comparableResult = result
.map(mapOnlyText(processorReplace))
.join('')
expect(comparableResult).to.eql(output)
})
it('fed with not really HTML at this point... tags that aren\'t finished', () => {
it("fed with not really HTML at this point... tags that aren't finished", () => {
const input = 'do you expect me to finish this <div class='
const output = '_'
const result = convertHtmlToLines(input)
const comparableResult = result.map(mapOnlyText(processorReplace)).join('')
const comparableResult = result
.map(mapOnlyText(processorReplace))
.join('')
expect(comparableResult).to.eql(output)
})
it('fed with dubiously valid HTML (p within p and also div inside p)', () => {
const input = 'look ma <p> p \nwithin <p> p! </p> and a <br/><div>div!</div></p>'
const input =
'look ma <p> p \nwithin <p> p! </p> and a <br/><div>div!</div></p>'
const output = '_<p>_\n_<p>_</p>_<br/><div>_</div></p>'
const result = convertHtmlToLines(input)
const comparableResult = result.map(mapOnlyText(processorReplace)).join('')
const comparableResult = result
.map(mapOnlyText(processorReplace))
.join('')
expect(comparableResult).to.eql(output)
})
@ -131,7 +148,9 @@ describe('html_line_converter', () => {
const input = 'a <div class="what"/> what now <p aria-label="wtf"/> ?'
const output = '_<div class="what"/>_<p aria-label="wtf"/>_'
const result = convertHtmlToLines(input)
const comparableResult = result.map(mapOnlyText(processorReplace)).join('')
const comparableResult = result
.map(mapOnlyText(processorReplace))
.join('')
expect(comparableResult).to.eql(output)
})
@ -139,7 +158,9 @@ describe('html_line_converter', () => {
const input = 'Yes, it is me, <![CDATA[DIO]]>'
const output = '_'
const result = convertHtmlToLines(input)
const comparableResult = result.map(mapOnlyText(processorReplace)).join('')
const comparableResult = result
.map(mapOnlyText(processorReplace))
.join('')
expect(comparableResult).to.eql(output)
})
@ -153,7 +174,9 @@ describe('html_line_converter', () => {
false</code></pre><blockquote>That, christian-like JS diagram but its evangelion instead.</blockquote>
`
const result = convertHtmlToLines(input)
const comparableResult = result.map(mapOnlyText(processorReplace)).join('')
const comparableResult = result
.map(mapOnlyText(processorReplace))
.join('')
expect(comparableResult).to.eql(input)
})
it('Testing handling ignored blocks 2', () => {
@ -164,7 +187,9 @@ describe('html_line_converter', () => {
<blockquote>An SSL error has happened.</blockquote><p>_</p>
`
const result = convertHtmlToLines(input)
const comparableResult = result.map(mapOnlyText(processorReplace)).join('')
const comparableResult = result
.map(mapOnlyText(processorReplace))
.join('')
expect(comparableResult).to.eql(output)
})
})

View file

@ -6,43 +6,19 @@ describe('html_tree_converter', () => {
const input = '1 <p>2</p> <b>3<img src="a">4</b>5'
expect(convertHtmlToTree(input)).to.eql([
'1 ',
[
'<p>',
['2'],
'</p>'
],
['<p>', ['2'], '</p>'],
' ',
[
'<b>',
[
'3',
['<img src="a">'],
'4'
],
'</b>'
],
'5'
['<b>', ['3', ['<img src="a">'], '4'], '</b>'],
'5',
])
})
it('converts html to tree while preserving tag formatting', () => {
const input = '1 <p >2</p><b >3<img src="a">4</b>5'
expect(convertHtmlToTree(input)).to.eql([
'1 ',
[
'<p >',
['2'],
'</p>'
],
[
'<b >',
[
'3',
['<img src="a">'],
'4'
],
'</b>'
],
'5'
['<p >', ['2'], '</p>'],
['<b >', ['3', ['<img src="a">'], '4'], '</b>'],
'5',
])
})
it('converts semi-broken html', () => {
@ -51,14 +27,12 @@ describe('html_tree_converter', () => {
'1 ',
['<br>'],
' 2 ',
[
'<p>',
[' 42']
]
['<p>', [' 42']],
])
})
it('realistic case 1', () => {
const input = '<p><span class="h-card"><a class="u-url mention" data-user="9wRC6T2ZZiKWJ0vUi8" href="https://cawfee.club/users/benis" rel="ugc">@<span>benis</span></a></span> <span class="h-card"><a class="u-url mention" data-user="194" href="https://shigusegubu.club/users/hj" rel="ugc">@<span>hj</span></a></span> nice</p>'
const input =
'<p><span class="h-card"><a class="u-url mention" data-user="9wRC6T2ZZiKWJ0vUi8" href="https://cawfee.club/users/benis" rel="ugc">@<span>benis</span></a></span> <span class="h-card"><a class="u-url mention" data-user="194" href="https://shigusegubu.club/users/hj" rel="ugc">@<span>hj</span></a></span> nice</p>'
expect(convertHtmlToTree(input)).to.eql([
[
'<p>',
@ -68,20 +42,11 @@ describe('html_tree_converter', () => {
[
[
'<a class="u-url mention" data-user="9wRC6T2ZZiKWJ0vUi8" href="https://cawfee.club/users/benis" rel="ugc">',
[
'@',
[
'<span>',
[
'benis'
],
'</span>'
]
],
'</a>'
]
['@', ['<span>', ['benis'], '</span>']],
'</a>',
],
],
'</span>'
'</span>',
],
' ',
[
@ -89,43 +54,29 @@ describe('html_tree_converter', () => {
[
[
'<a class="u-url mention" data-user="194" href="https://shigusegubu.club/users/hj" rel="ugc">',
[
'@',
[
'<span>',
[
'hj'
],
'</span>'
]
],
'</a>'
]
['@', ['<span>', ['hj'], '</span>']],
'</a>',
],
],
'</span>'
'</span>',
],
' nice'
' nice',
],
'</p>'
]
'</p>',
],
])
})
it('realistic case 2', () => {
const inputOutput = 'Country improv: give me a city<br/>Audience: Memphis<br/>Improv troupe: come on, a better one<br/>Audience: el paso'
const inputOutput =
'Country improv: give me a city<br/>Audience: Memphis<br/>Improv troupe: come on, a better one<br/>Audience: el paso'
expect(convertHtmlToTree(inputOutput)).to.eql([
'Country improv: give me a city',
[
'<br/>'
],
['<br/>'],
'Audience: Memphis',
[
'<br/>'
],
['<br/>'],
'Improv troupe: come on, a better one',
[
'<br/>'
],
'Audience: el paso'
['<br/>'],
'Audience: el paso',
])
})
})

View file

@ -1,4 +1,7 @@
import { processTextForEmoji, getAttrs } from 'src/services/html_converter/utility.service.js'
import {
processTextForEmoji,
getAttrs,
} from 'src/services/html_converter/utility.service.js'
describe('html_converter utility', () => {
describe('processTextForEmoji', () => {
@ -6,22 +9,22 @@ describe('html_converter utility', () => {
const input = 'Hello from finland! :lol: We have best water! :lmao:'
const emojis = [
{ shortcode: 'lol', src: 'LOL' },
{ shortcode: 'lmao', src: 'LMAO' }
{ shortcode: 'lmao', src: 'LMAO' },
]
const processor = ({ shortcode, src }) => ({ shortcode, src })
expect(processTextForEmoji(input, emojis, processor)).to.eql([
'Hello from finland! ',
{ shortcode: 'lol', src: 'LOL' },
' We have best water! ',
{ shortcode: 'lmao', src: 'LMAO' }
{ shortcode: 'lmao', src: 'LMAO' },
])
})
it('leaves text as is', () => {
const input = 'Number one: that\'s terror'
const input = "Number one: that's terror"
const emojis = []
const processor = ({ shortcode, src }) => ({ shortcode, src })
expect(processTextForEmoji(input, emojis, processor)).to.eql([
'Number one: that\'s terror'
"Number one: that's terror",
])
})
})

View file

@ -5,7 +5,7 @@ const localAttn = () => ({
is_local: true,
name: 'Guy',
screen_name: 'person',
statusnet_profile_url: 'https://instance.com/users/person'
statusnet_profile_url: 'https://instance.com/users/person',
})
const externalAttn = () => ({
@ -13,7 +13,7 @@ const externalAttn = () => ({
is_local: false,
name: 'Guy',
screen_name: 'person@instance.com',
statusnet_profile_url: 'https://instance.com/users/person'
statusnet_profile_url: 'https://instance.com/users/person',
})
describe('MatcherService', () => {

View file

@ -10,44 +10,46 @@ describe('NotificationUtils', () => {
{
id: 1,
action: { id: '1' },
type: 'like'
type: 'like',
},
{
id: 2,
action: { id: '2' },
type: 'mention'
type: 'mention',
},
{
id: 3,
action: { id: '3' },
type: 'repeat'
}
]
}
type: 'repeat',
},
],
},
},
getters: {
mergedConfig: {
notificationVisibility: {
likes: true,
repeats: true,
mentions: false
}
}
}
mentions: false,
},
},
},
}
const expected = [
{
action: { id: '3' },
id: 3,
type: 'repeat'
type: 'repeat',
},
{
action: { id: '1' },
id: 1,
type: 'like'
}
type: 'like',
},
]
expect(NotificationUtils.filteredNotificationsFromStore(store)).to.eql(expected)
expect(NotificationUtils.filteredNotificationsFromStore(store)).to.eql(
expected,
)
})
})
@ -60,34 +62,36 @@ describe('NotificationUtils', () => {
{
action: { id: '1' },
type: 'like',
seen: false
seen: false,
},
{
action: { id: '2' },
type: 'mention',
seen: true
}
]
}
seen: true,
},
],
},
},
getters: {
mergedConfig: {
notificationVisibility: {
likes: true,
repeats: true,
mentions: false
}
}
}
mentions: false,
},
},
},
}
const expected = [
{
action: { id: '1' },
type: 'like',
seen: false
}
seen: false,
},
]
expect(NotificationUtils.unseenNotificationsFromStore(store)).to.eql(expected)
expect(NotificationUtils.unseenNotificationsFromStore(store)).to.eql(
expected,
)
})
})
})

View file

@ -2,20 +2,25 @@ import { deserialize } from 'src/services/theme_data/iss_deserializer.js'
import { serialize } from 'src/services/theme_data/iss_serializer.js'
const componentsContext = import.meta.glob(
['/src/**/*.style.js', '/src/**/*.style.json'],
{ eager: true }
{ eager: true },
)
describe('ISS (de)serialization', () => {
Object.keys(componentsContext).forEach(key => {
Object.keys(componentsContext).forEach((key) => {
const component = componentsContext[key].default
it(`(De)serialization of component ${component.name} works`, () => {
const normalized = component.defaultRules.map(x => ({ component: component.name, ...x }))
const normalized = component.defaultRules.map((x) => ({
component: component.name,
...x,
}))
const serialized = serialize(normalized)
const deserialized = deserialize(serialized)
// for some reason comparing objects directly fails the assert
expect(JSON.stringify(deserialized, null, 2)).to.equal(JSON.stringify(normalized, null, 2))
expect(JSON.stringify(deserialized, null, 2)).to.equal(
JSON.stringify(normalized, null, 2),
)
})
})

View file

@ -5,7 +5,7 @@ const checkColors = (output) => {
Object.entries(output.colors).forEach(([key, v]) => {
expect(v, key).to.be.an('object')
expect(v, key).to.include.all.keys('r', 'g', 'b')
'rgba'.split('').forEach(k => {
'rgba'.split('').forEach((k) => {
if ((k === 'a' && Object.hasOwn(v, 'a')) || k !== 'a') {
expect(v[k], key + '.' + k).to.be.a('number')
expect(v[k], key + '.' + k).to.be.least(0)
@ -16,7 +16,10 @@ const checkColors = (output) => {
}
describe('Theme Data utility functions', () => {
const context = import.meta.glob('/public/static/themes/*.json', { import: 'default', eager: true })
const context = import.meta.glob('/public/static/themes/*.json', {
import: 'default',
eager: true,
})
Object.keys(context).forEach((key) => {
it(`Should render all colors for ${key} properly`, () => {
const { theme, source } = context[key]

View file

@ -1,4 +1,7 @@
import { getLayersArray, topoSort } from 'src/services/theme_data/theme_data.service.js'
import {
getLayersArray,
topoSort,
} from 'src/services/theme_data/theme_data.service.js'
describe('Theme Data utility functions', () => {
describe('getLayersArray', () => {
@ -6,7 +9,7 @@ describe('Theme Data utility functions', () => {
layer1: null,
layer2: 'layer1',
layer3a: 'layer2',
layer3b: 'layer2'
layer3b: 'layer2',
}
it('should expand layers properly (3b)', () => {
@ -38,7 +41,7 @@ describe('Theme Data utility functions', () => {
layerB: [],
layer1B: ['layerB'],
layer2B: ['layer1B'],
layer3AB: ['layer2B', 'layer2A']
layer3AB: ['layer2B', 'layer2A'],
}
// Same thing but messed up order
@ -49,7 +52,7 @@ describe('Theme Data utility functions', () => {
layerB: [],
layer3AB: ['layer2B', 'layer2A'],
layer2B: ['layer1B'],
layerA: []
layerA: [],
}
it('should make a topologically sorted array', () => {
@ -63,7 +66,7 @@ describe('Theme Data utility functions', () => {
expect(out.indexOf('layer2B')).to.be.below(out.indexOf('layer3AB'))
})
it('order in object shouldn\'t matter', () => {
it("order in object shouldn't matter", () => {
const out = topoSort(fixture2, (node, inheritance) => inheritance[node])
// This basically checks all ordering that matters
expect(out.indexOf('layerA')).to.be.below(out.indexOf('layer1A'))
@ -82,7 +85,9 @@ describe('Theme Data utility functions', () => {
})
it('ignores cyclic dependencies', () => {
const out = topoSort({ a: 'b', b: 'a', c: 'a' }, (node, inheritance) => [inheritance[node]])
const out = topoSort({ a: 'b', b: 'a', c: 'a' }, (node, inheritance) => [
inheritance[node],
])
expect(out.indexOf('a')).to.be.below(out.indexOf('c'))
})
})

View file

@ -1,62 +1,84 @@
import {
getAllPossibleCombinations
} from 'src/services/theme_data/iss_utils.js'
import {
init
} from 'src/services/theme_data/theme_data_3.service.js'
import {
basePaletteKeys
} from 'src/services/theme_data/theme2_to_theme3.js'
import { getAllPossibleCombinations } from 'src/services/theme_data/iss_utils.js'
import { init } from 'src/services/theme_data/theme_data_3.service.js'
import { basePaletteKeys } from 'src/services/theme_data/theme2_to_theme3.js'
describe('Theme Data 3', () => {
describe('getAllPossibleCombinations', () => {
it('test simple 3 values case', () => {
const out = getAllPossibleCombinations([1, 2, 3]).map(x => x.sort((a, b) => a - b))
expect(out).to.eql([
[1], [2], [3],
[1, 2], [1, 3], [2, 3],
[1, 2, 3]
])
const out = getAllPossibleCombinations([1, 2, 3]).map((x) =>
x.sort((a, b) => a - b),
)
expect(out).to.eql([[1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]])
})
it('test simple 4 values case', () => {
const out = getAllPossibleCombinations([1, 2, 3, 4]).map(x => x.sort((a, b) => a - b))
const out = getAllPossibleCombinations([1, 2, 3, 4]).map((x) =>
x.sort((a, b) => a - b),
)
expect(out).to.eql([
[1], [2], [3], [4],
[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4],
[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4],
[1, 2, 3, 4]
[1],
[2],
[3],
[4],
[1, 2],
[1, 3],
[1, 4],
[2, 3],
[2, 4],
[3, 4],
[1, 2, 3],
[1, 2, 4],
[1, 3, 4],
[2, 3, 4],
[1, 2, 3, 4],
])
})
it('test massive 5 values case, using strings', () => {
const out = getAllPossibleCombinations(['a', 'b', 'c', 'd', 'e']).map(x => x.sort((a, b) => a - b))
const out = getAllPossibleCombinations(['a', 'b', 'c', 'd', 'e']).map(
(x) => x.sort((a, b) => a - b),
)
expect(out).to.eql([
// 1
['a'], ['b'], ['c'], ['d'], ['e'],
['a'],
['b'],
['c'],
['d'],
['e'],
// 2
['a', 'b'], ['a', 'c'], ['a', 'd'], ['a', 'e'],
['b', 'c'], ['b', 'd'], ['b', 'e'],
['c', 'd'], ['c', 'e'],
['a', 'b'],
['a', 'c'],
['a', 'd'],
['a', 'e'],
['b', 'c'],
['b', 'd'],
['b', 'e'],
['c', 'd'],
['c', 'e'],
['d', 'e'],
// 3
['a', 'b', 'c'], ['a', 'b', 'd'], ['a', 'b', 'e'],
['a', 'c', 'd'], ['a', 'c', 'e'],
['a', 'b', 'c'],
['a', 'b', 'd'],
['a', 'b', 'e'],
['a', 'c', 'd'],
['a', 'c', 'e'],
['a', 'd', 'e'],
['b', 'c', 'd'], ['b', 'c', 'e'],
['b', 'c', 'd'],
['b', 'c', 'e'],
['b', 'd', 'e'],
['c', 'd', 'e'],
// 4
['a', 'b', 'c', 'd'], ['a', 'b', 'c', 'e'],
['a', 'b', 'c', 'd'],
['a', 'b', 'c', 'e'],
['a', 'b', 'd', 'e'],
['a', 'c', 'd', 'e'],
['b', 'c', 'd', 'e'],
// 5
['a', 'b', 'c', 'd', 'e']
['a', 'b', 'c', 'd', 'e'],
])
})
})
@ -76,59 +98,73 @@ describe('Theme Data 3', () => {
expect(out.staticVars).to.be.an('object')
// check backwards compat/generic stuff
basePaletteKeys.forEach(key => {
basePaletteKeys.forEach((key) => {
expect(out.staticVars).to.have.property(key)
})
})
it('Test initialization with a basic palette', () => {
const out = init({
inputRuleset: [{
component: 'Root',
directives: {
'--bg': 'color | #008080',
'--fg': 'color | #00C0A0'
}
}],
ultimateBackgroundColor: '#DEADAF'
inputRuleset: [
{
component: 'Root',
directives: {
'--bg': 'color | #008080',
'--fg': 'color | #00C0A0',
},
},
],
ultimateBackgroundColor: '#DEADAF',
})
expect(out.staticVars).to.have.property('bg').equal('#008080')
expect(out.staticVars).to.have.property('fg').equal('#00C0A0')
const panelRule = out.eager.filter(x => {
const panelRule = out.eager.filter((x) => {
if (x.component !== 'Panel') return false
return true
})[0]
expect(panelRule).to.have.nested.deep.property('dynamicVars.stacked', { r: 0, g: 128, b: 128 })
expect(panelRule).to.have.nested.deep.property('dynamicVars.stacked', {
r: 0,
g: 128,
b: 128,
})
})
it('Test initialization with opacity', () => {
const out = init({
inputRuleset: [{
component: 'Root',
directives: {
'--bg': 'color | #008080'
}
}, {
component: 'Panel',
directives: {
opacity: 0.5
}
}],
inputRuleset: [
{
component: 'Root',
directives: {
'--bg': 'color | #008080',
},
},
{
component: 'Panel',
directives: {
opacity: 0.5,
},
},
],
onlyNormalState: true,
ultimateBackgroundColor: '#DEADAF'
ultimateBackgroundColor: '#DEADAF',
})
expect(out.staticVars).to.have.property('bg').equal('#008080')
const panelRule = out.eager.filter(x => {
const panelRule = out.eager.filter((x) => {
if (x.component !== 'Panel') return false
return true
})[0]
expect(panelRule).to.have.nested.deep.property('dynamicVars.background', { r: 0, g: 128, b: 128, a: 0.5 })
expect(panelRule).to.have.nested.deep.property('dynamicVars.background', {
r: 0,
g: 128,
b: 128,
a: 0.5,
})
expect(panelRule).to.have.nested.deep.property('dynamicVars.stacked')
// Somewhat incorrect since we don't do gamma correction
// real expectancy should be this:
@ -140,9 +176,15 @@ describe('Theme Data 3', () => {
*/
expect(panelRule).to.have.nested.deep.property('dynamicVars.stacked.r').that.is.closeTo(111, 0.01)
expect(panelRule).to.have.nested.deep.property('dynamicVars.stacked.g').that.is.closeTo(150.5, 0.01)
expect(panelRule).to.have.nested.deep.property('dynamicVars.stacked.b').that.is.closeTo(151.5, 0.01)
expect(panelRule)
.to.have.nested.deep.property('dynamicVars.stacked.r')
.that.is.closeTo(111, 0.01)
expect(panelRule)
.to.have.nested.deep.property('dynamicVars.stacked.g')
.that.is.closeTo(150.5, 0.01)
expect(panelRule)
.to.have.nested.deep.property('dynamicVars.stacked.b')
.that.is.closeTo(151.5, 0.01)
})
})
})

View file

@ -3,19 +3,22 @@ import generateProfileLink from 'src/services/user_profile_link_generator/user_p
describe('generateProfileLink', () => {
it('returns obj for local user', () => {
expect(generateProfileLink(1, 'jack')).to.eql({
name: 'user-profile', params: { name: 'jack' }
name: 'user-profile',
params: { name: 'jack' },
})
})
it('returns obj for external user', () => {
expect(generateProfileLink(1, 'john@domain')).to.eql({
name: 'external-user-profile', params: { id: 1 }
name: 'external-user-profile',
params: { id: 1 },
})
})
it('returns obj for restricted user', () => {
expect(generateProfileLink(1, 'lain', ['lain'])).to.eql({
name: 'external-user-profile', params: { id: 1 }
name: 'external-user-profile',
params: { id: 1 },
})
})
})

View file

@ -7,8 +7,8 @@ setActivePinia(createPinia())
const store = useListsStore()
window.vuex = createStore({
modules: {
api: apiModule
}
api: apiModule,
},
})
describe('The lists store', () => {
@ -28,12 +28,18 @@ describe('The lists store', () => {
const modList = { id: '1', title: 'anotherTestTitle' }
store.setList({ listId: list.id, title: list.title })
expect(store.allListsObject[list.id]).to.eql({ title: list.title, accountIds: [] })
expect(store.allListsObject[list.id]).to.eql({
title: list.title,
accountIds: [],
})
expect(store.allLists).to.have.length(1)
expect(store.allLists[0]).to.eql(list)
store.setList({ listId: modList.id, title: modList.title })
expect(store.allListsObject[modList.id]).to.eql({ title: modList.title, accountIds: [] })
expect(store.allListsObject[modList.id]).to.eql({
title: modList.title,
accountIds: [],
})
expect(store.allLists).to.have.length(1)
expect(store.allLists[0]).to.eql(modList)
})
@ -46,16 +52,21 @@ describe('The lists store', () => {
store.setListAccounts({ listId: list.id, accountIds: list.accountIds })
expect(store.allListsObject[list.id].accountIds).to.eql(list.accountIds)
store.setListAccounts({ listId: modList.id, accountIds: modList.accountIds })
expect(store.allListsObject[modList.id].accountIds).to.eql(modList.accountIds)
store.setListAccounts({
listId: modList.id,
accountIds: modList.accountIds,
})
expect(store.allListsObject[modList.id].accountIds).to.eql(
modList.accountIds,
)
})
it('deletes a list', () => {
store.$patch({
allLists: [{ id: '1', title: 'testList' }],
allListsObject: {
1: { title: 'testList', accountIds: ['1', '2', '3'] }
}
1: { title: 'testList', accountIds: ['1', '2', '3'] },
},
})
const listId = '1'
@ -70,8 +81,8 @@ describe('The lists store', () => {
store.$patch({
allLists: [{ id: '1', title: 'testList' }],
allListsObject: {
1: { title: 'testList', accountIds: ['1', '2', '3'] }
}
1: { title: 'testList', accountIds: ['1', '2', '3'] },
},
})
const id = '1'
@ -82,8 +93,8 @@ describe('The lists store', () => {
store.$patch({
allLists: [{ id: '1', title: 'testList' }],
allListsObject: {
1: { title: 'testList', accountIds: ['1', '2', '3'] }
}
1: { title: 'testList', accountIds: ['1', '2', '3'] },
},
})
const id = '1'

View file

@ -3,16 +3,20 @@ import { createStore } from 'vuex'
import { createPinia, setActivePinia } from 'pinia'
import { http, HttpResponse } from 'msw'
import { useOAuthStore } from 'src/stores/oauth.js'
import { injectMswToTest, authApis, testServer } from '/test/fixtures/mock_api.js'
import {
injectMswToTest,
authApis,
testServer,
} from '/test/fixtures/mock_api.js'
const test = injectMswToTest(authApis)
const vuexStore = createStore({
modules: {
instance: {
state: () => ({ server: testServer })
}
}
state: () => ({ server: testServer }),
},
},
})
const app = createApp({})
app.use(vuexStore)
@ -29,7 +33,6 @@ const getStore = (defaultStateInjection) => {
return useOAuthStore()
}
describe('createApp', () => {
test('it should use create an app and record client id and secret', async () => {
const store = getStore()
@ -44,7 +47,7 @@ describe('createApp', () => {
worker.use(
http.post(`${testServer}/api/v1/apps`, () => {
return HttpResponse.text('Throttled', { status: 429 })
})
}),
)
const store = getStore()
@ -69,12 +72,12 @@ describe('ensureApp', () => {
worker.use(
http.post(`${testServer}/api/v1/apps`, () => {
return HttpResponse.text('Should not call this API', { status: 400 })
})
}),
)
const store = getStore({
clientId: 'another-id',
clientSecret: 'another-secret'
clientSecret: 'another-secret',
})
const app = await store.ensureApp()
expect(store.clientId).to.eql('another-id')
@ -88,7 +91,7 @@ describe('getAppToken', () => {
test('it should get app token and set it in state', async () => {
const store = getStore({
clientId: 'test-id',
clientSecret: 'test-secret'
clientSecret: 'test-secret',
})
const token = await store.getAppToken()
expect(token).to.eql('test-app-token')
@ -98,7 +101,7 @@ describe('getAppToken', () => {
test('it should throw and not set state if it cannot get app token', async () => {
const store = getStore({
clientId: 'bad-id',
clientSecret: 'bad-secret'
clientSecret: 'bad-secret',
})
await expect(store.getAppToken()).rejects.toThrowError('400')
expect(store.appToken).to.eql(false)
@ -115,38 +118,42 @@ describe('ensureAppToken', () => {
test('it should work if we already have a working token', async () => {
const store = getStore({
appToken: 'also-good-app-token'
appToken: 'also-good-app-token',
})
const token = await store.ensureAppToken()
expect(token).to.eql('also-good-app-token')
expect(store.appToken).to.eql('also-good-app-token')
})
test('it should work if we have a bad token but good app credentials', async ({ worker }) => {
test('it should work if we have a bad token but good app credentials', async ({
worker,
}) => {
worker.use(
http.post(`${testServer}/api/v1/apps`, () => {
return HttpResponse.text('Should not call this API', { status: 400 })
})
}),
)
const store = getStore({
appToken: 'bad-app-token',
clientId: 'test-id',
clientSecret: 'test-secret'
clientSecret: 'test-secret',
})
const token = await store.ensureAppToken()
expect(token).to.eql('test-app-token')
expect(store.appToken).to.eql('test-app-token')
})
test('it should work if we have no token but good app credentials', async ({ worker }) => {
test('it should work if we have no token but good app credentials', async ({
worker,
}) => {
worker.use(
http.post(`${testServer}/api/v1/apps`, () => {
return HttpResponse.text('Should not call this API', { status: 400 })
})
}),
)
const store = getStore({
clientId: 'test-id',
clientSecret: 'test-secret'
clientSecret: 'test-secret',
})
const token = await store.ensureAppToken()
expect(token).to.eql('test-app-token')
@ -156,7 +163,7 @@ describe('ensureAppToken', () => {
test('it should work if we have no token and bad app credentials', async () => {
const store = getStore({
clientId: 'bad-id',
clientSecret: 'bad-secret'
clientSecret: 'bad-secret',
})
const token = await store.ensureAppToken()
expect(token).to.eql('test-app-token')
@ -169,7 +176,7 @@ describe('ensureAppToken', () => {
const store = getStore({
appToken: 'bad-app-token',
clientId: 'bad-id',
clientSecret: 'bad-secret'
clientSecret: 'bad-secret',
})
const token = await store.ensureAppToken()
expect(token).to.eql('test-app-token')
@ -182,7 +189,7 @@ describe('ensureAppToken', () => {
worker.use(
http.post(`${testServer}/api/v1/apps`, () => {
return HttpResponse.text('Throttled', { status: 429 })
})
}),
)
const store = getStore()
@ -193,7 +200,7 @@ describe('ensureAppToken', () => {
worker.use(
http.post(`${testServer}/oauth/token`, () => {
return HttpResponse.text('Throttled', { status: 429 })
})
}),
)
const store = getStore()