Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F85806775
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
38 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
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)
Attached To
Mode
rPUFE pleroma-fe-upstream
Attached
Detach File
Event Timeline
Log In to Comment