Page MenuHomePhorge

No OneTemporary

Size
38 KB
Referenced Files
None
Subscribers
None
diff --git a/src/components/draft/draft.js b/src/components/draft/draft.js
index 5f17738d01..12df0d6516 100644
--- a/src/components/draft/draft.js
+++ b/src/components/draft/draft.js
@@ -1,107 +1,109 @@
import { cloneDeep } from 'lodash'
import { defineAsyncComponent } from 'vue'
import Gallery from 'src/components/gallery/gallery.vue'
import PostStatusForm from 'src/components/post_status_form/post_status_form.vue'
import StatusContent from 'src/components/status_content/status_content.vue'
import { useDraftsStore } from 'src/stores/drafts.js'
import { useMergedConfigStore } from 'src/stores/merged_config.js'
import { useStatusesStore } from 'src/stores/statuses.js'
import { library } from '@fortawesome/fontawesome-svg-core'
import { faPollH } from '@fortawesome/free-solid-svg-icons'
library.add(faPollH)
const Draft = {
components: {
PostStatusForm,
EditStatusForm: defineAsyncComponent(
() => import('src/components/edit_status_form/edit_status_form.vue'),
),
ConfirmModal: defineAsyncComponent(
() => import('src/components/confirm_modal/confirm_modal.vue'),
),
StatusContent,
Gallery,
},
props: {
draft: {
type: Object,
required: true,
},
},
data() {
return {
referenceDraft: cloneDeep(this.draft),
editing: false,
showingConfirmDialog: false,
}
},
computed: {
relAttrs() {
if (this.draft.type === 'edit') {
return { statusId: this.draft.refId }
} else if (this.draft.type === 'reply') {
return {
repliedStatus: this.refStatus,
}
} else {
return {}
}
},
safeToSave() {
return (
this.draft.status ||
this.draft.files?.length ||
this.draft.hasPoll ||
this.draft.hasQuote
)
},
postStatusFormProps() {
return {
draftId: this.draft.id,
...this.relAttrs,
}
},
refStatus() {
return this.draft.refId
? useStatusesStore().allStatuses.get(this.draft.refId)
: undefined
},
localCollapseSubjectDefault() {
return useMergedConfigStore().mergedConfig.collapseMessageWithSubject
},
},
watch: {
editing(newVal) {
+ console.log('SAVE?', newVal)
if (newVal) return
+ console.log('SAVE', newVal)
if (this.safeToSave) {
useDraftsStore().addOrSaveDraft(this.draft)
} else {
useDraftsStore().addOrSaveDraft(this.referenceDraft)
}
},
},
methods: {
toggleEditing() {
this.editing = !this.editing
},
abandon() {
this.showingConfirmDialog = true
},
doAbandon() {
useDraftsStore()
.abandonDraft(this.draft.id)
.then(() => {
this.hideConfirmDialog()
})
},
hideConfirmDialog() {
this.showingConfirmDialog = false
},
},
}
export default Draft
diff --git a/src/stores/drafts.js b/src/stores/drafts.js
index 8325fba5b9..0f3fae5276 100644
--- a/src/stores/drafts.js
+++ b/src/stores/drafts.js
@@ -1,76 +1,77 @@
import { defineStore } from 'pinia'
import { storage } from 'src/lib/storage.js'
const storageKey = 'pleroma-fe-drafts'
/*
* Note: we do not use the persist state plugin because
* it is not impossible for a user to have two windows at
* the same time. The persist state plugin is just overriding
* everything with the current state. This isn't good because
* if a draft is created in one window and another draft is
* created in another, the draft in the first window will just
* be overriden.
* Here, we can't guarantee 100% atomicity unless one uses
* different keys, which will just pollute the whole storage.
* It is indeed best to have backend support for this.
*/
const getStorageData = async () => await storage.getItem(storageKey)
const saveDraftToStorage = async (draft) => {
const currentData = (await getStorageData()) ?? {}
currentData[draft.id] = JSON.parse(JSON.stringify(draft))
await storage.setItem(storageKey, currentData)
}
const deleteDraftFromStorage = async (ids) => {
const currentData = (await getStorageData()) ?? {}
ids.forEach((id) => {
delete currentData[id]
})
await storage.setItem(storageKey, currentData)
}
export const useDraftsStore = defineStore('drafts', {
state: () => ({
drafts: new Map(),
}),
getters: {
draftsByTypeAndRefId(state) {
return (type, refId) => {
return [...state.drafts.values()].filter(
(draft) => draft.type === type && draft.refId === refId,
)
}
},
draftsArray(state) {
return [...state.drafts.values()]
},
draftsCount(state) {
return state.drafts.size
},
},
actions: {
async abandonDraft(id) {
this.drafts.delete(id)
await deleteDraftFromStorage([id])
},
async loadDrafts() {
const currentData = await getStorageData()
this.drafts = new Map(Object.entries(currentData))
},
async addOrSaveDraft(draft) {
const id = draft.id ?? new Date().getTime().toString()
+ console.log('SAVE', id)
const draftWithId = { ...draft, id }
this.drafts.set(draft.id, draftWithId)
await saveDraftToStorage(draftWithId)
return id
},
async abandonAllDrafts(store) {
const ids = this.drafts.keys()
ids.forEach((id) => this.abandonDraft(id))
await deleteDraftFromStorage(ids)
},
},
})
diff --git a/test/unit/specs/components/draft.spec.js b/test/unit/specs/components/draft.spec.js
deleted file mode 100644
index 9e26e36b36..0000000000
--- a/test/unit/specs/components/draft.spec.js
+++ /dev/null
@@ -1,193 +0,0 @@
-import { createTestingPinia } from '@pinia/testing'
-import { flushPromises, mount } from '@vue/test-utils'
-import { setActivePinia } from 'pinia'
-import { $t, mountOpts, waitForEvent } from 'test/fixtures/setup_test.js'
-import { nextTick } from 'vue'
-
-import PostStatusForm from 'src/components/post_status_form/post_status_form.vue'
-
-import { useMergedConfigStore } from 'src/stores/merged_config.js'
-import { useUsersStore } from 'src/stores/users.js'
-
-const autoSaveOrNot = (caseFn, caseTitle, runFn) => {
- caseFn(`${caseTitle} with auto-save`, function () {
- return runFn.bind(this)(true)
- })
-
- caseFn(`${caseTitle} with no auto-save`, function () {
- return runFn.bind(this)(false)
- })
-}
-
-const saveManually = async (wrapper) => {
- 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'),
- )
- await btn.trigger('click')
-}
-
-const waitSaveTime = 4000
-
-const currentUser = {
- id: 'current-user',
- default_scope: 'public',
- locked: false,
-}
-
-describe('Draft saving', () => {
- beforeEach(() => {
- setActivePinia(createTestingPinia())
- useUsersStore().currentUser = currentUser
- })
-
- afterEach(() => {
- vi.useRealTimers()
- })
-
- autoSaveOrNot(
- it,
- 'should save when the button is clicked',
- async (autoSave) => {
- const wrapper = mount(PostStatusForm, mountOpts())
- const store = useMergedConfigStore()
- store.mergedConfig = {
- autoSaveDraft: autoSave,
- }
- expect(wrapper.vm.$store.getters.draftCount).to.equal(0)
-
- 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',
- )
- },
- )
-
- it('should auto-save if it is enabled', async function () {
- vi.useFakeTimers()
- const wrapper = mount(PostStatusForm, mountOpts())
- const store = useMergedConfigStore()
- store.mergedConfig = {
- autoSaveDraft: true,
- }
- expect(wrapper.vm.$store.getters.draftCount).to.equal(0)
- const textarea = wrapper.get('textarea')
- await textarea.setValue('mew mew')
-
- expect(wrapper.vm.$store.getters.draftCount).to.equal(0)
- await vi.advanceTimersByTimeAsync(waitSaveTime)
- 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 when close if auto-save is on', async () => {
- const wrapper = mount(
- PostStatusForm,
- mountOpts({
- props: {
- closeable: true,
- },
- }),
- )
- const store = useMergedConfigStore()
- store.mergedConfig = {
- autoSaveDraft: true,
- }
- expect(wrapper.vm.$store.getters.draftCount).to.equal(0)
- const textarea = wrapper.get('textarea')
- await textarea.setValue('mew mew')
- wrapper.vm.requestClose()
- expect(wrapper.vm.$store.getters.draftCount).to.equal(1)
- await waitForEvent(wrapper, 'close-accepted')
- })
-
- it('should save when close if auto-save is off, and unsavedPostAction is save', async () => {
- const wrapper = mount(
- PostStatusForm,
- mountOpts({
- props: {
- closeable: true,
- },
- }),
- )
- const store = useMergedConfigStore()
- store.mergedConfig = {
- autoSaveDraft: false,
- unsavedPostAction: 'save',
- }
- expect(wrapper.vm.$store.getters.draftCount).to.equal(0)
- const textarea = wrapper.get('textarea')
- await textarea.setValue('mew mew')
- wrapper.vm.requestClose()
- expect(wrapper.vm.$store.getters.draftCount).to.equal(1)
- await waitForEvent(wrapper, 'close-accepted')
- })
-
- it('should discard when close if auto-save is off, and unsavedPostAction is discard', async () => {
- const wrapper = mount(
- PostStatusForm,
- mountOpts({
- props: {
- closeable: true,
- },
- }),
- )
- const store = useMergedConfigStore()
- store.mergedConfig = {
- autoSaveDraft: false,
- unsavedPostAction: 'discard',
- }
- expect(wrapper.vm.$store.getters.draftCount).to.equal(0)
- const textarea = wrapper.get('textarea')
- await textarea.setValue('mew mew')
- wrapper.vm.requestClose()
- await waitForEvent(wrapper, 'close-accepted')
- expect(wrapper.vm.$store.getters.draftCount).to.equal(0)
- })
-
- it('should confirm when close if auto-save is off, and unsavedPostAction is confirm', async () => {
- const wrapper = mount(
- PostStatusForm,
- mountOpts({
- props: {
- closeable: true,
- },
- }),
- )
- const store = useMergedConfigStore(createTestingPinia())
- store.mergedConfig = {
- autoSaveDraft: false,
- unsavedPostAction: '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()
- await flushPromises()
- const saveButton = await vi.waitFor(() => {
- const button = wrapper.findByText(
- 'button',
- $t('post_status.close_confirm_save_button'),
- )
- if (!button) throw new Error('Save button not present')
- return button
- })
- expect(saveButton).to.be.ok
- await saveButton.trigger('click')
- console.info('clicked')
- expect(wrapper.vm.$store.getters.draftCount).to.equal(1)
- await flushPromises()
- await waitForEvent(wrapper, 'close-accepted')
- })
-})
diff --git a/test/unit/specs/components/post_status_form.spec.js b/test/unit/specs/components/post_status_form.spec.js
index 1fddf4a95d..45a6bea22d 100644
--- a/test/unit/specs/components/post_status_form.spec.js
+++ b/test/unit/specs/components/post_status_form.spec.js
@@ -1,338 +1,539 @@
import { createTestingPinia } from '@pinia/testing'
-import { mount } from '@vue/test-utils'
+import { flushPromises, mount } from '@vue/test-utils'
import { setActivePinia } from 'pinia'
-import { mountOpts } from 'test/fixtures/setup_test.js'
+import { $t, mountOpts, waitForEvent } from 'test/fixtures/setup_test.js'
import { vi } from 'vitest'
+import { nextTick } from 'vue'
import PostStatusForm from 'src/components/post_status_form/post_status_form.vue'
+import { useDraftsStore } from 'src/stores/drafts.js'
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
import { useMergedConfigStore } from 'src/stores/merged_config.js'
import { useStatusesStore } from 'src/stores/statuses.js'
import { useUsersStore } from 'src/stores/users.js'
+const autoSaveOrNot = (caseFn, caseTitle, runFn) => {
+ caseFn(`${caseTitle} with auto-save`, function () {
+ return runFn.bind(this)(true)
+ })
+
+ caseFn(`${caseTitle} with no auto-save`, function () {
+ return runFn.bind(this)(false)
+ })
+}
+
+const saveManually = async (wrapper) => {
+ 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'),
+ )
+ await btn.trigger('click')
+}
+
+const waitSaveTime = 4000
+
const currentUser = {
id: 'current-user',
default_scope: 'public',
locked: false,
}
const repliedUser = {
id: 'replied-user',
screen_name: 'replied',
}
const repliedStatus = {
id: 'status-1',
visibility: 'public',
user: repliedUser,
}
const repliedStatus2 = {
id: 'status-2',
visibility: 'private',
summary: 'subject',
user: repliedUser,
}
describe('PostStatusForm', () => {
- beforeEach(() => {
- vi.useFakeTimers()
- setActivePinia(createTestingPinia())
- useUsersStore().currentUser = currentUser
- useStatusesStore().allStatuses = new Map([
- [repliedStatus.id, repliedStatus],
- ])
- })
-
- it('Clean empty initial state', () => {
- const wrapper = mount(PostStatusForm, mountOpts())
-
- 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('')
- })
+ describe('Basic functionality', () => {
+ beforeEach(() => {
+ vi.useFakeTimers()
+ setActivePinia(createTestingPinia())
+ useUsersStore().currentUser = currentUser
+ useStatusesStore().allStatuses = new Map([
+ [repliedStatus.id, repliedStatus],
+ ])
+ })
- it('Reset cleans form to pristine state equal to state form was when created', () => {
- const wrapper = mount(PostStatusForm, mountOpts())
+ afterEach(() => {
+ vi.useRealTimers()
+ })
- const initial = { ...wrapper.vm.newStatus }
- wrapper.vm.clearStatus()
+ it('Clean empty initial state', () => {
+ const wrapper = mount(PostStatusForm, mountOpts())
- expect(wrapper.vm.newStatus).to.eql(initial)
- })
-
- it('Initializes a reply form', () => {
- const wrapper = mount(
- PostStatusForm,
- mountOpts({
- props: {
- repliedStatus: repliedStatus,
- },
- }),
- )
+ 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('')
+ })
- 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.be.null
- expect(wrapper.vm.newStatus.poll).to.be.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('Reset cleans form to pristine state equal to state form was when created', () => {
+ const wrapper = mount(PostStatusForm, mountOpts())
- it('Copies scope and subject line, disables quoting for locked posts', () => {
- const wrapper = mount(
- PostStatusForm,
- mountOpts({
- props: {
- repliedStatus: repliedStatus2,
- },
- }),
- )
+ const initial = { ...wrapper.vm.newStatus }
+ wrapper.vm.clearStatus()
- 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.be.null
- expect(wrapper.vm.newStatus.poll).to.be.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.be.null
- expect(wrapper.vm.postingOptions.contentType).to.eql('text/plain')
- expect(wrapper.vm.postingOptions.poll).to.be.null
- })
+ expect(wrapper.vm.newStatus).to.eql(initial)
+ })
- 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 = mountOpts({
- props: {
- repliedStatus: { ...repliedStatus2, visibility: 'direct' },
- },
+ it('Initializes a reply form', () => {
+ const wrapper = mount(
+ PostStatusForm,
+ mountOpts({
+ props: {
+ 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.be.null
+ expect(wrapper.vm.newStatus.poll).to.be.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')
})
- // ...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.be.null
- expect(wrapper.vm.newStatus.poll).to.be.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('Copies scope and subject line, disables quoting for locked posts', () => {
+ const wrapper = mount(
+ PostStatusForm,
+ mountOpts({
+ props: {
+ 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.be.null
+ expect(wrapper.vm.newStatus.poll).to.be.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.be.null
+ expect(wrapper.vm.postingOptions.contentType).to.eql('text/plain')
+ expect(wrapper.vm.postingOptions.poll).to.be.null
+ })
- it('Sets status to statusText without mentions if mentions line is enabled', () => {
- const wrapper = mount(
- PostStatusForm,
- mountOpts({
+ 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 = mountOpts({
props: {
- repliedStatus: repliedStatus2,
- statusText: 'testing',
- mentionsLine: true,
+ repliedStatus: { ...repliedStatus2, visibility: 'direct' },
},
- }),
- )
-
- 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')
- })
+ })
+
+ // ...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.be.null
+ expect(wrapper.vm.newStatus.poll).to.be.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 mention when asked for it', () => {
- const wrapper = mount(
- PostStatusForm,
- mountOpts({
- props: {
- profileMention: repliedUser,
- },
- }),
- )
+ it('Sets status to statusText without mentions if mentions line is enabled', () => {
+ const wrapper = mount(
+ PostStatusForm,
+ mountOpts({
+ props: {
+ 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')
+ })
- 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('Sets mention when asked for it', () => {
+ const wrapper = mount(
+ PostStatusForm,
+ mountOpts({
+ props: {
+ 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,
- mountOpts({
- props: {
- repliedStatus: repliedStatus2,
- },
- }),
- )
+ it('Initializes quote when reply/quote toggled to quote', () => {
+ const wrapper = mount(
+ PostStatusForm,
+ mountOpts({
+ props: {
+ 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',
+ })
+ })
- expect(wrapper.vm.statusType).to.equal('reply')
- expect(wrapper.vm.isReply).to.equal(true)
+ it('Resets quote when reply/quote toggled to reply', () => {
+ const wrapper = mount(
+ PostStatusForm,
+ mountOpts({
+ props: {
+ repliedStatus: repliedStatus2,
+ },
+ }),
+ )
- wrapper.vm.quoteThreadToggled = true
+ expect(wrapper.vm.statusType).to.equal('reply')
+ expect(wrapper.vm.isReply).to.equal(true)
- expect(wrapper.vm.newStatus.quote).to.eql({ thread: true, id: 'status-2' })
- })
+ wrapper.vm.quoteThreadToggled = true
+ wrapper.vm.quoteThreadToggled = false
- it('Resets quote when reply/quote toggled to reply', () => {
- const wrapper = mount(
- PostStatusForm,
- mountOpts({
- props: {
- repliedStatus: repliedStatus2,
- },
- }),
- )
+ expect(wrapper.vm.newStatus.quote).to.be.null
+ })
- expect(wrapper.vm.statusType).to.equal('reply')
- expect(wrapper.vm.isReply).to.equal(true)
+ it('Initializes and reset quote when toggling quote attachment', () => {
+ const wrapper = mount(
+ PostStatusForm,
+ mountOpts({
+ props: {
+ 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.be.null
+ })
- wrapper.vm.quoteThreadToggled = true
- wrapper.vm.quoteThreadToggled = false
+ it('Status editing', () => {
+ const wrapper = mount(
+ PostStatusForm,
+ mountOpts({
+ props: {
+ 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([])
+ })
- expect(wrapper.vm.newStatus.quote).to.be.null
- })
+ it('Posting should reset idempotency key', async () => {
+ vi.setSystemTime(new Date(2027, 1, 1, 13))
+ const wrapper = mount(PostStatusForm, mountOpts())
+ const oldIdempotency = wrapper.vm.idempotencyKey
- it('Initializes and reset quote when toggling quote attachment', () => {
- const wrapper = mount(
- PostStatusForm,
- mountOpts({
- props: {
- repliedStatus: repliedStatus2,
- },
- }),
- )
+ vi.setSystemTime(new Date(2028, 1, 1, 13))
- expect(wrapper.vm.statusType).to.equal('reply')
- expect(wrapper.vm.isReply).to.equal(true)
+ wrapper.vm.newStatus.status = 'Testing'
+ await wrapper.vm.postStatus()
- wrapper.vm.toggleQuoteForm()
- expect(wrapper.vm.newStatus.quote).to.eql({
- thread: false,
- id: null,
- url: '',
+ expect(wrapper.vm.idempotencyKey).to.not.eql(oldIdempotency)
})
- wrapper.vm.toggleQuoteForm()
- expect(wrapper.vm.newStatus.quote).to.be.null
})
- it('Status editing', () => {
- const wrapper = mount(
- PostStatusForm,
- mountOpts({
- props: {
- statusId: 'edited',
- statusText: 'text',
- statusSubject: 'heading',
- statusIsSensitive: true,
- statusPoll: {},
- statusQuote: {},
- statusFiles: [],
- statusMediaDescriptions: {},
- statusVisibility: 'unlisted',
- statusContentType: 'text/markdown',
- },
- }),
- )
+ describe('Attachments', () => {
+ beforeEach(() => {
+ vi.useFakeTimers()
+ setActivePinia(createTestingPinia())
+ useUsersStore().currentUser = currentUser
+ useStatusesStore().allStatuses = new Map([
+ [repliedStatus.id, repliedStatus],
+ ])
+ })
- 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([])
- })
+ afterEach(() => {
+ vi.useRealTimers()
+ })
- it('Posting should reset idempotency key', async () => {
- vi.setSystemTime(new Date(2027, 1, 1, 13))
- const wrapper = mount(PostStatusForm, mountOpts())
- const oldIdempotency = wrapper.vm.idempotencyKey
+ // 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, mountOpts())
- vi.setSystemTime(new Date(2028, 1, 1, 13))
+ 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.status = 'Testing'
- await wrapper.vm.postStatus()
+ wrapper.vm.newStatus.files = [i3, i1, iX, i2]
- expect(wrapper.vm.idempotencyKey).to.not.eql(oldIdempotency)
- })
+ wrapper.vm.removeMediaFile(iX)
+ expect(wrapper.vm.newStatus.files).to.eql([i3, i1, i2])
- // 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, mountOpts())
+ wrapper.vm.shiftUpMediaFile(i1)
+ expect(wrapper.vm.newStatus.files).to.eql([i1, i3, i2])
- 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.shiftUpMediaFile(i1) // should ignore
+ expect(wrapper.vm.newStatus.files).to.eql([i1, i3, i2])
- wrapper.vm.newStatus.files = [i3, i1, iX, i2]
+ wrapper.vm.shiftDnMediaFile(i3)
+ expect(wrapper.vm.newStatus.files).to.eql([i1, i2, i3])
- wrapper.vm.removeMediaFile(iX)
- expect(wrapper.vm.newStatus.files).to.eql([i3, i1, i2])
+ wrapper.vm.shiftDnMediaFile(i3) // should ignore
+ expect(wrapper.vm.newStatus.files).to.eql([i1, i2, i3])
- wrapper.vm.shiftUpMediaFile(i1)
- expect(wrapper.vm.newStatus.files).to.eql([i1, i3, i2])
+ wrapper.vm.addMediaFile(i4)
+ expect(wrapper.vm.newStatus.files).to.eql([i1, i2, i3, i4])
+ })
- wrapper.vm.shiftUpMediaFile(i1) // should ignore
- expect(wrapper.vm.newStatus.files).to.eql([i1, i3, i2])
+ it('Attachment descriptions', () => {
+ const wrapper = mount(PostStatusForm, mountOpts())
- wrapper.vm.shiftDnMediaFile(i3)
- expect(wrapper.vm.newStatus.files).to.eql([i1, i2, i3])
+ const i1 = { id: '1', url: 'a' }
- wrapper.vm.shiftDnMediaFile(i3) // should ignore
- expect(wrapper.vm.newStatus.files).to.eql([i1, i2, i3])
+ wrapper.vm.addMediaFile(i1)
+ expect(wrapper.vm.newStatus.files).to.eql([i1])
- wrapper.vm.addMediaFile(i4)
- expect(wrapper.vm.newStatus.files).to.eql([i1, i2, i3, i4])
+ wrapper.vm.editAttachment(i1, 'description')
+ expect(wrapper.vm.newStatus.mediaDescriptions['1']).to.eql('description')
+ })
})
- it('Attachment descriptions', () => {
- const wrapper = mount(PostStatusForm, mountOpts())
+ describe('Draft saving', () => {
+ beforeEach(() => {
+ setActivePinia(createTestingPinia({ stubActions: false }))
+ useUsersStore().currentUser = currentUser
+ vi.useFakeTimers()
+ })
- const i1 = { id: '1', url: 'a' }
+ afterEach(() => {
+ vi.useRealTimers()
+ })
- wrapper.vm.addMediaFile(i1)
- expect(wrapper.vm.newStatus.files).to.eql([i1])
+ autoSaveOrNot(
+ it,
+ 'should save when the button is clicked',
+ async (autoSave) => {
+ const wrapper = mount(PostStatusForm, mountOpts())
+ const store = useMergedConfigStore()
+ store.mergedConfig = {
+ autoSaveDraft: autoSave,
+ }
+ expect(useDraftsStore().draftsCount).to.equal(0)
+
+ const textarea = wrapper.get('textarea')
+ await textarea.setValue('mew mew')
+
+ await saveManually(wrapper)
+ expect(useDraftsStore().draftsCount).to.equal(1)
+ expect(useDraftsStore().draftsArray[0].status).to.equal('mew mew')
+ },
+ )
- wrapper.vm.editAttachment(i1, 'description')
- expect(wrapper.vm.newStatus.mediaDescriptions['1']).to.eql('description')
+ it('should auto-save if it is enabled', async function () {
+ vi.useFakeTimers()
+ const wrapper = mount(PostStatusForm, mountOpts())
+ const store = useMergedConfigStore()
+ store.mergedConfig = {
+ autoSaveDraft: true,
+ }
+ expect(useDraftsStore().draftsCount).to.equal(0)
+ const textarea = wrapper.get('textarea')
+ await textarea.setValue('mew mew')
+
+ expect(useDraftsStore().draftsCount).to.equal(0)
+ await vi.advanceTimersByTimeAsync(waitSaveTime)
+ expect(useDraftsStore().draftsCount).to.equal(1)
+ expect(useDraftsStore().draftsArray[0].status).to.equal('mew mew')
+ })
+
+ it('should auto-save when close if auto-save is on', async () => {
+ const wrapper = mount(
+ PostStatusForm,
+ mountOpts({
+ props: {
+ closeable: true,
+ },
+ }),
+ )
+ const store = useMergedConfigStore()
+ store.mergedConfig = {
+ autoSaveDraft: true,
+ }
+ expect(useDraftsStore().draftsCount).to.equal(0)
+ const textarea = wrapper.get('textarea')
+ await textarea.setValue('mew mew')
+ wrapper.vm.requestClose()
+ expect(useDraftsStore().draftsCount).to.equal(1)
+ await waitForEvent(wrapper, 'close-accepted')
+ })
+
+ it('should save when close if auto-save is off, and unsavedPostAction is save', async () => {
+ const wrapper = mount(
+ PostStatusForm,
+ mountOpts({
+ props: {
+ closeable: true,
+ },
+ }),
+ )
+ const store = useMergedConfigStore()
+ store.mergedConfig = {
+ autoSaveDraft: false,
+ unsavedPostAction: 'save',
+ }
+ expect(useDraftsStore().draftsCount).to.equal(0)
+ const textarea = wrapper.get('textarea')
+ await textarea.setValue('mew mew')
+ wrapper.vm.requestClose()
+ expect(useDraftsStore().draftsCount).to.equal(1)
+ await waitForEvent(wrapper, 'close-accepted')
+ })
+
+ it('should discard when close if auto-save is off, and unsavedPostAction is discard', async () => {
+ const wrapper = mount(
+ PostStatusForm,
+ mountOpts({
+ props: {
+ closeable: true,
+ },
+ }),
+ )
+ const store = useMergedConfigStore()
+ store.mergedConfig = {
+ autoSaveDraft: false,
+ unsavedPostAction: 'discard',
+ }
+ expect(useDraftsStore().draftsCount).to.equal(0)
+ const textarea = wrapper.get('textarea')
+ await textarea.setValue('mew mew')
+ wrapper.vm.requestClose()
+ await waitForEvent(wrapper, 'close-accepted')
+ expect(useDraftsStore().draftsCount).to.equal(0)
+ })
+
+ it('should confirm when close if auto-save is off, and unsavedPostAction is confirm', async () => {
+ const store = useMergedConfigStore()
+ const wrapper = mount(
+ PostStatusForm,
+ mountOpts({
+ props: {
+ closeable: true,
+ },
+ }),
+ )
+ store.mergedConfig = {
+ autoSaveDraft: false,
+ unsavedPostAction: 'confirm',
+ }
+ expect(useDraftsStore().draftsCount).to.equal(0)
+ const textarea = wrapper.get('textarea')
+ await textarea.setValue('mew mew')
+ wrapper.vm.requestClose()
+ await nextTick()
+ await flushPromises()
+ const saveButton = await vi.waitFor(() => {
+ const button = wrapper.findByText(
+ 'button',
+ $t('post_status.close_confirm_save_button'),
+ )
+ if (!button) throw new Error('Save button not present')
+ return button
+ })
+ expect(saveButton).to.be.ok
+ await saveButton.trigger('click')
+ console.info('clicked')
+ expect(useDraftsStore().draftsCount).to.equal(1)
+ await flushPromises()
+ await waitForEvent(wrapper, 'close-accepted')
+ })
})
- // TODO: Drafts (needs vuex to pinia migration)
})

File Metadata

Mime Type
text/x-diff
Expires
Tue, Oct 13, 10:06 AM (1 d, 18 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1786717
Default Alt Text
(38 KB)

Event Timeline