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

@ -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 },
})
})
})