Merge branch 'weight-removal' into shigusegubu-themes3
This commit is contained in:
commit
0fe745f1bc
28 changed files with 981 additions and 1713 deletions
|
|
@ -1,28 +0,0 @@
|
|||
import { readFile } from 'node:fs/promises'
|
||||
import { resolve } from 'node:path'
|
||||
|
||||
const target = 'node_modules/msw/lib/mockServiceWorker.js'
|
||||
|
||||
const mswPlugin = () => {
|
||||
let projectRoot
|
||||
return {
|
||||
name: 'msw-plugin',
|
||||
apply: 'serve',
|
||||
configResolved(conf) {
|
||||
projectRoot = conf.root
|
||||
},
|
||||
configureServer(server) {
|
||||
server.middlewares.use(async (req, res, next) => {
|
||||
if (req.path === '/mockServiceWorker.js') {
|
||||
const file = await readFile(resolve(projectRoot, target))
|
||||
res.set('Content-Type', 'text/javascript')
|
||||
res.send(file)
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export default mswPlugin
|
||||
1
changelog.d/node24.change
Normal file
1
changelog.d/node24.change
Normal file
|
|
@ -0,0 +1 @@
|
|||
Building PleromaFE now requires NodeJS version 24 or newer
|
||||
82
package.json
82
package.json
|
|
@ -21,74 +21,72 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@chenfengyuan/vue-qrcode": "2.0.0",
|
||||
"@fortawesome/fontawesome-svg-core": "7.1.0",
|
||||
"@fortawesome/free-regular-svg-icons": "7.1.0",
|
||||
"@fortawesome/free-solid-svg-icons": "7.1.0",
|
||||
"@fortawesome/vue-fontawesome": "3.1.2",
|
||||
"@fortawesome/fontawesome-svg-core": "7.3.1",
|
||||
"@fortawesome/free-regular-svg-icons": "7.3.1",
|
||||
"@fortawesome/free-solid-svg-icons": "7.3.1",
|
||||
"@fortawesome/vue-fontawesome": "3.3.3",
|
||||
"@kazvmoe-infra/pinch-zoom-element": "1.3.0",
|
||||
"@kazvmoe-infra/unicode-emoji-json": "0.4.0",
|
||||
"@ruffle-rs/ruffle": "0.1.0-nightly.2025.6.22",
|
||||
"@ruffle-rs/ruffle": "0.6.0-nightly.2026.9.4",
|
||||
"@vuelidate/core": "2.0.3",
|
||||
"@vuelidate/validators": "2.0.4",
|
||||
"@web3-storage/parse-link-header": "^3.1.0",
|
||||
"@web3-storage/parse-link-header": "3.1.0",
|
||||
"body-scroll-lock": "3.1.5",
|
||||
"chromatism": "3.0.0",
|
||||
"click-outside-vue3": "4.0.1",
|
||||
"cropperjs": "2.0.1",
|
||||
"cropperjs": "2.2.0",
|
||||
"escape-html": "1.0.3",
|
||||
"hash-sum": "^2.0.0",
|
||||
"js-cookie": "3.0.5",
|
||||
"hash-sum": "2.0.0",
|
||||
"js-cookie": "3.0.8",
|
||||
"localforage": "1.10.0",
|
||||
"lodash-es": "4.17.21",
|
||||
"parse-link-header": "2.0.0",
|
||||
"phoenix": "1.8.1",
|
||||
"pinia": "4.0.3",
|
||||
"punycode.js": "2.3.1",
|
||||
"qrcode": "1.5.4",
|
||||
"querystring-es3": "0.2.1",
|
||||
"utf8": "3.0.0",
|
||||
"uuid": "11.1.0",
|
||||
"uuid": "14.0.2",
|
||||
"vue": "3.5.42",
|
||||
"vue-i18n": "11.4.0",
|
||||
"vue-i18n": "11.4.10",
|
||||
"vue-router": "5.3.1",
|
||||
"vue-virtual-scroller": "^3.0.5"
|
||||
"vue-virtual-scroller": "3.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "2.5.11",
|
||||
"@pinia/testing": "1.0.3",
|
||||
"@pinia/testing": "2.0.1",
|
||||
"@ungap/event-target": "0.2.4",
|
||||
"@vitejs/devtools": "^0.3.1",
|
||||
"@vitejs/plugin-vue": "^6.0.8",
|
||||
"@vitejs/plugin-vue-jsx": "^5.1.5",
|
||||
"@vitest/browser": "^4.1.11",
|
||||
"@vitest/browser-playwright": "^4.1.11",
|
||||
"@vitest/coverage-v8": "^4.1.11",
|
||||
"@vitest/ui": "^4.1.11",
|
||||
"@vue/compiler-sfc": "3.5.22",
|
||||
"@vue/devtools-api": "^8.1.5",
|
||||
"@vitejs/devtools": "0.7.2",
|
||||
"@vitejs/plugin-vue": "6.0.8",
|
||||
"@vitejs/plugin-vue-jsx": "5.1.6",
|
||||
"@vitest/browser": "5.0.0",
|
||||
"@vitest/browser-playwright": "5.0.0",
|
||||
"@vitest/coverage-v8": "5.0.0",
|
||||
"@vitest/ui": "5.0.0",
|
||||
"@vue/compiler-sfc": "3.5.42",
|
||||
"@vue/compiler-dom": "3.5.42",
|
||||
"@vue/devtools-api": "8.2.0",
|
||||
"@vue/test-utils": "2.5.0",
|
||||
"autoprefixer": "10.4.21",
|
||||
"chalk": "5.6.2",
|
||||
"autoprefixer": "10.5.4",
|
||||
"chalk": "6.0.0",
|
||||
"cross-spawn": "7.0.6",
|
||||
"iso-639-1": "3.1.5",
|
||||
"msw": "2.14.6",
|
||||
"iso-639-1": "3.1.6",
|
||||
"playwright": "1.61.0",
|
||||
"postcss": "8.5.6",
|
||||
"postcss-html": "^1.5.0",
|
||||
"postcss-scss": "^4.0.6",
|
||||
"sass-embedded": "^1.100.0",
|
||||
"postcss": "8.5.28",
|
||||
"postcss-html": "2.0.0",
|
||||
"postcss-scss": "4.0.9",
|
||||
"sass-embedded": "1.104.0",
|
||||
"serve-static": "2.2.0",
|
||||
"stylelint": "16.25.0",
|
||||
"stylelint-config-html": "^1.1.0",
|
||||
"stylelint-config-recommended": "^16.0.0",
|
||||
"stylelint-config-recommended-scss": "^14.0.0",
|
||||
"stylelint-config-recommended-vue": "^1.6.0",
|
||||
"stylelint-config-standard": "38.0.0",
|
||||
"vite": "^8.0.0",
|
||||
"stylelint": "17.14.0",
|
||||
"stylelint-config-html": "2.0.0",
|
||||
"stylelint-config-recommended": "18.0.0",
|
||||
"stylelint-config-recommended-scss": "17.0.1",
|
||||
"stylelint-config-recommended-vue": "2.0.0",
|
||||
"stylelint-config-standard": "40.0.0",
|
||||
"vite": "8.2.2",
|
||||
"vite-plugin-biome": "^1.2.0",
|
||||
"vite-plugin-stylelint": "^6.1.0",
|
||||
"vite-plugin-vue-devtools": "^8.2.1",
|
||||
"vitest": "^4.1.11"
|
||||
"vite-plugin-stylelint": "6.3.0",
|
||||
"vite-plugin-vue-devtools": "8.2.1",
|
||||
"vitest": "5.0.0"
|
||||
},
|
||||
"type": "module",
|
||||
"engines": {
|
||||
|
|
|
|||
|
|
@ -664,7 +664,7 @@ option {
|
|||
display: grid;
|
||||
grid-auto-flow: row dense;
|
||||
grid-template-columns: repeat(auto-fit, minmax(20em, 1fr));
|
||||
grid-gap: 0.5em;
|
||||
gap: 0.5em;
|
||||
|
||||
li {
|
||||
border: 1px solid var(--border);
|
||||
|
|
@ -923,7 +923,9 @@ option {
|
|||
margin: -1px;
|
||||
overflow: hidden;
|
||||
visibility: visible;
|
||||
/* stylelint-disable property-no-deprecated */
|
||||
clip: rect(0 0 0 0);
|
||||
/* stylelint-enable property-no-deprecated */
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
"x-slide x-slide x-slide . "
|
||||
"x-num x-num y-num y-num "
|
||||
"assists assists assists assists";
|
||||
grid-gap: 0.5em;
|
||||
gap: 0.5em;
|
||||
|
||||
&:not(.-shadow-controls) {
|
||||
grid-template-areas:
|
||||
|
|
@ -45,7 +45,7 @@
|
|||
display: grid;
|
||||
grid-auto-flow: row;
|
||||
grid-auto-rows: 2em;
|
||||
grid-gap: 0.5em;
|
||||
gap: 0.5em;
|
||||
}
|
||||
|
||||
.input-light-grid {
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@
|
|||
border-top: 1px solid var(--border);
|
||||
display: grid;
|
||||
justify-content: end;
|
||||
grid-gap: 0.5em;
|
||||
gap: 0.5em;
|
||||
grid-template-columns: min-content;
|
||||
grid-auto-columns: min-content;
|
||||
grid-auto-flow: column dense;
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@
|
|||
grid-template-columns: 1fr;
|
||||
grid-auto-columns: 10em;
|
||||
grid-auto-flow: column;
|
||||
grid-gap: 0.5em;
|
||||
gap: 0.5em;
|
||||
align-items: start;
|
||||
max-width: 100%;
|
||||
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@
|
|||
height: auto;
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-gap: 0.5em;
|
||||
gap: 0.5em;
|
||||
grid-template-columns: repeat(auto-fill, minmax(15em, 1fr));
|
||||
|
||||
.gallery-item {
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@
|
|||
|
||||
&-buttons-wrapper {
|
||||
display: grid;
|
||||
grid-gap: 0.5em;
|
||||
gap: 0.5em;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
|
||||
button {
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@
|
|||
grid-template-columns: 1fr;
|
||||
grid-auto-columns: var(--__line-height);
|
||||
grid-auto-flow: column;
|
||||
grid-gap: var(--__horizontal-gap);
|
||||
gap: var(--__horizontal-gap);
|
||||
align-items: baseline;
|
||||
|
||||
&[aria-expanded] {
|
||||
|
|
|
|||
|
|
@ -181,7 +181,7 @@ const updatePalette = (paletteKey, value) => {
|
|||
justify-content: space-around;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
grid-template-rows: repeat(5, 1fr) auto;
|
||||
grid-gap: 0.5em;
|
||||
gap: 0.5em;
|
||||
align-items: baseline;
|
||||
|
||||
.buttons {
|
||||
|
|
@ -196,7 +196,7 @@ const updatePalette = (paletteKey, value) => {
|
|||
grid-auto-flow: row;
|
||||
grid-auto-rows: auto;
|
||||
grid-template-columns: repeat(auto-fill, 10em);
|
||||
grid-gap: 0.5em;
|
||||
gap: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@
|
|||
grid-template-columns: 1fr;
|
||||
grid-auto-flow: column;
|
||||
grid-auto-columns: auto;
|
||||
grid-gap: 0.5em;
|
||||
gap: 0.5em;
|
||||
|
||||
.popover-wrapper {
|
||||
box-sizing: border-box;
|
||||
|
|
@ -73,7 +73,7 @@
|
|||
.main-button {
|
||||
width: 100%;
|
||||
padding: var(--__horizontal-gap) var(--__horizontal-gap);
|
||||
grid-gap: var(--__horizontal-gap);
|
||||
gap: var(--__horizontal-gap);
|
||||
grid-template-columns: 1fr var(--__line-height);
|
||||
grid-auto-flow: column;
|
||||
grid-auto-columns: auto;
|
||||
|
|
|
|||
|
|
@ -169,7 +169,7 @@ div.PWAManifestIconsSetting {
|
|||
list-style: none;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, 12em);
|
||||
grid-gap: 2em;
|
||||
gap: 2em;
|
||||
}
|
||||
|
||||
dl {
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@
|
|||
.palettes {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-gap: 0.5em;
|
||||
gap: 0.5em;
|
||||
padding: 0.5em;
|
||||
width: 100%;
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
padding: 0.5em;
|
||||
display: grid;
|
||||
align-items: baseline;
|
||||
grid-gap: 0.5em;
|
||||
gap: 0.5em;
|
||||
}
|
||||
|
||||
.never {
|
||||
|
|
@ -69,7 +69,7 @@
|
|||
grid-column: 1 / span 3;
|
||||
justify-self: end;
|
||||
display: inline-grid;
|
||||
grid-gap: 0.5em;
|
||||
gap: 0.5em;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
max-width: 100%;
|
||||
justify-content: end;
|
||||
|
|
@ -81,7 +81,7 @@
|
|||
align-items: baseline;
|
||||
grid-auto-flow: column;
|
||||
grid-template-columns: 1fr;
|
||||
grid-gap: 0.5em;
|
||||
gap: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
|
||||
.total {
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@
|
|||
display: grid;
|
||||
grid-template:
|
||||
"meta preview";
|
||||
grid-gap: 1em;
|
||||
gap: 1em;
|
||||
grid-template-columns: min-content 6fr;
|
||||
|
||||
.theme-preview-container {
|
||||
|
|
@ -110,7 +110,7 @@
|
|||
"movement editor";
|
||||
grid-template-columns: 10em 1fr;
|
||||
grid-template-rows: auto 1fr auto;
|
||||
grid-gap: 0.5em;
|
||||
gap: 0.5em;
|
||||
|
||||
.list-edit-area {
|
||||
grid-area: editor;
|
||||
|
|
@ -141,7 +141,7 @@
|
|||
grid-template-columns: auto 1fr auto 10em;
|
||||
grid-template-rows: subgrid;
|
||||
align-items: baseline;
|
||||
grid-gap: 0 0.5em;
|
||||
gap: 0 0.5em;
|
||||
}
|
||||
|
||||
.list-edit-area {
|
||||
|
|
@ -158,7 +158,7 @@
|
|||
display: grid;
|
||||
grid-template-columns: 6fr 3fr 4fr;
|
||||
grid-template-rows: auto auto 1fr;
|
||||
grid-gap: 0.5em;
|
||||
gap: 0.5em;
|
||||
grid-template-areas:
|
||||
"component component variant"
|
||||
"state state state"
|
||||
|
|
@ -176,7 +176,7 @@
|
|||
grid-template-columns: 1fr minmax(10em, 1fr);
|
||||
grid-template-rows: auto;
|
||||
grid-auto-flow: column;
|
||||
grid-gap: 0.5em;
|
||||
gap: 0.5em;
|
||||
align-items: baseline;
|
||||
|
||||
> label:not(.Select) {
|
||||
|
|
@ -200,7 +200,7 @@
|
|||
grid-auto-flow: dense;
|
||||
grid-template-columns: repeat(5, minmax(min-content, 1fr));
|
||||
grid-auto-rows: 1fr;
|
||||
grid-gap: 0.5em;
|
||||
gap: 0.5em;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
|
@ -220,7 +220,7 @@
|
|||
.editor-tab {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 2em;
|
||||
grid-column-gap: 0.5em;
|
||||
column-gap: 0.5em;
|
||||
align-items: center;
|
||||
grid-auto-rows: min-content;
|
||||
grid-auto-flow: dense;
|
||||
|
|
@ -272,7 +272,7 @@
|
|||
grid-template-columns: 1fr;
|
||||
grid-template-rows: auto;
|
||||
grid-auto-flow: row;
|
||||
grid-gap: 0.5em;
|
||||
gap: 0.5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -286,7 +286,7 @@
|
|||
.style-actions {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(7em, 1fr));
|
||||
grid-gap: 0.25em;
|
||||
gap: 0.25em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
grid-template-columns: 10em 1fr 1fr;
|
||||
grid-template-rows: 1fr;
|
||||
grid-template-areas: "selector preview tweak";
|
||||
grid-gap: 0.5em;
|
||||
gap: 0.5em;
|
||||
justify-content: stretch;
|
||||
|
||||
.style-control {
|
||||
|
|
@ -49,7 +49,7 @@
|
|||
margin-right: 0.125em;
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr;
|
||||
grid-gap: 0.25em;
|
||||
gap: 0.25em;
|
||||
|
||||
/* hack */
|
||||
.input-boolean {
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@
|
|||
|
||||
.action-button-inner {
|
||||
display: grid;
|
||||
grid-gap: 0.125em;
|
||||
gap: 0.125em;
|
||||
grid-template-columns: max-content;
|
||||
grid-auto-flow: column;
|
||||
grid-auto-columns: max-content;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
grid-template-columns: repeat(auto-fill, minmax(3.75em, 10%));
|
||||
grid-auto-flow: row dense;
|
||||
grid-auto-rows: 1fr;
|
||||
grid-gap: 0.5em 0.1em;
|
||||
gap: 0.5em 0.1em;
|
||||
}
|
||||
|
||||
.pin-action-button {
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@
|
|||
grid-auto-flow: column;
|
||||
grid-template-columns: auto;
|
||||
grid-auto-columns: auto;
|
||||
grid-column-gap: var(--__panel-heading-gap);
|
||||
column-gap: var(--__panel-heading-gap);
|
||||
padding: var(--panel-heading-height-padding);
|
||||
height: var(--__panel-heading-height);
|
||||
line-height: var(--__panel-heading-height-inner);
|
||||
|
|
|
|||
|
|
@ -220,7 +220,7 @@
|
|||
position: relative;
|
||||
display: inline-grid;
|
||||
grid-auto-flow: column;
|
||||
grid-gap: 0.6em;
|
||||
gap: 0.6em;
|
||||
font-size: 1.25rem;
|
||||
z-index: 2;
|
||||
align-self: end;
|
||||
|
|
@ -255,7 +255,6 @@
|
|||
&.-overlay {
|
||||
position: absolute;
|
||||
inset: -0.6em;
|
||||
left: -0.6em;
|
||||
right: -1.2em;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
|
@ -419,7 +418,7 @@
|
|||
.user-stats {
|
||||
display: block;
|
||||
text-align: center;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
width: 100%;
|
||||
|
||||
dl {
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@
|
|||
grid-auto-flow: column;
|
||||
grid-template-columns: minmax(50%, 1fr);
|
||||
grid-auto-columns: auto;
|
||||
grid-column-gap: var(--__panel-heading-gap);
|
||||
column-gap: var(--__panel-heading-gap);
|
||||
flex: none;
|
||||
background-size: cover;
|
||||
padding: var(--panel-heading-height-padding);
|
||||
|
|
|
|||
19
test/fixtures/mock_api.js
vendored
19
test/fixtures/mock_api.js
vendored
|
|
@ -1,19 +0,0 @@
|
|||
import { test as testBase } from 'vitest'
|
||||
|
||||
import { worker } from './worker.js'
|
||||
|
||||
export const test = testBase.extend({
|
||||
worker: [
|
||||
// biome-ignore lint: required by vitest
|
||||
async ({}, use) => {
|
||||
await worker.start()
|
||||
|
||||
await use(worker)
|
||||
|
||||
worker.resetHandlers()
|
||||
},
|
||||
{
|
||||
auto: true,
|
||||
},
|
||||
],
|
||||
})
|
||||
5
test/fixtures/worker.js
vendored
5
test/fixtures/worker.js
vendored
|
|
@ -1,5 +0,0 @@
|
|||
import { setupWorker } from 'msw/browser'
|
||||
|
||||
export const worker = setupWorker()
|
||||
|
||||
window.__test__ = window.__test__ || 'TEST'
|
||||
|
|
@ -1,23 +1,18 @@
|
|||
import { createTestingPinia } from '@pinia/testing'
|
||||
import { HttpResponse, http } from 'msw'
|
||||
import { setActivePinia } from 'pinia'
|
||||
|
||||
import { test as it } from '/test/fixtures/mock_api.js'
|
||||
|
||||
import { useListsStore } from 'src/stores/lists.js'
|
||||
|
||||
import { MASTODON_LIST_ACCOUNTS_URL, MASTODON_LIST_URL } from 'src/api/user.js'
|
||||
|
||||
describe('The lists store', () => {
|
||||
let store
|
||||
|
||||
beforeEach(() => {
|
||||
setActivePinia(createTestingPinia({ stubActions: false }))
|
||||
store = useListsStore()
|
||||
})
|
||||
|
||||
describe('actions', () => {
|
||||
it('updates array of all lists', () => {
|
||||
const store = useListsStore()
|
||||
const list = { id: '1', title: 'testList' }
|
||||
|
||||
store.setLists([list])
|
||||
|
|
@ -25,19 +20,33 @@ describe('The lists store', () => {
|
|||
expect(store.allLists).to.eql([list])
|
||||
})
|
||||
|
||||
it('adds a new list with a title, updating the title for existing lists', async ({
|
||||
worker,
|
||||
}) => {
|
||||
it('adds a new list with a title, updating the title for existing lists', async () => {
|
||||
const store = useListsStore()
|
||||
const list = { id: '1', title: 'testList' }
|
||||
const modList = { id: '1', title: 'anotherTestTitle' }
|
||||
|
||||
worker.use(
|
||||
http.put(MASTODON_LIST_URL(':id'), () =>
|
||||
HttpResponse.json({ ok: true }),
|
||||
),
|
||||
)
|
||||
const mockFetch = vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce(
|
||||
new Response(JSON.stringify({ ok: true }), {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
}),
|
||||
)
|
||||
.mockResolvedValueOnce(
|
||||
new Response(JSON.stringify({ ok: true }), {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
}),
|
||||
)
|
||||
|
||||
vi.stubGlobal('fetch', mockFetch)
|
||||
|
||||
await store.setList({ listId: list.id, title: list.title })
|
||||
|
||||
expect(mockFetch).to.have.been.calledOnce
|
||||
expect(mockFetch.mock.calls[0][0]).to.eql(MASTODON_LIST_URL('1'))
|
||||
expect(mockFetch.mock.calls[0][1]).to.have.property('method', 'PUT')
|
||||
mockFetch.mockClear()
|
||||
|
||||
expect(store.allListsObject[list.id]).to.eql({
|
||||
title: list.title,
|
||||
accountIds: [],
|
||||
|
|
@ -46,6 +55,11 @@ describe('The lists store', () => {
|
|||
expect(store.allLists[0]).to.eql(list)
|
||||
|
||||
await store.setList({ listId: modList.id, title: modList.title })
|
||||
|
||||
expect(mockFetch).to.have.been.calledOnce
|
||||
expect(mockFetch.mock.calls[0][0]).to.eql(MASTODON_LIST_URL('1'))
|
||||
expect(mockFetch.mock.calls[0][1]).to.have.property('method', 'PUT')
|
||||
|
||||
expect(store.allListsObject[modList.id]).to.eql({
|
||||
title: modList.title,
|
||||
accountIds: [],
|
||||
|
|
@ -54,25 +68,39 @@ describe('The lists store', () => {
|
|||
expect(store.allLists[0]).to.eql(modList)
|
||||
})
|
||||
|
||||
it('adds a new list with an array of IDs, updating the IDs for existing lists', async ({
|
||||
worker,
|
||||
}) => {
|
||||
it('adds a new list with an array of IDs, updating the IDs for existing lists', async () => {
|
||||
const store = useListsStore()
|
||||
const list = { id: '1', accountIds: ['1', '2', '3'] }
|
||||
const modList = { id: '1', accountIds: ['3', '4', '5'] }
|
||||
|
||||
worker.use(
|
||||
http.post(MASTODON_LIST_ACCOUNTS_URL(':id'), () =>
|
||||
HttpResponse.json({ ok: true }),
|
||||
),
|
||||
http.delete(MASTODON_LIST_ACCOUNTS_URL(':id'), () =>
|
||||
HttpResponse.json({ ok: true }),
|
||||
),
|
||||
)
|
||||
const mockFetch = vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce(
|
||||
new Response(JSON.stringify({ ok: true }), {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
}),
|
||||
)
|
||||
.mockResolvedValueOnce(
|
||||
new Response(JSON.stringify({ ok: true }), {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
}),
|
||||
)
|
||||
.mockResolvedValueOnce(
|
||||
new Response(JSON.stringify({ ok: true }), {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
}),
|
||||
)
|
||||
vi.stubGlobal('fetch', mockFetch)
|
||||
|
||||
await store.setListAccounts({
|
||||
listId: list.id,
|
||||
accountIds: list.accountIds,
|
||||
})
|
||||
expect(mockFetch).to.have.been.calledOnce
|
||||
expect(mockFetch.mock.calls[0][0]).to.eql(MASTODON_LIST_ACCOUNTS_URL('1'))
|
||||
expect(mockFetch.mock.calls[0][1]).to.have.property('method', 'POST')
|
||||
mockFetch.mockClear()
|
||||
|
||||
expect(store.allListsObject[list.id].accountIds).to.eql(list.accountIds)
|
||||
|
||||
await store.setListAccounts({
|
||||
|
|
@ -80,12 +108,19 @@ describe('The lists store', () => {
|
|||
accountIds: modList.accountIds,
|
||||
})
|
||||
|
||||
expect(mockFetch).to.have.been.calledTwice
|
||||
expect(mockFetch.mock.calls[0][0]).to.eql(MASTODON_LIST_ACCOUNTS_URL('1'))
|
||||
expect(mockFetch.mock.calls[0][1]).to.have.property('method', 'POST')
|
||||
expect(mockFetch.mock.calls[1][0]).to.eql(MASTODON_LIST_ACCOUNTS_URL('1'))
|
||||
expect(mockFetch.mock.calls[1][1]).to.have.property('method', 'DELETE')
|
||||
|
||||
expect(store.allListsObject[modList.id].accountIds).to.eql(
|
||||
modList.accountIds,
|
||||
)
|
||||
})
|
||||
|
||||
it('deletes a list', async ({ worker }) => {
|
||||
it('deletes a list', async () => {
|
||||
const store = useListsStore()
|
||||
store.$patch({
|
||||
allLists: [{ id: '1', title: 'testList' }],
|
||||
allListsObject: {
|
||||
|
|
@ -94,11 +129,12 @@ describe('The lists store', () => {
|
|||
})
|
||||
const listId = '1'
|
||||
|
||||
worker.use(
|
||||
http.delete(MASTODON_LIST_URL(':id'), () =>
|
||||
HttpResponse.json({ ok: true }),
|
||||
),
|
||||
const mockFetch = vi.fn().mockResolvedValueOnce(
|
||||
new Response(JSON.stringify({ ok: true }), {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
}),
|
||||
)
|
||||
vi.stubGlobal('fetch', mockFetch)
|
||||
|
||||
await store.deleteList({ listId })
|
||||
expect(store.allLists).to.have.length(0)
|
||||
|
|
@ -108,6 +144,7 @@ describe('The lists store', () => {
|
|||
|
||||
describe('getters', () => {
|
||||
it('returns list title', () => {
|
||||
const store = useListsStore()
|
||||
store.$patch({
|
||||
allLists: [{ id: '1', title: 'testList' }],
|
||||
allListsObject: {
|
||||
|
|
@ -120,6 +157,7 @@ describe('The lists store', () => {
|
|||
})
|
||||
|
||||
it('returns list accounts', () => {
|
||||
const store = useListsStore()
|
||||
store.$patch({
|
||||
allLists: [{ id: '1', title: 'testList' }],
|
||||
allListsObject: {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
import { createTestingPinia } from '@pinia/testing'
|
||||
import { HttpResponse, http } from 'msw'
|
||||
import { setActivePinia } from 'pinia'
|
||||
|
||||
import { test as it } from '/test/fixtures/mock_api.js'
|
||||
|
||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||
|
||||
import {
|
||||
|
|
@ -12,51 +9,67 @@ import {
|
|||
OAUTH_TOKEN_URL,
|
||||
} from 'src/api/oauth.js'
|
||||
|
||||
const authApis = () => [
|
||||
http.post(MASTODON_APP_URL, () => {
|
||||
return HttpResponse.json({
|
||||
client_id: 'test-id',
|
||||
client_secret: 'test-secret',
|
||||
})
|
||||
}),
|
||||
http.get(MASTODON_APP_VERIFY_URL, ({ request }) => {
|
||||
const authHeader = request.headers.get('Authorization')
|
||||
if (
|
||||
authHeader === 'Bearer test-app-token' ||
|
||||
authHeader === 'Bearer also-good-app-token'
|
||||
) {
|
||||
return HttpResponse.json({})
|
||||
} else {
|
||||
// Pleroma 2.9.0 gives the following respoonse upon error
|
||||
return HttpResponse.json(
|
||||
{ error: { detail: 'Internal server error' } },
|
||||
{
|
||||
status: 400,
|
||||
},
|
||||
)
|
||||
}
|
||||
}),
|
||||
http.post(OAUTH_TOKEN_URL, async ({ request }) => {
|
||||
const data = await request.formData()
|
||||
const response = (data, extra = {}) =>
|
||||
new Response(JSON.stringify(data), {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
...extra,
|
||||
})
|
||||
|
||||
if (
|
||||
data.get('client_id') === 'test-id' &&
|
||||
data.get('client_secret') === 'test-secret' &&
|
||||
data.get('grant_type') === 'client_credentials' &&
|
||||
data.has('redirect_uri')
|
||||
) {
|
||||
return HttpResponse.json({ access_token: 'test-app-token' })
|
||||
const defaultMockAppURL = () => {
|
||||
return response({
|
||||
client_id: 'test-id',
|
||||
client_secret: 'test-secret',
|
||||
})
|
||||
}
|
||||
|
||||
const defaultMockAppVerifyURL = (headers) => {
|
||||
const authHeader = headers.Authorization
|
||||
if (
|
||||
authHeader === 'Bearer test-app-token' ||
|
||||
authHeader === 'Bearer also-good-app-token'
|
||||
) {
|
||||
return response({})
|
||||
} else {
|
||||
return response(
|
||||
{ error: { detail: 'Internal server error' } },
|
||||
{ status: 400 },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const defaultMockOAuthTokenURL = (params) => {
|
||||
const data = params.body
|
||||
|
||||
if (
|
||||
data.get('client_id') === 'test-id' &&
|
||||
data.get('client_secret') === 'test-secret' &&
|
||||
data.get('grant_type') === 'client_credentials' &&
|
||||
data.has('redirect_uri')
|
||||
) {
|
||||
return response({ access_token: 'test-app-token' })
|
||||
} else {
|
||||
// Pleroma 2.9.0 gives the following respoonse upon error
|
||||
return response({ error: 'Invalid credentials' }, { status: 400 })
|
||||
}
|
||||
}
|
||||
|
||||
const authApis = ({ mockAppURL, mockAppVerifyURL, mockOAuthTokenURL } = {}) => {
|
||||
const mockFetch = vi.fn().mockImplementation((url, params) => {
|
||||
const { method = 'GET', headers } = params
|
||||
|
||||
if (url === MASTODON_APP_URL && method === 'POST') {
|
||||
return (mockAppURL ?? defaultMockAppURL)()
|
||||
} else if (url === MASTODON_APP_VERIFY_URL && method === 'GET') {
|
||||
return (mockAppVerifyURL ?? defaultMockAppVerifyURL)(headers)
|
||||
} else if (url === OAUTH_TOKEN_URL && method === 'POST') {
|
||||
return (mockOAuthTokenURL ?? defaultMockOAuthTokenURL)(params)
|
||||
} else {
|
||||
// Pleroma 2.9.0 gives the following respoonse upon error
|
||||
return HttpResponse.json(
|
||||
{ error: 'Invalid credentials' },
|
||||
{
|
||||
status: 400,
|
||||
},
|
||||
)
|
||||
return response({ error: 'Invalid request for test' }, { status: 401 })
|
||||
}
|
||||
}),
|
||||
]
|
||||
})
|
||||
vi.stubGlobal('fetch', mockFetch)
|
||||
return mockFetch
|
||||
}
|
||||
|
||||
describe('oauth store', () => {
|
||||
beforeEach(() => {
|
||||
|
|
@ -64,17 +77,10 @@ describe('oauth store', () => {
|
|||
})
|
||||
|
||||
describe('createApp', () => {
|
||||
it('should use create an app and record client id and secret', async ({
|
||||
worker,
|
||||
}) => {
|
||||
worker.use(
|
||||
http.post(MASTODON_APP_URL, () => {
|
||||
return HttpResponse.text('Throttled', { status: 429 })
|
||||
}),
|
||||
)
|
||||
it('should use create an app and record client id and secret', async () => {
|
||||
authApis()
|
||||
|
||||
const store = useOAuthStore()
|
||||
worker.use(...authApis())
|
||||
const app = await store.createApp()
|
||||
expect(store.clientId).to.eql('test-id')
|
||||
expect(store.clientSecret).to.eql('test-secret')
|
||||
|
|
@ -82,10 +88,13 @@ describe('oauth store', () => {
|
|||
expect(app.clientSecret).to.eql('test-secret')
|
||||
})
|
||||
|
||||
it('should throw and not update if failed', async ({ worker }) => {
|
||||
worker.use(
|
||||
http.post(MASTODON_APP_URL, () => {
|
||||
return HttpResponse.text('Throttled', { status: 429 })
|
||||
it('should throw and not update if failed', async () => {
|
||||
const mockFetch = authApis()
|
||||
mockFetch.mockResolvedValueOnce(
|
||||
new Response('Throttled', {
|
||||
status: 429,
|
||||
statusText: 'Throttled',
|
||||
headers: { 'Content-Type': 'text/plain' },
|
||||
}),
|
||||
)
|
||||
|
||||
|
|
@ -98,8 +107,8 @@ describe('oauth store', () => {
|
|||
})
|
||||
|
||||
describe('ensureApp', () => {
|
||||
it('should create an app if it does not exist', async ({ worker }) => {
|
||||
worker.use(...authApis())
|
||||
it('should create an app if it does not exist', async () => {
|
||||
authApis()
|
||||
const store = useOAuthStore()
|
||||
const app = await store.ensureApp()
|
||||
expect(store.clientId).to.eql('test-id')
|
||||
|
|
@ -108,12 +117,15 @@ describe('oauth store', () => {
|
|||
expect(app.clientSecret).to.eql('test-secret')
|
||||
})
|
||||
|
||||
it('should not create an app if it exists', async ({ worker }) => {
|
||||
worker.use(
|
||||
http.post(MASTODON_APP_URL, () => {
|
||||
return HttpResponse.text('Should not call this API', { status: 400 })
|
||||
}),
|
||||
)
|
||||
it('should not create an app if it exists', async () => {
|
||||
authApis({
|
||||
mockAppURL: () =>
|
||||
new Response('Throttled', {
|
||||
status: 429,
|
||||
statusText: 'Throttled',
|
||||
headers: { 'Content-Type': 'text/plain' },
|
||||
}),
|
||||
})
|
||||
|
||||
const store = useOAuthStore()
|
||||
store.clientId = 'another-id'
|
||||
|
|
@ -128,8 +140,8 @@ describe('oauth store', () => {
|
|||
})
|
||||
|
||||
describe('getAppToken', () => {
|
||||
it('should get app token and set it in state', async ({ worker }) => {
|
||||
worker.use(...authApis())
|
||||
it('should get app token and set it in state', async () => {
|
||||
authApis()
|
||||
const store = useOAuthStore()
|
||||
store.clientId = 'test-id'
|
||||
store.clientSecret = 'test-secret'
|
||||
|
|
@ -139,10 +151,8 @@ describe('oauth store', () => {
|
|||
expect(store.appToken).to.eql('test-app-token')
|
||||
})
|
||||
|
||||
it('should throw and not set state if it cannot get app token', async ({
|
||||
worker,
|
||||
}) => {
|
||||
worker.use(...authApis())
|
||||
it('should throw and not set state if it cannot get app token', async () => {
|
||||
authApis()
|
||||
const store = useOAuthStore()
|
||||
store.clientId = 'bad-id'
|
||||
store.clientSecret = 'bad-secret'
|
||||
|
|
@ -153,16 +163,16 @@ describe('oauth store', () => {
|
|||
})
|
||||
|
||||
describe('ensureAppToken', () => {
|
||||
it('should work if the state is empty', async ({ worker }) => {
|
||||
worker.use(...authApis())
|
||||
it('should work if the state is empty', async () => {
|
||||
authApis()
|
||||
const store = useOAuthStore()
|
||||
const token = await store.ensureAppToken()
|
||||
expect(token).to.eql('test-app-token')
|
||||
expect(store.appToken).to.eql('test-app-token')
|
||||
})
|
||||
|
||||
it('should work if we already have a working token', async ({ worker }) => {
|
||||
worker.use(...authApis())
|
||||
it('should work if we already have a working token', async () => {
|
||||
authApis()
|
||||
const store = useOAuthStore()
|
||||
store.appToken = 'also-good-app-token'
|
||||
|
||||
|
|
@ -171,15 +181,16 @@ describe('oauth store', () => {
|
|||
expect(store.appToken).to.eql('also-good-app-token')
|
||||
})
|
||||
|
||||
it('should work if we have a bad token but good app credentials', async ({
|
||||
worker,
|
||||
}) => {
|
||||
worker.use(
|
||||
...authApis(),
|
||||
http.post(MASTODON_APP_URL, () => {
|
||||
return HttpResponse.text('Should not call this API', { status: 400 })
|
||||
}),
|
||||
)
|
||||
it('should work if we have a bad token but good app credentials', async () => {
|
||||
authApis({
|
||||
mockAppURL: () =>
|
||||
new Response('Should not call this API', {
|
||||
status: 400,
|
||||
statusText: 'Should not call this API',
|
||||
headers: { 'Content-Type': 'text/plain' },
|
||||
}),
|
||||
})
|
||||
|
||||
const store = useOAuthStore()
|
||||
store.appToken = 'bad-app-token'
|
||||
store.clientId = 'test-id'
|
||||
|
|
@ -190,15 +201,16 @@ describe('oauth store', () => {
|
|||
expect(store.appToken).to.eql('test-app-token')
|
||||
})
|
||||
|
||||
it('should work if we have no token but good app credentials', async ({
|
||||
worker,
|
||||
}) => {
|
||||
worker.use(
|
||||
...authApis(),
|
||||
http.post(MASTODON_APP_URL, () => {
|
||||
return HttpResponse.text('Should not call this API', { status: 400 })
|
||||
}),
|
||||
)
|
||||
it('should work if we have no token but good app credentials', async () => {
|
||||
authApis({
|
||||
mockAppURL: () =>
|
||||
new Response('Should not call this API', {
|
||||
status: 400,
|
||||
statusText: 'Should not call this API',
|
||||
headers: { 'Content-Type': 'text/plain' },
|
||||
}),
|
||||
})
|
||||
|
||||
const store = useOAuthStore()
|
||||
store.clientId = 'test-id'
|
||||
store.clientSecret = 'test-secret'
|
||||
|
|
@ -208,10 +220,8 @@ describe('oauth store', () => {
|
|||
expect(store.appToken).to.eql('test-app-token')
|
||||
})
|
||||
|
||||
it('should work if we have no token and bad app credentials', async ({
|
||||
worker,
|
||||
}) => {
|
||||
worker.use(...authApis())
|
||||
it('should work if we have no token and bad app credentials', async () => {
|
||||
authApis()
|
||||
const store = useOAuthStore()
|
||||
store.clientId = 'bad-id'
|
||||
store.clientSecret = 'bad-secret'
|
||||
|
|
@ -223,10 +233,8 @@ describe('oauth store', () => {
|
|||
expect(store.clientSecret).to.eql('test-secret')
|
||||
})
|
||||
|
||||
it('should work if we have bad token and bad app credentials', async ({
|
||||
worker,
|
||||
}) => {
|
||||
worker.use(...authApis())
|
||||
it('should work if we have bad token and bad app credentials', async () => {
|
||||
authApis()
|
||||
const store = useOAuthStore()
|
||||
store.appToken = 'bad-app-token'
|
||||
store.clientId = 'bad-id'
|
||||
|
|
@ -239,23 +247,29 @@ describe('oauth store', () => {
|
|||
expect(store.clientSecret).to.eql('test-secret')
|
||||
})
|
||||
|
||||
it('should throw if we cannot create an app', async ({ worker }) => {
|
||||
worker.use(
|
||||
http.post(MASTODON_APP_URL, () => {
|
||||
return HttpResponse.text('Throttled', { status: 429 })
|
||||
}),
|
||||
)
|
||||
it('should throw if we cannot create an app', async () => {
|
||||
authApis({
|
||||
mockAppURL: () =>
|
||||
new Response('Throttled', {
|
||||
status: 429,
|
||||
statusText: 'Throttled',
|
||||
headers: { 'Content-Type': 'text/plain' },
|
||||
}),
|
||||
})
|
||||
|
||||
const store = useOAuthStore()
|
||||
await expect(store.ensureAppToken()).rejects.toThrowError('Throttled')
|
||||
})
|
||||
|
||||
it('should throw if we cannot obtain app token', async ({ worker }) => {
|
||||
worker.use(
|
||||
http.post(OAUTH_TOKEN_URL, () => {
|
||||
return HttpResponse.text('Throttled', { status: 429 })
|
||||
}),
|
||||
)
|
||||
it('should throw if we cannot obtain app token', async () => {
|
||||
authApis({
|
||||
mockOAuthTokenURL: () =>
|
||||
new Response('Throttled', {
|
||||
status: 429,
|
||||
statusText: 'Throttled',
|
||||
headers: { 'Content-Type': 'text/plain' },
|
||||
}),
|
||||
})
|
||||
|
||||
const store = useOAuthStore()
|
||||
await expect(store.getAppToken()).rejects.toThrowError('Throttled')
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import { configDefaults } from 'vitest/config'
|
|||
import { getCommitHash } from './build/commit_hash.js'
|
||||
import copyPlugin from './build/copy_plugin.js'
|
||||
import emojisPlugin from './build/emojis_plugin.js'
|
||||
import mswPlugin from './build/msw_plugin.js'
|
||||
import { buildSwPlugin, swMessagesPlugin } from './build/sw_plugin.js'
|
||||
|
||||
const localConfigPath = '<projectRoot>/config/local.json'
|
||||
|
|
@ -163,7 +162,6 @@ export default defineConfig(async ({ mode, command }) => {
|
|||
'node_modules/.cache/stylelintcache',
|
||||
),
|
||||
}),
|
||||
...(mode === 'test' ? [mswPlugin()] : []),
|
||||
],
|
||||
css: {
|
||||
devSourcemap: true,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue