mirror of
https://github.com/chenasraf/nextcloud-forum.git
synced 2026-05-17 17:28:02 +00:00
test: add basic frontend tests
This commit is contained in:
96
.github/workflows/vitest.yml
vendored
Normal file
96
.github/workflows/vitest.yml
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
# SPDX-FileCopyrightText: Chen Asraf <contact@casraf.dev>
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
name: Vitest
|
||||
|
||||
on: pull_request
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: vitest-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
changes:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
|
||||
outputs:
|
||||
src: ${{ steps.changes.outputs.src }}
|
||||
|
||||
steps:
|
||||
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
|
||||
id: changes
|
||||
continue-on-error: true
|
||||
with:
|
||||
filters: |
|
||||
src:
|
||||
- '.github/workflows/**'
|
||||
- 'src/**'
|
||||
- 'package.json'
|
||||
- 'pnpm-lock.yaml'
|
||||
- 'tsconfig.json'
|
||||
- 'tsconfig.*.json'
|
||||
- 'vite.config.ts'
|
||||
- 'vitest.config.ts'
|
||||
|
||||
vitest:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
needs: changes
|
||||
if: needs.changes.outputs.src != 'false'
|
||||
|
||||
name: Vitest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
node-version: 22
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
||||
with:
|
||||
run_install: false
|
||||
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
|
||||
- name: Setup pnpm cache
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Run tests
|
||||
run: pnpm test:run
|
||||
|
||||
summary:
|
||||
permissions:
|
||||
contents: none
|
||||
runs-on: ubuntu-latest
|
||||
needs: [changes, vitest]
|
||||
|
||||
if: always()
|
||||
|
||||
name: vitest-summary
|
||||
|
||||
steps:
|
||||
- name: Summary status
|
||||
run: if ${{ needs.changes.outputs.src != 'false' && needs.vitest.result != 'success' }}; then exit 1; fi
|
||||
@@ -13,7 +13,9 @@
|
||||
"lint": "eslint src",
|
||||
"format": "eslint --fix src && prettier --write {vite.config.ts,src/,README.md}",
|
||||
"prepare": "husky",
|
||||
"gen": "simple-scaffold -c . -k"
|
||||
"gen": "simple-scaffold -c . -k",
|
||||
"test": "vitest",
|
||||
"test:run": "vitest run"
|
||||
},
|
||||
"browserslist": [
|
||||
"extends @nextcloud/browserslist-config"
|
||||
@@ -36,8 +38,11 @@
|
||||
"@nextcloud/browserslist-config": "^3.1.2",
|
||||
"@nextcloud/eslint-config": "^8.4.2",
|
||||
"@nextcloud/stylelint-config": "^3.1.1",
|
||||
"@vitejs/plugin-vue": "^6.0.3",
|
||||
"@vue/test-utils": "^2.4.6",
|
||||
"@vue/tsconfig": "^0.8.1",
|
||||
"eslint": "^9.39.2",
|
||||
"happy-dom": "^20.0.11",
|
||||
"husky": "^9.1.7",
|
||||
"lint-staged": "^16.2.7",
|
||||
"prettier": "^2.8.8",
|
||||
@@ -49,6 +54,7 @@
|
||||
"typescript-eslint": "^8.51.0",
|
||||
"vite": "^6.4.1",
|
||||
"vite-plugin-checker": "^0.12.0",
|
||||
"vitest": "^4.0.16",
|
||||
"vue-router": "^4.6.4",
|
||||
"vue-tsc": "^2.2.12"
|
||||
}
|
||||
|
||||
487
pnpm-lock.yaml
generated
487
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
178
src/components/Pagination.test.ts
Normal file
178
src/components/Pagination.test.ts
Normal file
@@ -0,0 +1,178 @@
|
||||
import { describe, it, expect, vi } from 'vitest'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import Pagination from './Pagination.vue'
|
||||
|
||||
// Mock @nextcloud/l10n
|
||||
vi.mock('@nextcloud/l10n', () => ({
|
||||
t: (app: string, text: string, vars?: Record<string, unknown>) => {
|
||||
if (vars) {
|
||||
return Object.entries(vars).reduce(
|
||||
(acc, [key, value]) => acc.replace(`{${key}}`, String(value)),
|
||||
text,
|
||||
)
|
||||
}
|
||||
return text
|
||||
},
|
||||
}))
|
||||
|
||||
// Mock @nextcloud/vue/components/NcButton
|
||||
vi.mock('@nextcloud/vue/components/NcButton', () => ({
|
||||
default: {
|
||||
name: 'NcButton',
|
||||
template:
|
||||
'<button :disabled="disabled" @click="$emit(\'click\')"><slot /><slot name="icon" /></button>',
|
||||
props: ['variant', 'disabled', 'ariaLabel', 'title'],
|
||||
},
|
||||
}))
|
||||
|
||||
// Mock icon components
|
||||
vi.mock('@icons/PageFirst.vue', () => ({
|
||||
default: { name: 'PageFirstIcon', template: '<span>«</span>', props: ['size'] },
|
||||
}))
|
||||
vi.mock('@icons/PageLast.vue', () => ({
|
||||
default: { name: 'PageLastIcon', template: '<span>»</span>', props: ['size'] },
|
||||
}))
|
||||
vi.mock('@icons/ChevronLeft.vue', () => ({
|
||||
default: { name: 'ChevronLeftIcon', template: '<span>‹</span>', props: ['size'] },
|
||||
}))
|
||||
vi.mock('@icons/ChevronRight.vue', () => ({
|
||||
default: { name: 'ChevronRightIcon', template: '<span>›</span>', props: ['size'] },
|
||||
}))
|
||||
|
||||
describe('Pagination', () => {
|
||||
describe('visibility', () => {
|
||||
it('should not render when maxPages is 1', () => {
|
||||
const wrapper = mount(Pagination, {
|
||||
props: { currentPage: 1, maxPages: 1 },
|
||||
})
|
||||
expect(wrapper.find('nav').exists()).toBe(false)
|
||||
})
|
||||
|
||||
it('should render when maxPages is greater than 1', () => {
|
||||
const wrapper = mount(Pagination, {
|
||||
props: { currentPage: 1, maxPages: 2 },
|
||||
})
|
||||
expect(wrapper.find('nav').exists()).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('pageItems calculation', () => {
|
||||
it('should show all pages when maxPages <= 10', () => {
|
||||
const wrapper = mount(Pagination, {
|
||||
props: { currentPage: 1, maxPages: 5 },
|
||||
})
|
||||
// Access the computed property via vm
|
||||
const pageItems = (wrapper.vm as unknown as { pageItems: (number | 'ellipsis')[] }).pageItems
|
||||
expect(pageItems).toEqual([1, 2, 3, 4, 5])
|
||||
})
|
||||
|
||||
it('should show all pages when maxPages is exactly 10', () => {
|
||||
const wrapper = mount(Pagination, {
|
||||
props: { currentPage: 5, maxPages: 10 },
|
||||
})
|
||||
const pageItems = (wrapper.vm as unknown as { pageItems: (number | 'ellipsis')[] }).pageItems
|
||||
expect(pageItems).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
|
||||
})
|
||||
|
||||
it('should add ellipsis for pages > 10 when on first page', () => {
|
||||
const wrapper = mount(Pagination, {
|
||||
props: { currentPage: 1, maxPages: 20 },
|
||||
})
|
||||
const pageItems = (wrapper.vm as unknown as { pageItems: (number | 'ellipsis')[] }).pageItems
|
||||
// Should show: 1, 2, 3 (first 3) + ellipsis + 18, 19, 20 (last 3)
|
||||
expect(pageItems).toEqual([1, 2, 3, 'ellipsis', 18, 19, 20])
|
||||
})
|
||||
|
||||
it('should add ellipsis for pages > 10 when on last page', () => {
|
||||
const wrapper = mount(Pagination, {
|
||||
props: { currentPage: 20, maxPages: 20 },
|
||||
})
|
||||
const pageItems = (wrapper.vm as unknown as { pageItems: (number | 'ellipsis')[] }).pageItems
|
||||
// Should show: 1, 2, 3 (first 3) + ellipsis + 18, 19, 20 (last 3)
|
||||
expect(pageItems).toEqual([1, 2, 3, 'ellipsis', 18, 19, 20])
|
||||
})
|
||||
|
||||
it('should show pages around current page in the middle', () => {
|
||||
const wrapper = mount(Pagination, {
|
||||
props: { currentPage: 10, maxPages: 20 },
|
||||
})
|
||||
const pageItems = (wrapper.vm as unknown as { pageItems: (number | 'ellipsis')[] }).pageItems
|
||||
// Should show: 1, 2, 3 + ellipsis + 8, 9, 10, 11, 12 + ellipsis + 18, 19, 20
|
||||
expect(pageItems).toEqual([1, 2, 3, 'ellipsis', 8, 9, 10, 11, 12, 'ellipsis', 18, 19, 20])
|
||||
})
|
||||
|
||||
it('should handle edge case where current page is near the start', () => {
|
||||
const wrapper = mount(Pagination, {
|
||||
props: { currentPage: 4, maxPages: 20 },
|
||||
})
|
||||
const pageItems = (wrapper.vm as unknown as { pageItems: (number | 'ellipsis')[] }).pageItems
|
||||
// Current=4, so around: 2,3,4,5,6, combined with first 3 (1,2,3): 1,2,3,4,5,6
|
||||
expect(pageItems).toContain(1)
|
||||
expect(pageItems).toContain(4)
|
||||
expect(pageItems).toContain(6)
|
||||
})
|
||||
|
||||
it('should handle edge case where current page is near the end', () => {
|
||||
const wrapper = mount(Pagination, {
|
||||
props: { currentPage: 17, maxPages: 20 },
|
||||
})
|
||||
const pageItems = (wrapper.vm as unknown as { pageItems: (number | 'ellipsis')[] }).pageItems
|
||||
// Current=17, so around: 15,16,17,18,19, combined with last 3 (18,19,20): 15,16,17,18,19,20
|
||||
expect(pageItems).toContain(15)
|
||||
expect(pageItems).toContain(17)
|
||||
expect(pageItems).toContain(20)
|
||||
})
|
||||
})
|
||||
|
||||
describe('navigation', () => {
|
||||
it('should emit update:currentPage when going to a page', async () => {
|
||||
const wrapper = mount(Pagination, {
|
||||
props: { currentPage: 5, maxPages: 10 },
|
||||
})
|
||||
|
||||
// Find all buttons and click the one for page 3
|
||||
const buttons = wrapper.findAll('button')
|
||||
const page3Button = buttons.find((btn) => btn.text() === '3')
|
||||
expect(page3Button).toBeDefined()
|
||||
|
||||
await page3Button!.trigger('click')
|
||||
expect(wrapper.emitted('update:currentPage')).toBeTruthy()
|
||||
expect(wrapper.emitted('update:currentPage')![0]).toEqual([3])
|
||||
})
|
||||
|
||||
it('should not emit when clicking current page', async () => {
|
||||
const wrapper = mount(Pagination, {
|
||||
props: { currentPage: 5, maxPages: 10 },
|
||||
})
|
||||
|
||||
const buttons = wrapper.findAll('button')
|
||||
const page5Button = buttons.find((btn) => btn.text() === '5')
|
||||
|
||||
await page5Button!.trigger('click')
|
||||
expect(wrapper.emitted('update:currentPage')).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should disable first/previous buttons on first page', () => {
|
||||
const wrapper = mount(Pagination, {
|
||||
props: { currentPage: 1, maxPages: 10 },
|
||||
})
|
||||
|
||||
const buttons = wrapper.findAll('button')
|
||||
// First two buttons are first page and previous page
|
||||
expect(buttons[0].attributes('disabled')).toBeDefined()
|
||||
expect(buttons[1].attributes('disabled')).toBeDefined()
|
||||
})
|
||||
|
||||
it('should disable next/last buttons on last page', () => {
|
||||
const wrapper = mount(Pagination, {
|
||||
props: { currentPage: 10, maxPages: 10 },
|
||||
})
|
||||
|
||||
const buttons = wrapper.findAll('button')
|
||||
// Last two buttons are next page and last page
|
||||
const lastIdx = buttons.length - 1
|
||||
expect(buttons[lastIdx].attributes('disabled')).toBeDefined()
|
||||
expect(buttons[lastIdx - 1].attributes('disabled')).toBeDefined()
|
||||
})
|
||||
})
|
||||
})
|
||||
188
src/components/RoleBadge.test.ts
Normal file
188
src/components/RoleBadge.test.ts
Normal file
@@ -0,0 +1,188 @@
|
||||
import { describe, it, expect, vi } from 'vitest'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import RoleBadge from './RoleBadge.vue'
|
||||
import type { Role } from '@/types/models'
|
||||
|
||||
// Mock isDarkTheme - default to light theme
|
||||
vi.mock('@nextcloud/vue/functions/isDarkTheme', () => ({
|
||||
isDarkTheme: false,
|
||||
}))
|
||||
|
||||
// Helper to create a mock role
|
||||
function createMockRole(overrides: Partial<Role> = {}): Role {
|
||||
return {
|
||||
id: 100,
|
||||
name: 'Test Role',
|
||||
description: null,
|
||||
colorLight: null,
|
||||
colorDark: null,
|
||||
canAccessAdminTools: false,
|
||||
canEditRoles: false,
|
||||
canEditCategories: false,
|
||||
isSystemRole: false,
|
||||
roleType: 'custom',
|
||||
createdAt: Date.now(),
|
||||
...overrides,
|
||||
}
|
||||
}
|
||||
|
||||
describe('RoleBadge', () => {
|
||||
describe('rendering', () => {
|
||||
it('should display the role name', () => {
|
||||
const role = createMockRole({ name: 'Super Admin' })
|
||||
const wrapper = mount(RoleBadge, {
|
||||
props: { role },
|
||||
})
|
||||
expect(wrapper.text()).toBe('Super Admin')
|
||||
})
|
||||
|
||||
it('should apply normal density class by default', () => {
|
||||
const role = createMockRole()
|
||||
const wrapper = mount(RoleBadge, {
|
||||
props: { role },
|
||||
})
|
||||
expect(wrapper.find('.role-badge').classes()).toContain('density-normal')
|
||||
})
|
||||
|
||||
it('should apply compact density class when specified', () => {
|
||||
const role = createMockRole()
|
||||
const wrapper = mount(RoleBadge, {
|
||||
props: { role, density: 'compact' },
|
||||
})
|
||||
expect(wrapper.find('.role-badge').classes()).toContain('density-compact')
|
||||
})
|
||||
})
|
||||
|
||||
describe('color calculation', () => {
|
||||
it('should use colorLight when provided (light theme)', () => {
|
||||
const role = createMockRole({ colorLight: '#ff5500' })
|
||||
const wrapper = mount(RoleBadge, {
|
||||
props: { role },
|
||||
})
|
||||
const style = wrapper.find('.role-badge').attributes('style')
|
||||
expect(style).toContain('background-color: #ff5500')
|
||||
})
|
||||
|
||||
it('should use fallback color for Admin role (id=1)', () => {
|
||||
const role = createMockRole({ id: 1, name: 'Admin', roleType: 'admin' })
|
||||
const wrapper = mount(RoleBadge, {
|
||||
props: { role },
|
||||
})
|
||||
const style = wrapper.find('.role-badge').attributes('style')
|
||||
// Fallback light color for Admin is #dc2626
|
||||
expect(style).toContain('background-color: #dc2626')
|
||||
})
|
||||
|
||||
it('should use fallback color for Moderator role (id=2)', () => {
|
||||
const role = createMockRole({ id: 2, name: 'Moderator', roleType: 'moderator' })
|
||||
const wrapper = mount(RoleBadge, {
|
||||
props: { role },
|
||||
})
|
||||
const style = wrapper.find('.role-badge').attributes('style')
|
||||
// Fallback light color for Moderator is #2563eb
|
||||
expect(style).toContain('background-color: #2563eb')
|
||||
})
|
||||
|
||||
it('should use fallback color for User role (id=3)', () => {
|
||||
const role = createMockRole({ id: 3, name: 'User', roleType: 'default' })
|
||||
const wrapper = mount(RoleBadge, {
|
||||
props: { role },
|
||||
})
|
||||
const style = wrapper.find('.role-badge').attributes('style')
|
||||
// Fallback light color for User is #059669
|
||||
expect(style).toContain('background-color: #059669')
|
||||
})
|
||||
|
||||
it('should use default fallback for custom roles without colors', () => {
|
||||
const role = createMockRole({ id: 999, name: 'Custom' })
|
||||
const wrapper = mount(RoleBadge, {
|
||||
props: { role },
|
||||
})
|
||||
const style = wrapper.find('.role-badge').attributes('style')
|
||||
// Default light fallback is #000000
|
||||
expect(style).toContain('background-color: #000000')
|
||||
})
|
||||
})
|
||||
|
||||
describe('text color calculation (contrast)', () => {
|
||||
it('should use dark text on light backgrounds', () => {
|
||||
// White background should have black text
|
||||
const role = createMockRole({ colorLight: '#ffffff' })
|
||||
const wrapper = mount(RoleBadge, {
|
||||
props: { role },
|
||||
})
|
||||
const style = wrapper.find('.role-badge').attributes('style')
|
||||
expect(style).toContain('color: #000000')
|
||||
})
|
||||
|
||||
it('should use light text on dark backgrounds', () => {
|
||||
// Black background should have white text
|
||||
const role = createMockRole({ colorLight: '#000000' })
|
||||
const wrapper = mount(RoleBadge, {
|
||||
props: { role },
|
||||
})
|
||||
const style = wrapper.find('.role-badge').attributes('style')
|
||||
expect(style).toContain('color: #ffffff')
|
||||
})
|
||||
|
||||
it('should use light text on moderately dark backgrounds', () => {
|
||||
// Dark blue should have white text
|
||||
const role = createMockRole({ colorLight: '#1e3a5f' })
|
||||
const wrapper = mount(RoleBadge, {
|
||||
props: { role },
|
||||
})
|
||||
const style = wrapper.find('.role-badge').attributes('style')
|
||||
expect(style).toContain('color: #ffffff')
|
||||
})
|
||||
|
||||
it('should use dark text on moderately light backgrounds', () => {
|
||||
// Light yellow should have black text
|
||||
const role = createMockRole({ colorLight: '#ffeb3b' })
|
||||
const wrapper = mount(RoleBadge, {
|
||||
props: { role },
|
||||
})
|
||||
const style = wrapper.find('.role-badge').attributes('style')
|
||||
expect(style).toContain('color: #000000')
|
||||
})
|
||||
})
|
||||
|
||||
describe('hexToRgb method', () => {
|
||||
it('should correctly parse 6-digit hex colors', () => {
|
||||
const role = createMockRole({ colorLight: '#ff5500' })
|
||||
const wrapper = mount(RoleBadge, {
|
||||
props: { role },
|
||||
})
|
||||
// Access the method via vm
|
||||
const vm = wrapper.vm as unknown as {
|
||||
hexToRgb: (hex: string) => { r: number; g: number; b: number } | null
|
||||
}
|
||||
const result = vm.hexToRgb('#ff5500')
|
||||
expect(result).toEqual({ r: 255, g: 85, b: 0 })
|
||||
})
|
||||
|
||||
it('should correctly parse 3-digit shorthand hex colors', () => {
|
||||
const role = createMockRole({ colorLight: '#f00' })
|
||||
const wrapper = mount(RoleBadge, {
|
||||
props: { role },
|
||||
})
|
||||
const vm = wrapper.vm as unknown as {
|
||||
hexToRgb: (hex: string) => { r: number; g: number; b: number } | null
|
||||
}
|
||||
// #f00 expands to #ff0000
|
||||
const result = vm.hexToRgb('#f00')
|
||||
expect(result).toEqual({ r: 255, g: 0, b: 0 })
|
||||
})
|
||||
|
||||
it('should handle hex without # prefix', () => {
|
||||
const role = createMockRole({ colorLight: '#00ff00' })
|
||||
const wrapper = mount(RoleBadge, {
|
||||
props: { role },
|
||||
})
|
||||
const vm = wrapper.vm as unknown as {
|
||||
hexToRgb: (hex: string) => { r: number; g: number; b: number } | null
|
||||
}
|
||||
const result = vm.hexToRgb('00ff00')
|
||||
expect(result).toEqual({ r: 0, g: 255, b: 0 })
|
||||
})
|
||||
})
|
||||
})
|
||||
136
src/constants.test.ts
Normal file
136
src/constants.test.ts
Normal file
@@ -0,0 +1,136 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import {
|
||||
RoleType,
|
||||
isSystemRole,
|
||||
isAdminRole,
|
||||
isModeratorRole,
|
||||
isDefaultRole,
|
||||
isGuestRole,
|
||||
isCustomRole,
|
||||
} from './constants'
|
||||
import type { Role } from './types/models'
|
||||
|
||||
// Helper to create a mock role
|
||||
function createMockRole(overrides: Partial<Role> = {}): Role {
|
||||
return {
|
||||
id: 1,
|
||||
name: 'Test Role',
|
||||
description: null,
|
||||
colorLight: null,
|
||||
colorDark: null,
|
||||
canAccessAdminTools: false,
|
||||
canEditRoles: false,
|
||||
canEditCategories: false,
|
||||
isSystemRole: false,
|
||||
roleType: 'custom',
|
||||
createdAt: Date.now(),
|
||||
...overrides,
|
||||
}
|
||||
}
|
||||
|
||||
describe('RoleType constants', () => {
|
||||
it('should have correct values', () => {
|
||||
expect(RoleType.ADMIN).toBe('admin')
|
||||
expect(RoleType.MODERATOR).toBe('moderator')
|
||||
expect(RoleType.DEFAULT).toBe('default')
|
||||
expect(RoleType.GUEST).toBe('guest')
|
||||
expect(RoleType.CUSTOM).toBe('custom')
|
||||
})
|
||||
})
|
||||
|
||||
describe('isSystemRole', () => {
|
||||
it('should return true for system roles', () => {
|
||||
const role = createMockRole({ isSystemRole: true })
|
||||
expect(isSystemRole(role)).toBe(true)
|
||||
})
|
||||
|
||||
it('should return false for non-system roles', () => {
|
||||
const role = createMockRole({ isSystemRole: false })
|
||||
expect(isSystemRole(role)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('isAdminRole', () => {
|
||||
it('should return true for admin roles', () => {
|
||||
const role = createMockRole({ roleType: 'admin' })
|
||||
expect(isAdminRole(role)).toBe(true)
|
||||
})
|
||||
|
||||
it('should return false for non-admin roles', () => {
|
||||
const role = createMockRole({ roleType: 'moderator' })
|
||||
expect(isAdminRole(role)).toBe(false)
|
||||
})
|
||||
|
||||
it('should return false for null/undefined', () => {
|
||||
expect(isAdminRole(null)).toBe(false)
|
||||
expect(isAdminRole(undefined)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('isModeratorRole', () => {
|
||||
it('should return true for moderator roles', () => {
|
||||
const role = createMockRole({ roleType: 'moderator' })
|
||||
expect(isModeratorRole(role)).toBe(true)
|
||||
})
|
||||
|
||||
it('should return false for non-moderator roles', () => {
|
||||
const role = createMockRole({ roleType: 'admin' })
|
||||
expect(isModeratorRole(role)).toBe(false)
|
||||
})
|
||||
|
||||
it('should return false for null/undefined', () => {
|
||||
expect(isModeratorRole(null)).toBe(false)
|
||||
expect(isModeratorRole(undefined)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('isDefaultRole', () => {
|
||||
it('should return true for default roles', () => {
|
||||
const role = createMockRole({ roleType: 'default' })
|
||||
expect(isDefaultRole(role)).toBe(true)
|
||||
})
|
||||
|
||||
it('should return false for non-default roles', () => {
|
||||
const role = createMockRole({ roleType: 'admin' })
|
||||
expect(isDefaultRole(role)).toBe(false)
|
||||
})
|
||||
|
||||
it('should return false for null/undefined', () => {
|
||||
expect(isDefaultRole(null)).toBe(false)
|
||||
expect(isDefaultRole(undefined)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('isGuestRole', () => {
|
||||
it('should return true for guest roles', () => {
|
||||
const role = createMockRole({ roleType: 'guest' })
|
||||
expect(isGuestRole(role)).toBe(true)
|
||||
})
|
||||
|
||||
it('should return false for non-guest roles', () => {
|
||||
const role = createMockRole({ roleType: 'admin' })
|
||||
expect(isGuestRole(role)).toBe(false)
|
||||
})
|
||||
|
||||
it('should return false for null/undefined', () => {
|
||||
expect(isGuestRole(null)).toBe(false)
|
||||
expect(isGuestRole(undefined)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('isCustomRole', () => {
|
||||
it('should return true for custom roles', () => {
|
||||
const role = createMockRole({ roleType: 'custom' })
|
||||
expect(isCustomRole(role)).toBe(true)
|
||||
})
|
||||
|
||||
it('should return false for non-custom roles', () => {
|
||||
const role = createMockRole({ roleType: 'admin' })
|
||||
expect(isCustomRole(role)).toBe(false)
|
||||
})
|
||||
|
||||
it('should return false for null/undefined', () => {
|
||||
expect(isCustomRole(null)).toBe(false)
|
||||
expect(isCustomRole(undefined)).toBe(false)
|
||||
})
|
||||
})
|
||||
18
vitest.config.ts
Normal file
18
vitest.config.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { defineConfig } from 'vitest/config'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import path from 'path'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, 'src'),
|
||||
'@icons': path.resolve(__dirname, 'node_modules/vue-material-design-icons'),
|
||||
},
|
||||
},
|
||||
test: {
|
||||
environment: 'happy-dom',
|
||||
include: ['src/**/*.{test,spec}.{js,ts}'],
|
||||
globals: true,
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user