156 lines
5.1 KiB
TypeScript
156 lines
5.1 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
import { mount, flushPromises, type VueWrapper } from '@vue/test-utils'
|
|
import { createPinia, setActivePinia } from 'pinia'
|
|
import uiPlugin from '@nuxt/ui/vue-plugin'
|
|
import router from '@/router'
|
|
import { useApi, type UserRole } from '@/composables/useApi'
|
|
import type { UpdateStatus } from '@/composables/useUpdate'
|
|
import { useSessionStore } from '@/stores/session'
|
|
import SettingsView from '../SettingsView.vue'
|
|
import {
|
|
RUNNING_VERSION,
|
|
STATUS_PATH,
|
|
findButton,
|
|
newerAvailable,
|
|
stubClipboard,
|
|
upToDate,
|
|
visibleButtonLabels,
|
|
DEFAULT_PURGE_SETTINGS,
|
|
PURGE_SETTINGS_PATH,
|
|
} from './updateTestKit'
|
|
|
|
vi.mock('@/composables/useApi', async (importOriginal) => {
|
|
const actual = await importOriginal<typeof import('@/composables/useApi')>()
|
|
return {
|
|
...actual,
|
|
useApi: vi.fn(),
|
|
}
|
|
})
|
|
|
|
const CONTAINER_COMMAND = 'docker compose pull && docker compose up -d'
|
|
|
|
let wrapper: VueWrapper | null = null
|
|
|
|
async function mountSettings(role: UserRole, status: UpdateStatus) {
|
|
setActivePinia(createPinia())
|
|
const session = useSessionStore()
|
|
session.session = { verified: true, username: 'marta', id: 1, role }
|
|
session.status = 'ready'
|
|
const request = vi.fn((path: string) => {
|
|
if (path === '/v1/settings/') {
|
|
return Promise.resolve({ version: RUNNING_VERSION })
|
|
}
|
|
if (path === STATUS_PATH) {
|
|
return Promise.resolve(status)
|
|
}
|
|
if (path === PURGE_SETTINGS_PATH) {
|
|
return Promise.resolve(DEFAULT_PURGE_SETTINGS)
|
|
}
|
|
throw new Error(`Unexpected request: ${path}`)
|
|
})
|
|
vi.mocked(useApi).mockReturnValue({
|
|
getMe: vi.fn().mockResolvedValue({
|
|
verified: true,
|
|
username: 'marta',
|
|
id: 1,
|
|
displayName: '',
|
|
role,
|
|
}),
|
|
request,
|
|
})
|
|
await router.push('/settings')
|
|
wrapper = mount(SettingsView, {
|
|
attachTo: document.body,
|
|
global: { plugins: [router, uiPlugin] },
|
|
})
|
|
await flushPromises()
|
|
}
|
|
|
|
function updatesCard() {
|
|
return document.body.querySelector('[data-testid="updates-card"]')!
|
|
}
|
|
|
|
function cardText() {
|
|
return updatesCard().textContent ?? ''
|
|
}
|
|
|
|
afterEach(async () => {
|
|
wrapper?.unmount()
|
|
wrapper = null
|
|
vi.useRealTimers()
|
|
await router.push('/login')
|
|
document.body.innerHTML = ''
|
|
})
|
|
|
|
describe('Settings, owner, container, update available', () => {
|
|
it('shows the pull-image guidance, the command, Copy and the docs link, with no other button', async () => {
|
|
await mountSettings('owner', newerAvailable({ container: true }))
|
|
|
|
expect(cardText()).toContain('Version 2.3.0-beta is available')
|
|
expect(cardText()).toContain(
|
|
'You have v2.2.0-beta. This site runs in Docker, so it updates by pulling a new image. Run the commands below on the server.',
|
|
)
|
|
expect(updatesCard().querySelector('code')?.textContent).toBe(CONTAINER_COMMAND)
|
|
const link = Array.from(updatesCard().querySelectorAll('a')).find(
|
|
(anchor) => anchor.textContent?.trim() === 'How to update',
|
|
)!
|
|
expect(link.getAttribute('href')).toBe(
|
|
'https://git.example/fsylvain/RenewCMS/src/branch/main/docs/docker.md',
|
|
)
|
|
expect(visibleButtonLabels(updatesCard())).toEqual(["What's new in 2.3.0-beta", 'Copy'])
|
|
expect(cardText()).not.toContain('renewcms enable-browser-updates')
|
|
expect(cardText()).not.toContain('They run this once on the server')
|
|
expect(cardText()).not.toContain("can't update itself from the browser")
|
|
})
|
|
|
|
it('copies the container command and reads Copied for 2 s', async () => {
|
|
const writeText = stubClipboard()
|
|
vi.useFakeTimers({ toFake: ['setTimeout', 'clearTimeout'] })
|
|
await mountSettings('owner', newerAvailable({ container: true }))
|
|
|
|
findButton('Copy')!.click()
|
|
await flushPromises()
|
|
|
|
expect(writeText).toHaveBeenCalledWith(CONTAINER_COMMAND)
|
|
expect(findButton('Copied')!.getAttribute('aria-live')).toBe('polite')
|
|
|
|
await vi.advanceTimersByTimeAsync(2000)
|
|
expect(findButton('Copy')?.textContent?.trim()).toBe('Copy')
|
|
expect(findButton('Copied')).toBeUndefined()
|
|
})
|
|
})
|
|
|
|
describe('Settings, owner, container, up to date', () => {
|
|
it("shows You're up to date with only Check for updates and no container hint", async () => {
|
|
await mountSettings('owner', upToDate({ container: true }))
|
|
|
|
expect(cardText()).toContain("You're up to date")
|
|
expect(visibleButtonLabels(updatesCard())).toEqual(['Check for updates'])
|
|
expect(cardText()).not.toContain('Docker')
|
|
expect(updatesCard().querySelector('code')).toBeNull()
|
|
})
|
|
})
|
|
|
|
describe('Settings, admin, container', () => {
|
|
it('keeps the admin view with no command block', async () => {
|
|
await mountSettings('admin', newerAvailable({ container: true }))
|
|
|
|
expect(cardText()).toContain('Only the owner can update RenewCMS.')
|
|
expect(cardText()).not.toContain('Docker')
|
|
expect(updatesCard().querySelector('code')).toBeNull()
|
|
expect(visibleButtonLabels(updatesCard())).toEqual([])
|
|
})
|
|
})
|
|
|
|
describe('Settings, owner, not container, not prepared', () => {
|
|
it('still shows the enable-browser-updates command', async () => {
|
|
await mountSettings('owner', newerAvailable({ container: false }))
|
|
|
|
expect(updatesCard().querySelector('code')?.textContent).toBe(
|
|
'renewcms enable-browser-updates',
|
|
)
|
|
expect(cardText()).toContain("This site can't update itself from the browser yet.")
|
|
expect(cardText()).not.toContain('Docker')
|
|
})
|
|
})
|