Merge branch 'chat-refactor' into shigusegubu-themes3
This commit is contained in:
commit
d6b6472192
13 changed files with 428 additions and 94 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -13,3 +13,6 @@ config/local.json
|
|||
src/assets/emoji.json
|
||||
logs/
|
||||
__screenshots__/
|
||||
.vitest-attachments/
|
||||
html/
|
||||
coverage/
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@
|
|||
"scripts": {
|
||||
"dev": "node build/update-emoji.js && vite dev",
|
||||
"build": "node build/update-emoji.js && vite build",
|
||||
"unit": "node build/update-emoji.js && vitest --run",
|
||||
"unit-ci": "node build/update-emoji.js && vitest --run --browser.headless",
|
||||
"unit:watch": "node build/update-emoji.js && vitest",
|
||||
"unit": "node build/update-emoji.js && vitest --run --coverage",
|
||||
"unit-ci": "node build/update-emoji.js && vitest --run --coverage --browser.headless",
|
||||
"unit:watch": "node build/update-emoji.js && vitest --coverage",
|
||||
"e2e:pw": "playwright test --config test/e2e-playwright/playwright.config.mjs",
|
||||
"e2e": "sh ./tools/e2e/run.sh",
|
||||
"test": "yarn run unit && yarn run e2e",
|
||||
|
|
@ -68,8 +68,9 @@
|
|||
"@vitejs/devtools": "^0.3.1",
|
||||
"@vitejs/plugin-vue": "^6.0.7",
|
||||
"@vitejs/plugin-vue-jsx": "^5.1.5",
|
||||
"@vitest/browser-playwright": "^4.1.7",
|
||||
"@vitest/browser": "^4.1.7",
|
||||
"@vitest/browser-playwright": "^4.1.7",
|
||||
"@vitest/coverage-v8": "^4.1.10",
|
||||
"@vitest/ui": "^4.1.7",
|
||||
"@vue/babel-helper-vue-jsx-merge-props": "1.4.0",
|
||||
"@vue/babel-plugin-jsx": "1.5.0",
|
||||
|
|
|
|||
|
|
@ -183,6 +183,7 @@ const Chat = {
|
|||
},
|
||||
async replyStatus(newVal) {
|
||||
await nextTick() // wait for changes to propagate to postStatusForm
|
||||
if (this.testMode) return
|
||||
this.$refs.postStatusForm.update()
|
||||
},
|
||||
$route: async function (newVal) {
|
||||
|
|
@ -246,12 +247,13 @@ const Chat = {
|
|||
|
||||
if (!isNewMessage) return
|
||||
|
||||
if (!this.testMode)
|
||||
if (!this.testMode) {
|
||||
await readChat({
|
||||
id: this.chat.id,
|
||||
lastReadId,
|
||||
credentials: useOAuthStore().token,
|
||||
})
|
||||
}
|
||||
|
||||
useChatsStore().readChat(this.chat.id)
|
||||
this.lastReadMessageId = this.maxId
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ const PostStatusForm = {
|
|||
statusQuote: Object,
|
||||
statusFiles: Array,
|
||||
statusMediaDescriptions: Object,
|
||||
statusScope: String,
|
||||
statusVisibility: String,
|
||||
statusContentType: String,
|
||||
|
||||
// Replies/mentions
|
||||
|
|
@ -306,7 +306,7 @@ const PostStatusForm = {
|
|||
defaultNewStatus.status = this.mentionsString + statusText
|
||||
}
|
||||
|
||||
defaultNewStatus.mentions = this.mentionsString
|
||||
defaultNewStatus.mentions = this.mentionsString.trim()
|
||||
defaultNewStatus.spoilerText = this.repliedSubjectString ?? ''
|
||||
defaultNewStatus.nsfw = this.userDefaultSensitive
|
||||
defaultNewStatus.visibility = scope
|
||||
|
|
@ -351,21 +351,13 @@ const PostStatusForm = {
|
|||
return this.newStatus.quote !== null
|
||||
},
|
||||
quotable() {
|
||||
return this.quotingAvailable && this.repliedStatus?.id
|
||||
},
|
||||
defaultQuotable() {
|
||||
if (
|
||||
!this.quotingAvailable ||
|
||||
!this.isReply ||
|
||||
!useMergedConfigStore().mergedConfig.quoteReply
|
||||
!this.isReply
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (!this.repliedStatus) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (
|
||||
this.repliedStatus.visibility === 'public' ||
|
||||
this.repliedStatus.visibility === 'unlisted' ||
|
||||
|
|
@ -379,8 +371,9 @@ const PostStatusForm = {
|
|||
return false
|
||||
},
|
||||
quoteId() {
|
||||
return this.newStatus.quote?.id
|
||||
return this.newStatus.quote?.id ?? null
|
||||
},
|
||||
// This is for "reply/quote" toggle
|
||||
quoteThreadToggled: {
|
||||
get() {
|
||||
return this.newStatus.quote?.thread
|
||||
|
|
@ -395,6 +388,24 @@ const PostStatusForm = {
|
|||
}
|
||||
},
|
||||
},
|
||||
postingOptions() {
|
||||
const poll = this.hasPoll ? pollFormToMasto(this.newStatus.poll) : null
|
||||
|
||||
return {
|
||||
status: this.newStatusContent,
|
||||
spoilerText: this.newStatus.spoilerText,
|
||||
visibility: this.newStatus.visibility,
|
||||
sensitive: this.newStatus.nsfw,
|
||||
media: this.newStatus.files,
|
||||
inReplyToStatusId: this.inReplyToStatusId,
|
||||
quoteId: this.quoteId,
|
||||
contentType: this.newStatus.contentType,
|
||||
poll,
|
||||
idempotencyKey: this.idempotencyKey,
|
||||
|
||||
store: this.$store,
|
||||
}
|
||||
},
|
||||
|
||||
// Emoji stuff
|
||||
emojiUserSuggestor() {
|
||||
|
|
@ -596,8 +607,7 @@ const PostStatusForm = {
|
|||
},
|
||||
onMentionsLineUpdate(e) {
|
||||
if (this.mentionsLineReadOnly) return
|
||||
// TODO
|
||||
//this.newStatus.mentions = e
|
||||
this.newStatus.mentions = e
|
||||
},
|
||||
changeVis(visibility) {
|
||||
this.newStatus.visibility = visibility
|
||||
|
|
@ -605,7 +615,7 @@ const PostStatusForm = {
|
|||
clearStatus() {
|
||||
this.saveInhibited = true
|
||||
this.newStatus.status = ''
|
||||
this.newStatus.mentions = null
|
||||
this.newStatus.mentions = ''
|
||||
this.newStatus.spoilerText = ''
|
||||
this.newStatus.files = []
|
||||
this.newStatus.poll = null
|
||||
|
|
@ -613,7 +623,6 @@ const PostStatusForm = {
|
|||
this.newStatus.nsfw = this.defaultNewStatus.nsfw
|
||||
this.newStatus.mediaDescriptions = {}
|
||||
this.$refs.mediaUpload && this.$refs.mediaUpload.clearFile()
|
||||
this.clearQuoteForm()
|
||||
if (this.preserveFocus) {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.textarea.focus()
|
||||
|
|
@ -626,7 +635,7 @@ const PostStatusForm = {
|
|||
if (this.preview) this.previewStatus()
|
||||
this.saveable = false
|
||||
},
|
||||
async postStatus(event, newStatus) {
|
||||
async postStatus(event) {
|
||||
if (this.posting && !this.optimisticPosting) {
|
||||
return
|
||||
}
|
||||
|
|
@ -653,7 +662,6 @@ const PostStatusForm = {
|
|||
return
|
||||
}
|
||||
|
||||
const poll = this.hasPoll ? pollFormToMasto(newStatus.poll) : {}
|
||||
if (this.pollContentError) {
|
||||
this.error = this.pollContentError
|
||||
return
|
||||
|
|
@ -669,25 +677,11 @@ const PostStatusForm = {
|
|||
return
|
||||
}
|
||||
|
||||
const postingOptions = {
|
||||
status: this.newStatusContent,
|
||||
spoilerText: newStatus.spoilerText ?? null,
|
||||
visibility: newStatus.visibility,
|
||||
sensitive: newStatus.nsfw,
|
||||
media: newStatus.files,
|
||||
store: this.$store,
|
||||
inReplyToStatusId: this.inReplyToStatusId,
|
||||
quoteId: this.quoteId,
|
||||
contentType: newStatus.contentType,
|
||||
poll,
|
||||
idempotencyKey: this.idempotencyKey,
|
||||
}
|
||||
|
||||
const postHandler = this.postHandler
|
||||
? this.postHandler
|
||||
: statusPoster.postStatus
|
||||
|
||||
postHandler(postingOptions)
|
||||
postHandler(this.postingOptions)
|
||||
.then((data) => {
|
||||
this.abandonDraft()
|
||||
this.clearStatus()
|
||||
|
|
@ -714,17 +708,9 @@ const PostStatusForm = {
|
|||
|
||||
statusPoster
|
||||
.postStatus({
|
||||
status: this.newStatusContent,
|
||||
spoilerText: newStatus.spoilerText || null,
|
||||
visibility: newStatus.visibility,
|
||||
sensitive: newStatus.nsfw,
|
||||
...this.postingOptions,
|
||||
media: [],
|
||||
store: this.$store,
|
||||
inReplyToStatusId: this.inReplyToStatusId,
|
||||
quoteId: this.quoteId,
|
||||
contentType: newStatus.contentType,
|
||||
poll: null,
|
||||
quote: null,
|
||||
preview: true,
|
||||
})
|
||||
.then((data) => {
|
||||
|
|
@ -789,12 +775,14 @@ const PostStatusForm = {
|
|||
shiftUpMediaFile(fileInfo) {
|
||||
const { files } = this.newStatus
|
||||
const index = this.newStatus.files.indexOf(fileInfo)
|
||||
if (index === 0) return
|
||||
files.splice(index, 1)
|
||||
files.splice(index - 1, 0, fileInfo)
|
||||
},
|
||||
shiftDnMediaFile(fileInfo) {
|
||||
const { files } = this.newStatus
|
||||
const index = this.newStatus.files.indexOf(fileInfo)
|
||||
if (index === files.length - 1) return
|
||||
files.splice(index, 1)
|
||||
files.splice(index + 1, 0, fileInfo)
|
||||
},
|
||||
|
|
@ -973,7 +961,7 @@ const PostStatusForm = {
|
|||
},
|
||||
|
||||
// Quote
|
||||
toggleQuoteForm() {
|
||||
toggleQuoteForm() { // This is for the "attach quote" button
|
||||
if (!this.hasQuote) {
|
||||
this.newStatus.quote = {}
|
||||
this.newStatus.quote.thread = false
|
||||
|
|
@ -983,11 +971,6 @@ const PostStatusForm = {
|
|||
this.newStatus.quote = null
|
||||
}
|
||||
},
|
||||
clearQuoteForm() {
|
||||
if (this.$refs.quoteForm) {
|
||||
this.$refs.quoteForm.clear()
|
||||
}
|
||||
},
|
||||
|
||||
// Drafts
|
||||
statusChanged() {
|
||||
|
|
|
|||
|
|
@ -208,9 +208,9 @@
|
|||
class="input form-post-body"
|
||||
:class="{ 'scrollable-form': !!maxHeight }"
|
||||
v-bind="propsToNative(inputProps)"
|
||||
@keydown.exact.enter="submitOnEnter && postStatus($event, newStatus)"
|
||||
@keydown.exact.enter="submitOnEnter && postStatus($event)"
|
||||
@keydown.meta.enter="postStatus($event, newStatus)"
|
||||
@keydown.ctrl.enter="!submitOnEnter && postStatus($event, newStatus)"
|
||||
@keydown.ctrl.enter="!submitOnEnter && postStatus($event)"
|
||||
@input="resize"
|
||||
@compositionupdate="resize"
|
||||
@paste="paste"
|
||||
|
|
@ -328,7 +328,7 @@
|
|||
<button
|
||||
class="btn button-default post-button"
|
||||
:disabled="isOverLengthLimit || posting || uploadingFiles || disableSubmit"
|
||||
@click.stop.prevent="postStatus($event, newStatus)"
|
||||
@click.stop.prevent="postStatus($event)"
|
||||
>
|
||||
<template v-if="posting">
|
||||
{{ $t('post_status.posting') }}
|
||||
|
|
|
|||
|
|
@ -207,12 +207,12 @@ export const BUTTONS = [
|
|||
return dispatch('fetchStatusSource', { id: status.id }).then((data) =>
|
||||
useEditStatusStore().openEditStatusModal({
|
||||
statusId: status.id,
|
||||
subject: data.spoiler_text,
|
||||
statusSubject: data.spoiler_text,
|
||||
statusText: data.text,
|
||||
statusIsSensitive: status.nsfw,
|
||||
statusPoll: status.poll,
|
||||
statusFiles: [...status.attachments],
|
||||
visibility: status.visibility,
|
||||
statusVisibility: status.visibility,
|
||||
statusContentType: data.content_type,
|
||||
}),
|
||||
)
|
||||
|
|
|
|||
2
test/fixtures/setup_test.js
vendored
2
test/fixtures/setup_test.js
vendored
|
|
@ -26,6 +26,7 @@ const getDefaultOpts = ({
|
|||
global: {
|
||||
plugins: [
|
||||
applyAfterStore(makeMockStore(), afterStore),
|
||||
createTestingPinia(),
|
||||
VueVirtualScroller,
|
||||
createRouter({
|
||||
history: createMemoryHistory(),
|
||||
|
|
@ -41,7 +42,6 @@ const getDefaultOpts = ({
|
|||
(Vue) => {
|
||||
Vue.directive('body-scroll-lock', {})
|
||||
},
|
||||
createTestingPinia(),
|
||||
],
|
||||
components: {
|
||||
RichContent,
|
||||
|
|
|
|||
|
|
@ -37,8 +37,8 @@ describe('routes', () => {
|
|||
|
||||
const matchedComponents = router.currentRoute.value.matched
|
||||
|
||||
expect(matchedComponents[0].components.default.name).to.eql(
|
||||
'AsyncComponentWrapper',
|
||||
expect(matchedComponents[0].components.default.__file).to.contain(
|
||||
'user_profile.vue',
|
||||
)
|
||||
})
|
||||
|
||||
|
|
@ -47,8 +47,8 @@ describe('routes', () => {
|
|||
|
||||
const matchedComponents = router.currentRoute.value.matched
|
||||
|
||||
expect(matchedComponents[0].components.default.name).to.eql(
|
||||
'AsyncComponentWrapper',
|
||||
expect(matchedComponents[0].components.default.__file).to.contain(
|
||||
'user_profile.vue',
|
||||
)
|
||||
})
|
||||
|
||||
|
|
@ -56,9 +56,8 @@ describe('routes', () => {
|
|||
await router.push('/lists')
|
||||
|
||||
const matchedComponents = router.currentRoute.value.matched
|
||||
|
||||
expect(matchedComponents[0].components.default.name).to.eql(
|
||||
'AsyncComponentWrapper',
|
||||
expect(matchedComponents[0].components.default.__file).to.contain(
|
||||
'lists.vue',
|
||||
)
|
||||
})
|
||||
|
||||
|
|
@ -67,8 +66,8 @@ describe('routes', () => {
|
|||
|
||||
const matchedComponents = router.currentRoute.value.matched
|
||||
|
||||
expect(matchedComponents[0].components.default.name).to.eql(
|
||||
'AsyncComponentWrapper',
|
||||
expect(matchedComponents[0].components.default.__file).to.contain(
|
||||
'lists_timeline.vue',
|
||||
)
|
||||
})
|
||||
|
||||
|
|
@ -77,8 +76,8 @@ describe('routes', () => {
|
|||
|
||||
const matchedComponents = router.currentRoute.value.matched
|
||||
|
||||
expect(matchedComponents[0].components.default.name).to.eql(
|
||||
'AsyncComponentWrapper',
|
||||
expect(matchedComponents[0].components.default.__file).to.contain(
|
||||
'lists_edit.vue',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { shallowMount } from '@vue/test-utils'
|
||||
|
||||
import ChatMessageList from './chat_message_list.vue'
|
||||
import ChatMessageList from 'src/components/chat_message_list/chat_message_list.vue'
|
||||
|
||||
describe('ChatMessageList', () => {
|
||||
describe('computed.chatItems', () => {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { createTestingPinia } from '@pinia/testing'
|
|||
import { shallowMount } from '@vue/test-utils'
|
||||
import { setActivePinia } from 'pinia'
|
||||
|
||||
import ChatView from './chat_view.vue'
|
||||
import ChatView from 'src/components/chat_view/chat_view.vue'
|
||||
|
||||
const message1 = {
|
||||
id: '1',
|
||||
|
|
@ -33,6 +33,10 @@ const global = {
|
|||
state: {
|
||||
api: {},
|
||||
users: {},
|
||||
statuses: {
|
||||
allStatusesObject: {
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
$route: {
|
||||
|
|
@ -69,7 +73,7 @@ describe('ChatView methods', () => {
|
|||
expect(component.vm.messages.length).to.eql(2)
|
||||
})
|
||||
|
||||
it('Updates minId and lastMessage and newMessageCount', () => {
|
||||
it('Updates minId and lastMessage and newMessageCount', async () => {
|
||||
component.vm.addMessages({ messages: [message1] })
|
||||
expect(component.vm.maxId).to.eql(message1.id)
|
||||
expect(component.vm.minId).to.eql(message1.id)
|
||||
|
|
@ -80,7 +84,7 @@ describe('ChatView methods', () => {
|
|||
expect(component.vm.minId).to.eql(message1.id)
|
||||
expect(component.vm.newMessageCount).to.eql(2)
|
||||
|
||||
component.vm.readChat()
|
||||
await component.vm.readChat()
|
||||
expect(component.vm.newMessageCount).to.eql(0)
|
||||
expect(component.vm.lastReadMessageId).to.eql(message2.id)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { vi } from 'vitest'
|
||||
|
||||
import { createTestingPinia } from '@pinia/testing'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { setActivePinia } from 'pinia'
|
||||
|
|
@ -6,6 +8,7 @@ import PostStatusForm from 'src/components/post_status_form/post_status_form.vue
|
|||
import { mountOpts } from '../../../fixtures/setup_test'
|
||||
|
||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
|
||||
const currentUser = {
|
||||
id: 'current-user',
|
||||
|
|
@ -24,15 +27,16 @@ const repliedStatus = {
|
|||
user: repliedUser,
|
||||
}
|
||||
|
||||
const replyMountOpts = () =>
|
||||
const repliedStatus2 = {
|
||||
id: 'status-2',
|
||||
visibility: 'private',
|
||||
summary: 'subject',
|
||||
user: repliedUser,
|
||||
}
|
||||
|
||||
const replyMountOpts = (props) =>
|
||||
mountOpts({
|
||||
props: {
|
||||
replyTo: repliedStatus.id,
|
||||
repliedUser,
|
||||
attentions: [],
|
||||
copyMessageScope: repliedStatus.visibility,
|
||||
disableDraft: true,
|
||||
},
|
||||
props,
|
||||
afterStore(store) {
|
||||
store.state.users.currentUser = currentUser
|
||||
store.state.statuses.allStatusesObject = {
|
||||
|
|
@ -43,19 +47,251 @@ const replyMountOpts = () =>
|
|||
|
||||
describe('PostStatusForm', () => {
|
||||
beforeEach(() => {
|
||||
setActivePinia(createTestingPinia())
|
||||
vi.useFakeTimers()
|
||||
})
|
||||
|
||||
it('initializes a reply form when quoteReply is unset', () => {
|
||||
useInstanceCapabilitiesStore().quotingAvailable = true
|
||||
|
||||
it('Clean empty initial state', () => {
|
||||
const wrapper = mount(PostStatusForm, replyMountOpts())
|
||||
|
||||
expect(wrapper.vm.newStatus.type).to.equal('reply')
|
||||
expect(wrapper.vm.newStatus.quote).to.eql({
|
||||
id: '',
|
||||
url: '',
|
||||
thread: false,
|
||||
})
|
||||
expect(wrapper.vm.statusType).to.equal('new')
|
||||
expect(wrapper.vm.newStatus.spoilerText).to.eql('')
|
||||
expect(wrapper.vm.newStatus.mentions).to.eql('')
|
||||
expect(wrapper.vm.newStatus.status).to.eql('')
|
||||
})
|
||||
|
||||
it('Reset cleans form to pristine state equal to state form was when created', () => {
|
||||
const wrapper = mount(PostStatusForm, replyMountOpts())
|
||||
|
||||
const initial = { ...wrapper.vm.newStatus }
|
||||
wrapper.vm.clearStatus()
|
||||
|
||||
expect(wrapper.vm.newStatus).to.eql(initial)
|
||||
})
|
||||
|
||||
it('Initializes a reply form', () => {
|
||||
const wrapper = mount(PostStatusForm, replyMountOpts({
|
||||
repliedStatus: repliedStatus,
|
||||
}))
|
||||
|
||||
useInstanceCapabilitiesStore().quotingAvailable = true
|
||||
|
||||
expect(wrapper.vm.statusType).to.equal('reply')
|
||||
expect(wrapper.vm.isReply).to.equal(true)
|
||||
expect(wrapper.vm.refId).to.equal('status-1')
|
||||
expect(wrapper.vm.quotable).to.equal(true)
|
||||
expect(wrapper.vm.inReplyToStatusId).to.equal('status-1')
|
||||
expect(wrapper.vm.newStatus.quote).to.eql(null)
|
||||
expect(wrapper.vm.newStatus.poll).to.eql(null)
|
||||
expect(wrapper.vm.newStatus.spoilerText).to.eql('')
|
||||
expect(wrapper.vm.newStatus.mentions).to.eql('@replied')
|
||||
expect(wrapper.vm.newStatus.status).to.eql('@replied ')
|
||||
expect(wrapper.vm.newStatus.visibility).to.eql('public')
|
||||
})
|
||||
|
||||
it('Copies scope and subject line, disables quoting for locked posts', () => {
|
||||
const wrapper = mount(PostStatusForm, replyMountOpts({
|
||||
repliedStatus: repliedStatus2,
|
||||
}))
|
||||
|
||||
useInstanceCapabilitiesStore().quotingAvailable = true
|
||||
|
||||
expect(wrapper.vm.statusType).to.equal('reply')
|
||||
expect(wrapper.vm.isReply).to.equal(true)
|
||||
expect(wrapper.vm.quotable).to.equal(false)
|
||||
expect(wrapper.vm.newStatus.quote).to.eql(null)
|
||||
expect(wrapper.vm.newStatus.poll).to.eql(null)
|
||||
expect(wrapper.vm.newStatus.spoilerText).to.eql('re: subject')
|
||||
expect(wrapper.vm.newStatus.mentions).to.eql('@replied')
|
||||
expect(wrapper.vm.newStatus.status).to.eql('@replied ')
|
||||
expect(wrapper.vm.newStatus.visibility).to.eql('private')
|
||||
|
||||
expect(wrapper.vm.postingOptions.status).to.eql('@replied ')
|
||||
expect(wrapper.vm.postingOptions.spoilerText).to.eql('re: subject')
|
||||
expect(wrapper.vm.postingOptions.visibility).to.eql('private')
|
||||
expect(wrapper.vm.postingOptions.sensitive).to.eql(false)
|
||||
expect(wrapper.vm.postingOptions.media).to.eql([])
|
||||
expect(wrapper.vm.postingOptions.inReplyToStatusId).to.eql('status-2')
|
||||
expect(wrapper.vm.postingOptions.quoteId).to.eql(null)
|
||||
expect(wrapper.vm.postingOptions.contentType).to.eql('text/plain')
|
||||
expect(wrapper.vm.postingOptions.poll).to.eql(null)
|
||||
})
|
||||
|
||||
it('Forces direct mode when replying to a DM, mastodon style subject handling', () => {
|
||||
// We need to initialize pinia first which is happening here...
|
||||
const options = replyMountOpts({
|
||||
repliedStatus: { ...repliedStatus2, visibility: 'direct' },
|
||||
})
|
||||
|
||||
// ...set our settings...
|
||||
useMergedConfigStore().mergedConfig = {
|
||||
...useMergedConfigStore().mergedConfig,
|
||||
subjectLineBehavior: 'masto'
|
||||
}
|
||||
|
||||
// ...and only then mount our component
|
||||
const wrapper = mount(PostStatusForm, options)
|
||||
|
||||
// Otherwise we get multiple instances of pinia that don't talk to each other
|
||||
|
||||
expect(wrapper.vm.statusType).to.equal('reply')
|
||||
expect(wrapper.vm.isReply).to.equal(true)
|
||||
expect(wrapper.vm.quotable).to.equal(false)
|
||||
expect(wrapper.vm.newStatus.quote).to.eql(null)
|
||||
expect(wrapper.vm.newStatus.poll).to.eql(null)
|
||||
expect(wrapper.vm.newStatus.spoilerText).to.eql('subject')
|
||||
expect(wrapper.vm.newStatus.mentions).to.eql('@replied')
|
||||
expect(wrapper.vm.newStatus.status).to.eql('@replied ')
|
||||
expect(wrapper.vm.newStatus.visibility).to.eql('direct')
|
||||
})
|
||||
|
||||
it('Sets status to statusText without mentions if mentions line is enabled', () => {
|
||||
const wrapper = mount(PostStatusForm, replyMountOpts({
|
||||
repliedStatus: repliedStatus2,
|
||||
statusText: 'testing',
|
||||
mentionsLine: true,
|
||||
}))
|
||||
|
||||
expect(wrapper.vm.statusType).to.equal('reply')
|
||||
expect(wrapper.vm.isReply).to.equal(true)
|
||||
expect(wrapper.vm.newStatus.mentions).to.eql('@replied')
|
||||
expect(wrapper.vm.newStatus.status).to.eql('testing')
|
||||
})
|
||||
|
||||
it('Sets mention when asked for it', () => {
|
||||
const wrapper = mount(PostStatusForm, replyMountOpts({
|
||||
profileMention: repliedUser,
|
||||
}))
|
||||
|
||||
expect(wrapper.vm.statusType).to.equal('mention')
|
||||
expect(wrapper.vm.isReply).to.equal(false)
|
||||
expect(wrapper.vm.newStatus.mentions).to.eql('@replied')
|
||||
expect(wrapper.vm.newStatus.status).to.eql('@replied ')
|
||||
})
|
||||
|
||||
it('Initializes quote when reply/quote toggled to quote', () => {
|
||||
const wrapper = mount(PostStatusForm, replyMountOpts({
|
||||
repliedStatus: repliedStatus2,
|
||||
}))
|
||||
|
||||
expect(wrapper.vm.statusType).to.equal('reply')
|
||||
expect(wrapper.vm.isReply).to.equal(true)
|
||||
|
||||
wrapper.vm.quoteThreadToggled = true
|
||||
|
||||
expect(wrapper.vm.newStatus.quote).to.eql({ thread: true, id: 'status-2' })
|
||||
})
|
||||
|
||||
it('Resets quote when reply/quote toggled to reply', () => {
|
||||
const wrapper = mount(PostStatusForm, replyMountOpts({
|
||||
repliedStatus: repliedStatus2,
|
||||
}))
|
||||
|
||||
expect(wrapper.vm.statusType).to.equal('reply')
|
||||
expect(wrapper.vm.isReply).to.equal(true)
|
||||
|
||||
wrapper.vm.quoteThreadToggled = true
|
||||
wrapper.vm.quoteThreadToggled = false
|
||||
|
||||
expect(wrapper.vm.newStatus.quote).to.eql(null)
|
||||
})
|
||||
|
||||
it('Initializes and reset quote when toggling quote attachment', () => {
|
||||
const wrapper = mount(PostStatusForm, replyMountOpts({
|
||||
repliedStatus: repliedStatus2,
|
||||
}))
|
||||
|
||||
expect(wrapper.vm.statusType).to.equal('reply')
|
||||
expect(wrapper.vm.isReply).to.equal(true)
|
||||
|
||||
wrapper.vm.toggleQuoteForm()
|
||||
expect(wrapper.vm.newStatus.quote).to.eql({ thread: false, id: null, url: '' })
|
||||
wrapper.vm.toggleQuoteForm()
|
||||
expect(wrapper.vm.newStatus.quote).to.eql(null)
|
||||
})
|
||||
|
||||
it('Status editing', () => {
|
||||
const wrapper = mount(PostStatusForm, replyMountOpts({
|
||||
statusId: 'edited',
|
||||
statusText: 'text',
|
||||
statusSubject: 'heading',
|
||||
statusIsSensitive: true,
|
||||
statusPoll: {},
|
||||
statusQuote: {},
|
||||
statusFiles: [],
|
||||
statusMediaDescriptions: {},
|
||||
statusVisibility: 'unlisted',
|
||||
statusContentType: 'text/markdown',
|
||||
}))
|
||||
|
||||
expect(wrapper.vm.statusType).to.equal('edit')
|
||||
expect(wrapper.vm.isReply).to.equal(false) // edits don't support changing reply-to so it's pretty much ignored
|
||||
expect(wrapper.vm.isEdit).to.equal(true)
|
||||
expect(wrapper.vm.newStatus.quote).to.eql({})
|
||||
expect(wrapper.vm.newStatus.poll).to.eql({})
|
||||
expect(wrapper.vm.newStatus.spoilerText).to.eql('heading')
|
||||
expect(wrapper.vm.newStatus.mentions).to.eql('')
|
||||
expect(wrapper.vm.newStatus.status).to.eql('text')
|
||||
expect(wrapper.vm.newStatus.visibility).to.eql('unlisted')
|
||||
expect(wrapper.vm.newStatus.contentType).to.eql('text/markdown')
|
||||
expect(wrapper.vm.newStatus.nsfw).to.equal(true)
|
||||
expect(wrapper.vm.newStatus.files).to.eql([])
|
||||
})
|
||||
|
||||
it('Posting should reset idempotency key', async () => {
|
||||
vi.setSystemTime(new Date(2027, 1, 1, 13))
|
||||
const wrapper = mount(PostStatusForm, replyMountOpts())
|
||||
const oldIdempotency = wrapper.vm.idempotencyKey
|
||||
|
||||
vi.setSystemTime(new Date(2028, 1, 1, 13))
|
||||
|
||||
wrapper.vm.newStatus.status = 'Testing'
|
||||
await wrapper.vm.postStatus()
|
||||
|
||||
expect(wrapper.vm.idempotencyKey).to.not.eql(oldIdempotency)
|
||||
})
|
||||
|
||||
// TODO Probably better to separate attachment upload/manipulation into its own component?
|
||||
// we need to upload-on-submit for compression setting anyway
|
||||
it('Attachments manipulations (moving, adding, removing)', () => {
|
||||
const wrapper = mount(PostStatusForm, replyMountOpts())
|
||||
|
||||
const i1 = { id: '1', url: 'a' }
|
||||
const i2 = { id: '2', url: 'b' }
|
||||
const i3 = { id: '3', url: 'c' }
|
||||
const i4 = { id: '4', url: 'd' }
|
||||
const iX = { id: 'x', url: 'x' }
|
||||
|
||||
wrapper.vm.newStatus.files = [i3, i1, iX, i2]
|
||||
|
||||
wrapper.vm.removeMediaFile(iX)
|
||||
expect(wrapper.vm.newStatus.files).to.eql([i3, i1, i2])
|
||||
|
||||
wrapper.vm.shiftUpMediaFile(i1)
|
||||
expect(wrapper.vm.newStatus.files).to.eql([i1, i3, i2])
|
||||
|
||||
wrapper.vm.shiftUpMediaFile(i1) // should ignore
|
||||
expect(wrapper.vm.newStatus.files).to.eql([i1, i3, i2])
|
||||
|
||||
wrapper.vm.shiftDnMediaFile(i3)
|
||||
expect(wrapper.vm.newStatus.files).to.eql([i1, i2, i3])
|
||||
|
||||
wrapper.vm.shiftDnMediaFile(i3) // should ignore
|
||||
expect(wrapper.vm.newStatus.files).to.eql([i1, i2, i3])
|
||||
|
||||
wrapper.vm.addMediaFile(i4)
|
||||
expect(wrapper.vm.newStatus.files).to.eql([i1, i2, i3, i4])
|
||||
})
|
||||
|
||||
it('Attachment descriptions', () => {
|
||||
const wrapper = mount(PostStatusForm, replyMountOpts())
|
||||
|
||||
const i1 = { id: '1', url: 'a' }
|
||||
|
||||
wrapper.vm.addMediaFile(i1)
|
||||
expect(wrapper.vm.newStatus.files).to.eql([i1])
|
||||
|
||||
wrapper.vm.editAttachment(i1, 'description')
|
||||
expect(wrapper.vm.newStatus.mediaDescriptions['1']).to.eql('description')
|
||||
})
|
||||
// TODO: Drafts (needs vuex to pinia migration)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -241,9 +241,13 @@ export default defineConfig(async ({ mode, command }) => {
|
|||
test: {
|
||||
globals: true,
|
||||
exclude: [...configDefaults.exclude, 'test/e2e-playwright/**'],
|
||||
coverage: {
|
||||
provider: 'v8',
|
||||
exclude: ['**/*.style.js', 'public/**'],
|
||||
},
|
||||
browser: {
|
||||
enabled: true,
|
||||
headless: true,
|
||||
headless: false,
|
||||
provider: playwright(),
|
||||
// https://github.com/mswjs/msw/issues/2757
|
||||
instances: [{ browser: 'chromium' }],
|
||||
|
|
|
|||
104
yarn.lock
104
yarn.lock
|
|
@ -1255,7 +1255,7 @@
|
|||
"@babel/helper-string-parser" "^7.27.1"
|
||||
"@babel/helper-validator-identifier" "^7.28.5"
|
||||
|
||||
"@babel/types@^7.29.7":
|
||||
"@babel/types@^7.29.0", "@babel/types@^7.29.7":
|
||||
version "7.29.7"
|
||||
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.29.7.tgz#8005e31d82712ee7adaef6e23c63b71a62770a92"
|
||||
integrity sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==
|
||||
|
|
@ -1268,6 +1268,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@bazel/runfiles/-/runfiles-6.3.1.tgz#3f8824b2d82853377799d42354b4df78ab0ace0b"
|
||||
integrity sha512-1uLNT5NZsUVIGS4syuHwTzZ8HycMPyr6POA3FCE4GbMtc4rhoJk8aZKtNIRthJYfL+iioppi+rTfH3olMPr9nA==
|
||||
|
||||
"@bcoe/v8-coverage@^1.0.2":
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz#bbe12dca5b4ef983a0d0af4b07b9bc90ea0ababa"
|
||||
integrity sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==
|
||||
|
||||
"@biomejs/biome@2.3.11":
|
||||
version "2.3.11"
|
||||
resolved "https://registry.yarnpkg.com/@biomejs/biome/-/biome-2.3.11.tgz#a8f3682b3b2c0112e2728f6d51d9c67a6c5521f8"
|
||||
|
|
@ -1807,6 +1812,14 @@
|
|||
"@jridgewell/resolve-uri" "^3.1.0"
|
||||
"@jridgewell/sourcemap-codec" "^1.4.14"
|
||||
|
||||
"@jridgewell/trace-mapping@^0.3.31":
|
||||
version "0.3.31"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz#db15d6781c931f3a251a3dac39501c98a6082fd0"
|
||||
integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==
|
||||
dependencies:
|
||||
"@jridgewell/resolve-uri" "^3.1.0"
|
||||
"@jridgewell/sourcemap-codec" "^1.4.14"
|
||||
|
||||
"@kazvmoe-infra/pinch-zoom-element@1.3.0":
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@kazvmoe-infra/pinch-zoom-element/-/pinch-zoom-element-1.3.0.tgz#a5e35ab190f93d016b8a83f69004fc69a8e6b774"
|
||||
|
|
@ -2614,6 +2627,22 @@
|
|||
tinyrainbow "^3.1.0"
|
||||
ws "^8.19.0"
|
||||
|
||||
"@vitest/coverage-v8@^4.1.10":
|
||||
version "4.1.10"
|
||||
resolved "https://registry.yarnpkg.com/@vitest/coverage-v8/-/coverage-v8-4.1.10.tgz#037cd5e7ea8a2f448f4c2e10db1411c2b0c927bd"
|
||||
integrity sha512-IM49HmthevbgAO4anp1hwtoT9wYe59w0LR00gr+eagHE+ZJ5lK4sLPeO0ubgoJcwLk6dehU3R24N+FbEEKDc8g==
|
||||
dependencies:
|
||||
"@bcoe/v8-coverage" "^1.0.2"
|
||||
"@vitest/utils" "4.1.10"
|
||||
ast-v8-to-istanbul "^1.0.0"
|
||||
istanbul-lib-coverage "^3.2.2"
|
||||
istanbul-lib-report "^3.0.1"
|
||||
istanbul-reports "^3.2.0"
|
||||
magicast "^0.5.2"
|
||||
obug "^2.1.1"
|
||||
std-env "^4.0.0-rc.1"
|
||||
tinyrainbow "^3.1.0"
|
||||
|
||||
"@vitest/expect@4.1.9":
|
||||
version "4.1.9"
|
||||
resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-4.1.9.tgz#ba1af73ae53262e3dc9b518cb7b76fb614e0ef53"
|
||||
|
|
@ -2635,6 +2664,13 @@
|
|||
estree-walker "^3.0.3"
|
||||
magic-string "^0.30.21"
|
||||
|
||||
"@vitest/pretty-format@4.1.10":
|
||||
version "4.1.10"
|
||||
resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-4.1.10.tgz#75542e7273a08cc10fd4d8dad4e3eb1f16cd958c"
|
||||
integrity sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==
|
||||
dependencies:
|
||||
tinyrainbow "^3.1.0"
|
||||
|
||||
"@vitest/pretty-format@4.1.9":
|
||||
version "4.1.9"
|
||||
resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-4.1.9.tgz#885cfe9fcb6ff3df4409ea66192cc1fb23d62fae"
|
||||
|
|
@ -2678,6 +2714,15 @@
|
|||
tinyglobby "^0.2.15"
|
||||
tinyrainbow "^3.1.0"
|
||||
|
||||
"@vitest/utils@4.1.10":
|
||||
version "4.1.10"
|
||||
resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-4.1.10.tgz#ffc71055f18bfccb1fd0586365ebc2824892e403"
|
||||
integrity sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==
|
||||
dependencies:
|
||||
"@vitest/pretty-format" "4.1.10"
|
||||
convert-source-map "^2.0.0"
|
||||
tinyrainbow "^3.1.0"
|
||||
|
||||
"@vitest/utils@4.1.9":
|
||||
version "4.1.9"
|
||||
resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-4.1.9.tgz#0184c7e6eb3234739b2b6b3b985f78d1ed823ee1"
|
||||
|
|
@ -3286,6 +3331,15 @@ ast-types@^0.13.4:
|
|||
dependencies:
|
||||
tslib "^2.0.1"
|
||||
|
||||
ast-v8-to-istanbul@^1.0.0:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/ast-v8-to-istanbul/-/ast-v8-to-istanbul-1.0.5.tgz#708baeb6f5c879226d112a341ffa821c43881d2d"
|
||||
integrity sha512-UPAgKJFSEGMWSDr3LX4tqnAb4f7KGT8O40Tyx8wbYmmZ/yn58lNCm8h3svs3eXgiGd5AXxz8NDOvXWvicq+rJA==
|
||||
dependencies:
|
||||
"@jridgewell/trace-mapping" "^0.3.31"
|
||||
estree-walker "^3.0.3"
|
||||
js-tokens "^10.0.0"
|
||||
|
||||
astral-regex@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31"
|
||||
|
|
@ -5572,6 +5626,11 @@ html-encoding-sniffer@^4.0.0:
|
|||
dependencies:
|
||||
whatwg-encoding "^3.1.1"
|
||||
|
||||
html-escaper@^2.0.0:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453"
|
||||
integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==
|
||||
|
||||
html-tags@^3.3.1:
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.3.1.tgz#a04026a18c882e4bba8a01a3d39cfe465d40b5ce"
|
||||
|
|
@ -6045,6 +6104,28 @@ isobject@^3.0.1:
|
|||
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
|
||||
integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==
|
||||
|
||||
istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.2:
|
||||
version "3.2.2"
|
||||
resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756"
|
||||
integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==
|
||||
|
||||
istanbul-lib-report@^3.0.0, istanbul-lib-report@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d"
|
||||
integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==
|
||||
dependencies:
|
||||
istanbul-lib-coverage "^3.0.0"
|
||||
make-dir "^4.0.0"
|
||||
supports-color "^7.1.0"
|
||||
|
||||
istanbul-reports@^3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.2.0.tgz#cb4535162b5784aa623cee21a7252cf2c807ac93"
|
||||
integrity sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==
|
||||
dependencies:
|
||||
html-escaper "^2.0.0"
|
||||
istanbul-lib-report "^3.0.0"
|
||||
|
||||
jackspeak@^3.1.2:
|
||||
version "3.4.3"
|
||||
resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a"
|
||||
|
|
@ -6085,6 +6166,11 @@ js-cookie@3.0.5, js-cookie@^3.0.5:
|
|||
resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-3.0.5.tgz#0b7e2fd0c01552c58ba86e0841f94dc2557dcdbc"
|
||||
integrity sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==
|
||||
|
||||
js-tokens@^10.0.0:
|
||||
version "10.0.0"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-10.0.0.tgz#dffe7599b4a8bb7fe30aff8d0235234dffb79831"
|
||||
integrity sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==
|
||||
|
||||
js-tokens@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||
|
|
@ -6477,6 +6563,15 @@ magic-string@^0.30.21:
|
|||
dependencies:
|
||||
"@jridgewell/sourcemap-codec" "^1.5.5"
|
||||
|
||||
magicast@^0.5.2:
|
||||
version "0.5.3"
|
||||
resolved "https://registry.yarnpkg.com/magicast/-/magicast-0.5.3.tgz#1800f6e76dd8b0dbe7257438a2c336aefabbd905"
|
||||
integrity sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==
|
||||
dependencies:
|
||||
"@babel/parser" "^7.29.3"
|
||||
"@babel/types" "^7.29.0"
|
||||
source-map-js "^1.2.1"
|
||||
|
||||
make-dir@^2.0.0, make-dir@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5"
|
||||
|
|
@ -6485,6 +6580,13 @@ make-dir@^2.0.0, make-dir@^2.1.0:
|
|||
pify "^4.0.1"
|
||||
semver "^5.6.0"
|
||||
|
||||
make-dir@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e"
|
||||
integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==
|
||||
dependencies:
|
||||
semver "^7.5.3"
|
||||
|
||||
math-intrinsics@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue