From ee5a53aba83fa42194f31e8362ca5ebde2c02b2f Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Thu, 30 Jul 2026 21:16:11 +0300 Subject: [PATCH 1/4] add code coverage --- package.json | 7 ++-- vite.config.js | 4 ++ yarn.lock | 104 ++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 111 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index ed9a8ab33..d0a1419ab 100644 --- a/package.json +++ b/package.json @@ -7,8 +7,8 @@ "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": "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", "e2e:pw": "playwright test --config test/e2e-playwright/playwright.config.mjs", "e2e": "sh ./tools/e2e/run.sh", @@ -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", diff --git a/vite.config.js b/vite.config.js index beff5fe76..445faeb24 100644 --- a/vite.config.js +++ b/vite.config.js @@ -241,6 +241,10 @@ 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, diff --git a/yarn.lock b/yarn.lock index b60f88b7b..8644d2d53 100644 --- a/yarn.lock +++ b/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" From 75768715ac515cd8c9016f5bd5b7aecafa58b99c Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Fri, 31 Jul 2026 16:43:42 +0300 Subject: [PATCH 2/4] unit tests for PostStatusForm --- .../post_status_form/post_status_form.js | 79 ++--- .../post_status_form/post_status_form.vue | 6 +- .../buttons_definitions.js | 4 +- test/fixtures/setup_test.js | 2 +- .../components/chat_message_list.spec.js | 2 +- test/unit/specs/components/chat_view.spec.js | 2 +- .../specs/components/post_status_form.spec.js | 272 ++++++++++++++++-- 7 files changed, 293 insertions(+), 74 deletions(-) diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index 91837779d..ab1696eec 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -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() { diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue index b188d1b34..e38786206 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -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 @@