@htlkg/astro 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +265 -0
- package/dist/chunk-33R4URZV.js +59 -0
- package/dist/chunk-33R4URZV.js.map +1 -0
- package/dist/chunk-64USRLVP.js +85 -0
- package/dist/chunk-64USRLVP.js.map +1 -0
- package/dist/chunk-WLOFOVCL.js +210 -0
- package/dist/chunk-WLOFOVCL.js.map +1 -0
- package/dist/chunk-WNMPTDCR.js +73 -0
- package/dist/chunk-WNMPTDCR.js.map +1 -0
- package/dist/chunk-Z2ZAL7KX.js +9 -0
- package/dist/chunk-Z2ZAL7KX.js.map +1 -0
- package/dist/chunk-ZQ4XMJH7.js +1 -0
- package/dist/chunk-ZQ4XMJH7.js.map +1 -0
- package/dist/htlkg/config.js +7 -0
- package/dist/htlkg/config.js.map +1 -0
- package/dist/htlkg/index.js +7 -0
- package/dist/htlkg/index.js.map +1 -0
- package/dist/index.js +64 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/index.js +168 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/utils/hydration.js +21 -0
- package/dist/utils/hydration.js.map +1 -0
- package/dist/utils/index.js +56 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/ssr.js +21 -0
- package/dist/utils/ssr.js.map +1 -0
- package/dist/utils/static.js +19 -0
- package/dist/utils/static.js.map +1 -0
- package/package.json +53 -0
- package/src/__mocks__/astro-middleware.ts +19 -0
- package/src/__mocks__/virtual-htlkg-config.ts +14 -0
- package/src/auth/LoginForm.vue +482 -0
- package/src/auth/LoginPage.astro +70 -0
- package/src/components/PageHeader.astro +145 -0
- package/src/components/Sidebar.astro +157 -0
- package/src/components/Topbar.astro +167 -0
- package/src/components/index.ts +9 -0
- package/src/htlkg/config.test.ts +165 -0
- package/src/htlkg/config.ts +242 -0
- package/src/htlkg/index.ts +245 -0
- package/src/htlkg/virtual-modules.test.ts +158 -0
- package/src/htlkg/virtual-modules.ts +81 -0
- package/src/index.ts +37 -0
- package/src/layouts/AdminLayout.astro +184 -0
- package/src/layouts/AuthLayout.astro +164 -0
- package/src/layouts/BrandLayout.astro +309 -0
- package/src/layouts/DefaultLayout.astro +25 -0
- package/src/layouts/PublicLayout.astro +153 -0
- package/src/layouts/index.ts +10 -0
- package/src/middleware/auth.ts +53 -0
- package/src/middleware/index.ts +31 -0
- package/src/middleware/route-guards.test.ts +182 -0
- package/src/middleware/route-guards.ts +218 -0
- package/src/patterns/admin/DetailPage.astro +195 -0
- package/src/patterns/admin/FormPage.astro +203 -0
- package/src/patterns/admin/ListPage.astro +178 -0
- package/src/patterns/admin/index.ts +9 -0
- package/src/patterns/brand/ConfigPage.astro +128 -0
- package/src/patterns/brand/PortalPage.astro +161 -0
- package/src/patterns/brand/index.ts +8 -0
- package/src/patterns/index.ts +8 -0
- package/src/utils/hydration.test.ts +154 -0
- package/src/utils/hydration.ts +151 -0
- package/src/utils/index.ts +9 -0
- package/src/utils/ssr.test.ts +235 -0
- package/src/utils/ssr.ts +139 -0
- package/src/utils/static.test.ts +144 -0
- package/src/utils/static.ts +144 -0
- package/src/vue-app-setup.ts +88 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* Sidebar Component
|
|
4
|
+
*
|
|
5
|
+
* Navigation sidebar with logo, menu items, and footer.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```astro
|
|
9
|
+
* <Sidebar
|
|
10
|
+
* logo="https://example.com/logo.png"
|
|
11
|
+
* title="Admin"
|
|
12
|
+
* items={sidebarItems}
|
|
13
|
+
* currentPage="dashboard"
|
|
14
|
+
* />
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
interface SidebarItem {
|
|
19
|
+
id: string;
|
|
20
|
+
name: string;
|
|
21
|
+
href: string;
|
|
22
|
+
icon?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface Props {
|
|
26
|
+
logo: string;
|
|
27
|
+
title: string;
|
|
28
|
+
items: SidebarItem[];
|
|
29
|
+
currentPage?: string;
|
|
30
|
+
isOpen?: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const { logo, title, items, currentPage, isOpen = true } = Astro.props;
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
<aside class:list={["sidebar", { "sidebar-closed": !isOpen }]}>
|
|
37
|
+
<div class="sidebar-header">
|
|
38
|
+
<img src={logo} alt={title} class="sidebar-logo" />
|
|
39
|
+
<h2 class="sidebar-title">{title}</h2>
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<nav class="sidebar-nav">
|
|
43
|
+
{
|
|
44
|
+
items.map((item) => (
|
|
45
|
+
<a
|
|
46
|
+
href={item.href}
|
|
47
|
+
class:list={["sidebar-item", { active: currentPage === item.id }]}
|
|
48
|
+
>
|
|
49
|
+
{item.icon && <span class="sidebar-icon">{item.icon}</span>}
|
|
50
|
+
<span class="sidebar-label">{item.name}</span>
|
|
51
|
+
</a>
|
|
52
|
+
))
|
|
53
|
+
}
|
|
54
|
+
</nav>
|
|
55
|
+
|
|
56
|
+
<div class="sidebar-footer">
|
|
57
|
+
<slot name="footer" />
|
|
58
|
+
</div>
|
|
59
|
+
</aside>
|
|
60
|
+
|
|
61
|
+
<style>
|
|
62
|
+
.sidebar {
|
|
63
|
+
width: 16rem;
|
|
64
|
+
background: white;
|
|
65
|
+
border-right: 1px solid #e5e7eb;
|
|
66
|
+
display: flex;
|
|
67
|
+
flex-direction: column;
|
|
68
|
+
transition: width 0.3s ease;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.sidebar-closed {
|
|
72
|
+
width: 4rem;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.sidebar-closed .sidebar-title,
|
|
76
|
+
.sidebar-closed .sidebar-label {
|
|
77
|
+
display: none;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.sidebar-header {
|
|
81
|
+
padding: 1.5rem;
|
|
82
|
+
border-bottom: 1px solid #e5e7eb;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.sidebar-logo {
|
|
86
|
+
height: 2rem;
|
|
87
|
+
margin-bottom: 0.5rem;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.sidebar-title {
|
|
91
|
+
font-size: 1.125rem;
|
|
92
|
+
font-weight: 600;
|
|
93
|
+
color: #111827;
|
|
94
|
+
margin: 0;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.sidebar-nav {
|
|
98
|
+
flex: 1;
|
|
99
|
+
padding: 1rem;
|
|
100
|
+
overflow-y: auto;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.sidebar-item {
|
|
104
|
+
display: flex;
|
|
105
|
+
align-items: center;
|
|
106
|
+
gap: 0.75rem;
|
|
107
|
+
padding: 0.75rem 1rem;
|
|
108
|
+
border-radius: 0.5rem;
|
|
109
|
+
color: #6b7280;
|
|
110
|
+
text-decoration: none;
|
|
111
|
+
font-weight: 500;
|
|
112
|
+
transition: all 0.2s;
|
|
113
|
+
margin-bottom: 0.25rem;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.sidebar-item:hover {
|
|
117
|
+
background: #f3f4f6;
|
|
118
|
+
color: #111827;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.sidebar-item.active {
|
|
122
|
+
background: #eff6ff;
|
|
123
|
+
color: #2563eb;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.sidebar-icon {
|
|
127
|
+
font-size: 1.25rem;
|
|
128
|
+
flex-shrink: 0;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.sidebar-label {
|
|
132
|
+
white-space: nowrap;
|
|
133
|
+
overflow: hidden;
|
|
134
|
+
text-overflow: ellipsis;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.sidebar-footer {
|
|
138
|
+
padding: 1rem;
|
|
139
|
+
border-top: 1px solid #e5e7eb;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/* Responsive */
|
|
143
|
+
@media (max-width: 768px) {
|
|
144
|
+
.sidebar {
|
|
145
|
+
position: fixed;
|
|
146
|
+
left: 0;
|
|
147
|
+
top: 0;
|
|
148
|
+
bottom: 0;
|
|
149
|
+
z-index: 50;
|
|
150
|
+
transform: translateX(-100%);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.sidebar:not(.sidebar-closed) {
|
|
154
|
+
transform: translateX(0);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
</style>
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* Topbar Component
|
|
4
|
+
*
|
|
5
|
+
* Top navigation bar with actions and user menu.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```astro
|
|
9
|
+
* <Topbar
|
|
10
|
+
* user={user}
|
|
11
|
+
* actions={topbarActions}
|
|
12
|
+
* />
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
interface User {
|
|
17
|
+
username: string;
|
|
18
|
+
email: string;
|
|
19
|
+
avatar?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface Action {
|
|
23
|
+
name: string;
|
|
24
|
+
event: string;
|
|
25
|
+
icon?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface Props {
|
|
29
|
+
user: User;
|
|
30
|
+
actions?: Action[];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const { user, actions = [] } = Astro.props;
|
|
34
|
+
|
|
35
|
+
const userAvatar =
|
|
36
|
+
user.avatar ||
|
|
37
|
+
`https://ui-avatars.com/api/?name=${encodeURIComponent(user.username)}&background=3B82F6&color=fff`;
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
<header class="topbar">
|
|
41
|
+
<div class="topbar-left">
|
|
42
|
+
<slot name="left" />
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<div class="topbar-right">
|
|
46
|
+
{
|
|
47
|
+
actions.map((action) => (
|
|
48
|
+
<button class="topbar-action" data-event={action.event}>
|
|
49
|
+
{action.icon && <span class="action-icon">{action.icon}</span>}
|
|
50
|
+
<span class="action-label">{action.name}</span>
|
|
51
|
+
</button>
|
|
52
|
+
))
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
<div class="user-menu">
|
|
56
|
+
<img src={userAvatar} alt={user.username} class="user-avatar" />
|
|
57
|
+
<div class="user-info">
|
|
58
|
+
<div class="user-name">{user.username}</div>
|
|
59
|
+
<div class="user-email">{user.email}</div>
|
|
60
|
+
</div>
|
|
61
|
+
<slot name="user-menu" />
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
</header>
|
|
65
|
+
|
|
66
|
+
<style>
|
|
67
|
+
.topbar {
|
|
68
|
+
background: white;
|
|
69
|
+
border-bottom: 1px solid #e5e7eb;
|
|
70
|
+
padding: 1rem 1.5rem;
|
|
71
|
+
display: flex;
|
|
72
|
+
justify-content: space-between;
|
|
73
|
+
align-items: center;
|
|
74
|
+
gap: 1rem;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.topbar-left {
|
|
78
|
+
display: flex;
|
|
79
|
+
align-items: center;
|
|
80
|
+
gap: 1rem;
|
|
81
|
+
flex: 1;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.topbar-right {
|
|
85
|
+
display: flex;
|
|
86
|
+
align-items: center;
|
|
87
|
+
gap: 1rem;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.topbar-action {
|
|
91
|
+
display: flex;
|
|
92
|
+
align-items: center;
|
|
93
|
+
gap: 0.5rem;
|
|
94
|
+
padding: 0.5rem 1rem;
|
|
95
|
+
border: 1px solid #d1d5db;
|
|
96
|
+
border-radius: 0.5rem;
|
|
97
|
+
background: white;
|
|
98
|
+
color: #374151;
|
|
99
|
+
font-size: 0.875rem;
|
|
100
|
+
font-weight: 500;
|
|
101
|
+
cursor: pointer;
|
|
102
|
+
transition: all 0.2s;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.topbar-action:hover {
|
|
106
|
+
background: #f9fafb;
|
|
107
|
+
border-color: #9ca3af;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.action-icon {
|
|
111
|
+
font-size: 1rem;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.user-menu {
|
|
115
|
+
display: flex;
|
|
116
|
+
align-items: center;
|
|
117
|
+
gap: 0.75rem;
|
|
118
|
+
padding: 0.5rem;
|
|
119
|
+
border-radius: 0.5rem;
|
|
120
|
+
cursor: pointer;
|
|
121
|
+
transition: all 0.2s;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.user-menu:hover {
|
|
125
|
+
background: #f9fafb;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.user-avatar {
|
|
129
|
+
width: 2rem;
|
|
130
|
+
height: 2rem;
|
|
131
|
+
border-radius: 9999px;
|
|
132
|
+
object-fit: cover;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.user-info {
|
|
136
|
+
display: flex;
|
|
137
|
+
flex-direction: column;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.user-name {
|
|
141
|
+
font-size: 0.875rem;
|
|
142
|
+
font-weight: 500;
|
|
143
|
+
color: #111827;
|
|
144
|
+
line-height: 1.2;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.user-email {
|
|
148
|
+
font-size: 0.75rem;
|
|
149
|
+
color: #6b7280;
|
|
150
|
+
line-height: 1.2;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/* Responsive */
|
|
154
|
+
@media (max-width: 768px) {
|
|
155
|
+
.topbar {
|
|
156
|
+
padding: 0.75rem 1rem;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.action-label {
|
|
160
|
+
display: none;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.user-info {
|
|
164
|
+
display: none;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
</style>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @htlkg/pages - Components
|
|
3
|
+
*
|
|
4
|
+
* Reusable page components for building layouts.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export { default as PageHeader } from './PageHeader.astro';
|
|
8
|
+
export { default as Sidebar } from './Sidebar.astro';
|
|
9
|
+
export { default as Topbar } from './Topbar.astro';
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for htlkg configuration types and utilities
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect } from 'vitest';
|
|
6
|
+
import { isAuthenticatedUser } from './config.js';
|
|
7
|
+
|
|
8
|
+
describe('Configuration Utilities', () => {
|
|
9
|
+
describe('isAuthenticatedUser', () => {
|
|
10
|
+
it('should return true for valid AuthUser object', () => {
|
|
11
|
+
const validUser = {
|
|
12
|
+
username: 'testuser',
|
|
13
|
+
email: 'test@example.com',
|
|
14
|
+
brandIds: [1, 2, 3],
|
|
15
|
+
accountIds: [1],
|
|
16
|
+
isAdmin: false,
|
|
17
|
+
roles: ['user'],
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
expect(isAuthenticatedUser(validUser)).toBe(true);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should return true for admin user', () => {
|
|
24
|
+
const adminUser = {
|
|
25
|
+
username: 'admin',
|
|
26
|
+
email: 'admin@example.com',
|
|
27
|
+
brandIds: [],
|
|
28
|
+
accountIds: [1],
|
|
29
|
+
isAdmin: true,
|
|
30
|
+
roles: ['admin'],
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
expect(isAuthenticatedUser(adminUser)).toBe(true);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should return false for null', () => {
|
|
37
|
+
expect(isAuthenticatedUser(null)).toBe(false);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should return false for undefined', () => {
|
|
41
|
+
expect(isAuthenticatedUser(undefined)).toBe(false);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should return false for non-object values', () => {
|
|
45
|
+
expect(isAuthenticatedUser('string')).toBe(false);
|
|
46
|
+
expect(isAuthenticatedUser(123)).toBe(false);
|
|
47
|
+
expect(isAuthenticatedUser(true)).toBe(false);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should return false for object missing username', () => {
|
|
51
|
+
const invalidUser = {
|
|
52
|
+
email: 'test@example.com',
|
|
53
|
+
brandIds: [1],
|
|
54
|
+
accountIds: [1],
|
|
55
|
+
isAdmin: false,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
expect(isAuthenticatedUser(invalidUser)).toBe(false);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('should return false for object missing email', () => {
|
|
62
|
+
const invalidUser = {
|
|
63
|
+
username: 'testuser',
|
|
64
|
+
brandIds: [1],
|
|
65
|
+
accountIds: [1],
|
|
66
|
+
isAdmin: false,
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
expect(isAuthenticatedUser(invalidUser)).toBe(false);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should return false for object missing brandIds', () => {
|
|
73
|
+
const invalidUser = {
|
|
74
|
+
username: 'testuser',
|
|
75
|
+
email: 'test@example.com',
|
|
76
|
+
accountIds: [1],
|
|
77
|
+
isAdmin: false,
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
expect(isAuthenticatedUser(invalidUser)).toBe(false);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('should return false for object missing accountIds', () => {
|
|
84
|
+
const invalidUser = {
|
|
85
|
+
username: 'testuser',
|
|
86
|
+
email: 'test@example.com',
|
|
87
|
+
brandIds: [1],
|
|
88
|
+
isAdmin: false,
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
expect(isAuthenticatedUser(invalidUser)).toBe(false);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should return false for object missing isAdmin', () => {
|
|
95
|
+
const invalidUser = {
|
|
96
|
+
username: 'testuser',
|
|
97
|
+
email: 'test@example.com',
|
|
98
|
+
brandIds: [1],
|
|
99
|
+
accountIds: [1],
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
expect(isAuthenticatedUser(invalidUser)).toBe(false);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('should return false when username is not a string', () => {
|
|
106
|
+
const invalidUser = {
|
|
107
|
+
username: 123,
|
|
108
|
+
email: 'test@example.com',
|
|
109
|
+
brandIds: [1],
|
|
110
|
+
accountIds: [1],
|
|
111
|
+
isAdmin: false,
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
expect(isAuthenticatedUser(invalidUser)).toBe(false);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('should return false when email is not a string', () => {
|
|
118
|
+
const invalidUser = {
|
|
119
|
+
username: 'testuser',
|
|
120
|
+
email: 123,
|
|
121
|
+
brandIds: [1],
|
|
122
|
+
accountIds: [1],
|
|
123
|
+
isAdmin: false,
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
expect(isAuthenticatedUser(invalidUser)).toBe(false);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('should return false when brandIds is not an array', () => {
|
|
130
|
+
const invalidUser = {
|
|
131
|
+
username: 'testuser',
|
|
132
|
+
email: 'test@example.com',
|
|
133
|
+
brandIds: 'not-an-array',
|
|
134
|
+
accountIds: [1],
|
|
135
|
+
isAdmin: false,
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
expect(isAuthenticatedUser(invalidUser)).toBe(false);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('should return false when accountIds is not an array', () => {
|
|
142
|
+
const invalidUser = {
|
|
143
|
+
username: 'testuser',
|
|
144
|
+
email: 'test@example.com',
|
|
145
|
+
brandIds: [1],
|
|
146
|
+
accountIds: 'not-an-array',
|
|
147
|
+
isAdmin: false,
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
expect(isAuthenticatedUser(invalidUser)).toBe(false);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('should return false when isAdmin is not a boolean', () => {
|
|
154
|
+
const invalidUser = {
|
|
155
|
+
username: 'testuser',
|
|
156
|
+
email: 'test@example.com',
|
|
157
|
+
brandIds: [1],
|
|
158
|
+
accountIds: [1],
|
|
159
|
+
isAdmin: 'not-a-boolean',
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
expect(isAuthenticatedUser(invalidUser)).toBe(false);
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
});
|