Files

91 lines
3.2 KiB
JavaScript

import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import { flushPromises, mount } from '@vue/test-utils'
import { setupI18n } from '@/i18n'
import en from '@/locales/en.json'
import router from '@/routes'
import App from '@/App.vue'
export const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
export const publicFile = (name) => fs.readFileSync(path.join(root, 'public', name), 'utf8')
// The same i18n the app builds in main.js, with English loaded. The router's
// guard reads window.i18n to switch language, so it is set here too.
export function installI18n(locale = 'en') {
const i18n = setupI18n({ legacy: false, locale, fallbackLocale: 'en', messages: { en } })
window.i18n = i18n
return i18n
}
const reply = (body, ok = true, status = ok ? 200 : 503) => ({
ok,
status,
json: async () => (typeof body === 'string' ? JSON.parse(body) : body),
text: async () => (typeof body === 'string' ? body : JSON.stringify(body)),
})
/**
* Stands in for fetch. Local files come from the repo's public folder; the
* backend feeds can be given as data, or left to answer 503 ("backend down").
* Nothing reaches the network, and a URL nobody planned for fails the test.
*/
export function stubFetch({ servers, metrics } = {}) {
const calls = []
const stub = async (url) => {
const address = String(url)
calls.push(address)
if (
address.startsWith('/docs/') ||
address === '/partners.json' ||
address === '/servers.json'
) {
return reply(publicFile(address.slice(1)))
}
if (address === 'https://backend.beammp.com/servers-info')
return servers ? reply(servers) : reply('', false)
if (address === 'https://api.beammp.com/metrics')
return metrics ? reply(metrics) : reply('', false)
// The statistics page's release and stats feeds: down unless a test says otherwise.
if (
/^https:\/\/(api|backend)\.beammp\.com\//.test(address) ||
address.startsWith('https://api.github.com/')
)
return reply('', false)
throw new Error(`Unplanned fetch to ${address}`)
}
stub.calls = calls
vi.stubGlobal('fetch', stub)
return stub
}
/**
* Runs `action` (a navigation) and fails it if the router's language guard
* keeps redirecting. A runaway redirect would otherwise freeze the test run
* the way it freezes the page. The guard reads the saved language on every
* redirect, which is what is counted.
*/
export async function withRedirectLimit(action, limit = 5) {
const real = localStorage.getItem.bind(localStorage)
let passes = 0
localStorage.getItem = (key) => {
if (key === 'lang' && ++passes > limit) throw new Error('still redirecting')
return real(key)
}
try {
return await action()
} finally {
localStorage.getItem = real
}
}
/** Opens a page of the real app (router, layout, nav and footer) and lets it finish loading. */
export async function openPage(pathname) {
const i18n = installI18n('en')
await withRedirectLimit(() => router.push(pathname))
await router.isReady()
const wrapper = mount(App, { attachTo: document.body, global: { plugins: [i18n, router] } })
await flushPromises()
return wrapper
}