een-api-toolkit 0.3.16 → 0.3.22

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.
Files changed (53) hide show
  1. package/.claude/agents/docs-accuracy-reviewer.md +146 -0
  2. package/.claude/agents/een-auth-agent.md +168 -0
  3. package/.claude/agents/een-devices-agent.md +294 -0
  4. package/.claude/agents/een-events-agent.md +375 -0
  5. package/.claude/agents/een-media-agent.md +256 -0
  6. package/.claude/agents/een-setup-agent.md +126 -0
  7. package/.claude/agents/een-users-agent.md +239 -0
  8. package/.claude/agents/test-runner.md +144 -0
  9. package/CHANGELOG.md +151 -10
  10. package/README.md +1 -0
  11. package/dist/index.cjs +3 -1
  12. package/dist/index.cjs.map +1 -1
  13. package/dist/index.d.ts +561 -0
  14. package/dist/index.js +483 -260
  15. package/dist/index.js.map +1 -1
  16. package/docs/AI-CONTEXT.md +128 -1648
  17. package/docs/ai-reference/AI-AUTH.md +288 -0
  18. package/docs/ai-reference/AI-DEVICES.md +569 -0
  19. package/docs/ai-reference/AI-EVENTS.md +1745 -0
  20. package/docs/ai-reference/AI-MEDIA.md +974 -0
  21. package/docs/ai-reference/AI-SETUP.md +267 -0
  22. package/docs/ai-reference/AI-USERS.md +255 -0
  23. package/examples/vue-event-subscriptions/.env.example +15 -0
  24. package/examples/vue-event-subscriptions/README.md +103 -0
  25. package/examples/vue-event-subscriptions/e2e/app.spec.ts +71 -0
  26. package/examples/vue-event-subscriptions/e2e/auth.spec.ts +290 -0
  27. package/examples/vue-event-subscriptions/index.html +13 -0
  28. package/examples/vue-event-subscriptions/package-lock.json +1726 -0
  29. package/examples/vue-event-subscriptions/package.json +29 -0
  30. package/examples/vue-event-subscriptions/playwright.config.ts +47 -0
  31. package/examples/vue-event-subscriptions/src/App.vue +193 -0
  32. package/examples/vue-event-subscriptions/src/composables/useHlsPlayer.ts +272 -0
  33. package/examples/vue-event-subscriptions/src/main.ts +25 -0
  34. package/examples/vue-event-subscriptions/src/router/index.ts +68 -0
  35. package/examples/vue-event-subscriptions/src/stores/connection.ts +101 -0
  36. package/examples/vue-event-subscriptions/src/stores/mediaSession.ts +79 -0
  37. package/examples/vue-event-subscriptions/src/views/Callback.vue +76 -0
  38. package/examples/vue-event-subscriptions/src/views/Home.vue +192 -0
  39. package/examples/vue-event-subscriptions/src/views/LiveEvents.vue +901 -0
  40. package/examples/vue-event-subscriptions/src/views/Login.vue +33 -0
  41. package/examples/vue-event-subscriptions/src/views/Logout.vue +65 -0
  42. package/examples/vue-event-subscriptions/src/views/Subscriptions.vue +389 -0
  43. package/examples/vue-event-subscriptions/src/vite-env.d.ts +12 -0
  44. package/examples/vue-event-subscriptions/tsconfig.json +21 -0
  45. package/examples/vue-event-subscriptions/tsconfig.node.json +10 -0
  46. package/examples/vue-event-subscriptions/vite.config.ts +12 -0
  47. package/examples/vue-events/package-lock.json +8 -1
  48. package/examples/vue-events/package.json +1 -0
  49. package/examples/vue-events/src/components/EventsModal.vue +269 -47
  50. package/examples/vue-events/src/composables/useHlsPlayer.ts +272 -0
  51. package/examples/vue-events/src/stores/mediaSession.ts +79 -0
  52. package/package.json +10 -2
  53. package/scripts/setup-agents.ts +116 -0
@@ -0,0 +1,267 @@
1
+ # Vue 3 Application Setup - EEN API Toolkit
2
+
3
+ > **Version:** 0.3.22
4
+ >
5
+ > Complete guide for setting up a Vue 3 application with the een-api-toolkit.
6
+ > Load this document when creating a new project or troubleshooting setup issues.
7
+
8
+ ---
9
+
10
+ ## Prerequisites
11
+
12
+ | Requirement | Details |
13
+ |-------------|---------|
14
+ | **Node.js** | Version 20 LTS or later |
15
+ | **Vue 3.x** | The toolkit is built for Vue 3 Composition API |
16
+ | **Pinia** | Required peer dependency for state management |
17
+ | **Vite** | Recommended build tool |
18
+ | **OAuth Proxy** | Required for secure token management |
19
+
20
+ ---
21
+
22
+ ## Quick Start
23
+
24
+ ### 1. Create Vue 3 Project
25
+
26
+ ```bash
27
+ npm create vue@latest my-een-app
28
+ cd my-een-app
29
+ ```
30
+
31
+ ### 2. Install Dependencies
32
+
33
+ ```bash
34
+ npm install een-api-toolkit pinia
35
+ ```
36
+
37
+ ### 3. Configure main.ts
38
+
39
+ > **⚠️ CRITICAL:** Pinia MUST be installed on the Vue app BEFORE calling
40
+ > `initEenToolkit()` or using `useAuthStore()`.
41
+
42
+ ```typescript
43
+ import { createApp } from 'vue'
44
+ import { createPinia } from 'pinia'
45
+ import { initEenToolkit } from 'een-api-toolkit'
46
+ import App from './App.vue'
47
+ import router from './router'
48
+
49
+ const app = createApp(App)
50
+
51
+ // Install Pinia (required before initEenToolkit)
52
+ app.use(createPinia())
53
+
54
+ // Initialize EEN API Toolkit with memory storage for maximum security
55
+ // Note: Using 'memory' storage means tokens are lost on page refresh
56
+ initEenToolkit({
57
+ proxyUrl: import.meta.env.VITE_PROXY_URL,
58
+ clientId: import.meta.env.VITE_EEN_CLIENT_ID,
59
+ redirectUri: import.meta.env.VITE_REDIRECT_URI,
60
+ storageStrategy: 'memory',
61
+ debug: import.meta.env.VITE_DEBUG === 'true'
62
+ })
63
+
64
+ // Install router
65
+ app.use(router)
66
+
67
+ app.mount('#app')
68
+
69
+ ```
70
+
71
+ ### 4. Configure vite.config.ts
72
+
73
+ > **⚠️ IMPORTANT:** Must use `127.0.0.1:3333` for EEN OAuth callback.
74
+
75
+ ```typescript
76
+ import { defineConfig } from 'vite'
77
+ import vue from '@vitejs/plugin-vue'
78
+
79
+ export default defineConfig({
80
+ plugins: [vue()],
81
+ server: {
82
+ // IMPORTANT: Must use 127.0.0.1:3333 for EEN OAuth callback
83
+ // The EEN Identity Provider only permits this specific redirect URI
84
+ host: '127.0.0.1',
85
+ port: 3333
86
+ }
87
+ })
88
+
89
+ ```
90
+
91
+ ### 5. Configure Environment Variables
92
+
93
+ Create `.env` file:
94
+
95
+ ```env
96
+ VITE_PROXY_URL=http://localhost:8787
97
+ VITE_EEN_CLIENT_ID=your-een-client-id
98
+ VITE_REDIRECT_URI=http://127.0.0.1:3333
99
+ VITE_DEBUG=true
100
+ ```
101
+
102
+ ---
103
+
104
+ ## Router Setup
105
+
106
+ The router must handle OAuth callbacks on the root path and protect authenticated routes.
107
+
108
+ ```typescript
109
+ import { createRouter, createWebHistory } from 'vue-router'
110
+ import { useAuthStore } from 'een-api-toolkit'
111
+ import Home from '../views/Home.vue'
112
+ import Login from '../views/Login.vue'
113
+ import Callback from '../views/Callback.vue'
114
+ import Users from '../views/Users.vue'
115
+ import Logout from '../views/Logout.vue'
116
+
117
+ const router = createRouter({
118
+ history: createWebHistory(),
119
+ routes: [
120
+ {
121
+ path: '/',
122
+ name: 'home',
123
+ component: Home,
124
+ // Handle OAuth callback on root path (EEN IDP redirects to http://127.0.0.1:3333)
125
+ beforeEnter: (to, _from, next) => {
126
+ // If URL has code and state params, it's an OAuth callback
127
+ if (to.query.code && to.query.state) {
128
+ next({ name: 'callback', query: to.query })
129
+ } else {
130
+ next()
131
+ }
132
+ }
133
+ },
134
+ {
135
+ path: '/login',
136
+ name: 'login',
137
+ component: Login
138
+ },
139
+ {
140
+ path: '/callback',
141
+ name: 'callback',
142
+ component: Callback
143
+ },
144
+ {
145
+ path: '/users',
146
+ name: 'users',
147
+ component: Users,
148
+ meta: { requiresAuth: true }
149
+ },
150
+ {
151
+ path: '/logout',
152
+ name: 'logout',
153
+ component: Logout
154
+ }
155
+ ]
156
+ })
157
+
158
+ // Navigation guard for protected routes
159
+ router.beforeEach((to, _from, next) => {
160
+ const authStore = useAuthStore()
161
+
162
+ if (to.meta.requiresAuth && !authStore.isAuthenticated) {
163
+ next({ name: 'login' })
164
+ } else {
165
+ next()
166
+ }
167
+ })
168
+
169
+ export default router
170
+
171
+ ```
172
+
173
+ ---
174
+
175
+ ## Project Structure
176
+
177
+ ```
178
+ my-een-app/
179
+ ├── src/
180
+ │ ├── main.ts # Pinia + toolkit initialization
181
+ │ ├── App.vue # Root component
182
+ │ ├── router/
183
+ │ │ └── index.ts # Routes with auth guards
184
+ │ └── views/
185
+ │ ├── Home.vue # Landing page
186
+ │ ├── Login.vue # OAuth login trigger
187
+ │ ├── Logout.vue # Token revocation
188
+ │ └── Callback.vue # OAuth callback handler
189
+ ├── .env # Environment variables
190
+ └── vite.config.ts # Vite configuration
191
+ ```
192
+
193
+ ---
194
+
195
+ ## Common Setup Errors
196
+
197
+ ### "getActivePinia() was called but there was no active Pinia"
198
+
199
+ **Cause:** Pinia was not installed before `initEenToolkit()` was called.
200
+
201
+ **Solution:** Ensure `app.use(pinia)` is called BEFORE `initEenToolkit()`:
202
+
203
+ ```typescript
204
+ const app = createApp(App)
205
+ app.use(createPinia()) // ✅ First
206
+ initEenToolkit({...}) // ✅ Second
207
+ app.mount('#app') // ✅ Last
208
+ ```
209
+
210
+ ### "Redirect URI mismatch"
211
+
212
+ **Cause:** OAuth redirect URI doesn't exactly match `http://127.0.0.1:3333`.
213
+
214
+ **Solution:**
215
+ - Use `127.0.0.1` not `localhost`
216
+ - Use port `3333` exactly
217
+ - No trailing slash
218
+ - No path (not `/callback`)
219
+ - Register at [EEN Developer Portal](https://developer.eagleeyenetworks.com/page/my-application)
220
+
221
+ ### Port 3333 already in use
222
+
223
+ **Solution:** Kill existing process:
224
+
225
+ ```bash
226
+ kill $(lsof -ti :3333) 2>/dev/null || true
227
+ npm run dev
228
+ ```
229
+
230
+ ---
231
+
232
+ ## Configuration Options
233
+
234
+ ```typescript
235
+ interface EenToolkitConfig {
236
+ proxyUrl?: string // OAuth proxy URL (required)
237
+ clientId?: string // EEN OAuth client ID (required)
238
+ redirectUri?: string // OAuth redirect URI (default: http://127.0.0.1:3333)
239
+ storageStrategy?: StorageStrategy // Token storage strategy
240
+ debug?: boolean // Enable debug logging
241
+ }
242
+
243
+ type StorageStrategy = 'localStorage' | 'sessionStorage' | 'memory'
244
+ ```
245
+
246
+ ### Storage Strategies
247
+
248
+ | Strategy | Persistence | Use Case |
249
+ |----------|-------------|----------|
250
+ | `localStorage` | Survives browser restart | Default, good for most apps |
251
+ | `sessionStorage` | Per-tab, cleared on close | Multi-account scenarios |
252
+ | `memory` | Lost on page refresh | Maximum security |
253
+
254
+ ---
255
+
256
+ ## Next Steps
257
+
258
+ After setup, proceed to:
259
+ - [AI-AUTH.md](./AI-AUTH.md) - Implement login/logout flows
260
+ - [AI-USERS.md](./AI-USERS.md) - Work with user data
261
+ - [AI-DEVICES.md](./AI-DEVICES.md) - Work with cameras and bridges
262
+
263
+ ---
264
+
265
+ ## Reference Example
266
+
267
+ See `examples/vue-users/` for a complete working example.
@@ -0,0 +1,255 @@
1
+ # Users API - EEN API Toolkit
2
+
3
+ > **Version:** 0.3.22
4
+ >
5
+ > Complete reference for user management.
6
+ > Load this document when working with user data.
7
+
8
+ ---
9
+
10
+ ## Types
11
+
12
+ ### User
13
+
14
+ ```typescript
15
+ interface User {
16
+ id: string
17
+ email: string
18
+ firstName: string
19
+ lastName: string
20
+ accountId?: string
21
+ timeZone?: string // IANA timezone (e.g., "America/Los_Angeles")
22
+ language?: string // ISO 639-1 code (e.g., "en")
23
+ phone?: string
24
+ mobilePhone?: string
25
+ permissions?: string[] // Requires include: ['permissions']
26
+ lastLogin?: string // ISO 8601 timestamp
27
+ isActive?: boolean
28
+ createdAt?: string
29
+ updatedAt?: string
30
+ }
31
+ ```
32
+
33
+ ### UserProfile
34
+
35
+ ```typescript
36
+ interface UserProfile {
37
+ id: string
38
+ email: string
39
+ firstName: string
40
+ lastName: string
41
+ accountId?: string
42
+ timeZone?: string
43
+ language?: string
44
+ }
45
+ ```
46
+
47
+ ### Parameters
48
+
49
+ ```typescript
50
+ interface ListUsersParams {
51
+ pageSize?: number // Results per page (default: 100, max: 1000)
52
+ pageToken?: string // Pagination token
53
+ include?: string[] // Additional fields (e.g., ['permissions'])
54
+ }
55
+
56
+ interface GetUserParams {
57
+ include?: string[] // Additional fields to include
58
+ }
59
+ ```
60
+
61
+ ---
62
+
63
+ ## Functions
64
+
65
+ ### getCurrentUser()
66
+
67
+ Get the current authenticated user's profile.
68
+
69
+ ```typescript
70
+ import { getCurrentUser } from 'een-api-toolkit'
71
+
72
+ const { data, error } = await getCurrentUser()
73
+
74
+ if (error) {
75
+ if (error.code === 'AUTH_REQUIRED') {
76
+ router.push('/login')
77
+ }
78
+ return
79
+ }
80
+
81
+ console.log(`Welcome, ${data.firstName} ${data.lastName}`)
82
+ ```
83
+
84
+ ### getUsers(params?)
85
+
86
+ List users with optional pagination.
87
+
88
+ ```typescript
89
+ import { getUsers } from 'een-api-toolkit'
90
+
91
+ // Basic usage
92
+ const { data, error } = await getUsers()
93
+
94
+ // With pagination
95
+ const { data } = await getUsers({ pageSize: 50 })
96
+
97
+ // With include
98
+ const { data } = await getUsers({ include: ['permissions'] })
99
+ ```
100
+
101
+ ### getUser(userId, params?)
102
+
103
+ Get a specific user by ID.
104
+
105
+ ```typescript
106
+ import { getUser } from 'een-api-toolkit'
107
+
108
+ const { data, error } = await getUser('user-id-123')
109
+
110
+ if (error?.code === 'NOT_FOUND') {
111
+ console.log('User not found')
112
+ return
113
+ }
114
+
115
+ // With permissions
116
+ const { data: userWithPerms } = await getUser('user-id-123', {
117
+ include: ['permissions']
118
+ })
119
+ ```
120
+
121
+ ---
122
+
123
+ ## Pagination Example
124
+
125
+ ```typescript
126
+ async function fetchAllUsers(): Promise<User[]> {
127
+ const allUsers: User[] = []
128
+ let pageToken: string | undefined
129
+
130
+ do {
131
+ const { data, error } = await getUsers({ pageSize: 100, pageToken })
132
+ if (error) break
133
+ allUsers.push(...data.results)
134
+ pageToken = data.nextPageToken
135
+ } while (pageToken)
136
+
137
+ return allUsers
138
+ }
139
+ ```
140
+
141
+ ---
142
+
143
+ ## Complete Vue Component
144
+
145
+ ```vue
146
+ <script setup lang="ts">
147
+ import { ref, computed, onMounted } from 'vue'
148
+ import { getUsers, type User, type EenError, type ListUsersParams } from 'een-api-toolkit'
149
+
150
+ // Reactive state
151
+ const users = ref<User[]>([])
152
+ const loading = ref(false)
153
+ const error = ref<EenError | null>(null)
154
+ const nextPageToken = ref<string | undefined>(undefined)
155
+
156
+ const hasNextPage = computed(() => !!nextPageToken.value)
157
+
158
+ const params = ref<ListUsersParams>({ pageSize: 10 })
159
+
160
+ async function fetchUsers(fetchParams?: ListUsersParams, append = false) {
161
+ loading.value = true
162
+ error.value = null
163
+
164
+ const mergedParams = { ...params.value, ...fetchParams }
165
+ const result = await getUsers(mergedParams)
166
+
167
+ if (result.error) {
168
+ error.value = result.error
169
+ if (!append) {
170
+ users.value = []
171
+ }
172
+ nextPageToken.value = undefined
173
+ } else {
174
+ if (append) {
175
+ users.value = [...users.value, ...result.data.results]
176
+ } else {
177
+ users.value = result.data.results
178
+ }
179
+ nextPageToken.value = result.data.nextPageToken
180
+ }
181
+
182
+ loading.value = false
183
+ return result
184
+ }
185
+
186
+ function refresh() {
187
+ return fetchUsers()
188
+ }
189
+
190
+ async function fetchNextPage() {
191
+ if (!nextPageToken.value) return
192
+ return fetchUsers({ ...params.value, pageToken: nextPageToken.value }, true)
193
+ }
194
+
195
+ onMounted(() => {
196
+ fetchUsers()
197
+ })
198
+ </script>
199
+
200
+ <template>
201
+ <div class="users">
202
+ <div class="header">
203
+ <h2>Users</h2>
204
+ <button @click="refresh" :disabled="loading">
205
+ {{ loading ? 'Loading...' : 'Refresh' }}
206
+ </button>
207
+ </div>
208
+
209
+ <div v-if="loading && users.length === 0" class="loading">
210
+ Loading users...
211
+ </div>
212
+
213
+ <div v-else-if="error" class="error">
214
+ Error: {{ error.message }}
215
+ </div>
216
+
217
+ <div v-else>
218
+ <table v-if="users.length > 0">
219
+ <thead>
220
+ <tr>
221
+ <th>Name</th>
222
+ <th>Email</th>
223
+ <th>Status</th>
224
+ </tr>
225
+ </thead>
226
+ <tbody>
227
+ <tr v-for="user in users" :key="user.id">
228
+ <td>{{ user.firstName }} {{ user.lastName }}</td>
229
+ <td>{{ user.email }}</td>
230
+ <td>
231
+ <span :class="user.isActive ? 'active' : 'inactive'">
232
+ {{ user.isActive ? 'Active' : 'Inactive' }}
233
+ </span>
234
+ </td>
235
+ </tr>
236
+ </tbody>
237
+ </table>
238
+
239
+ <p v-else>No users found.</p>
240
+
241
+ <div v-if="hasNextPage" class="pagination">
242
+ <button @click="fetchNextPage" :disabled="loading">
243
+ {{ loading ? 'Loading...' : 'Load More' }}
244
+ </button>
245
+ </div>
246
+ </div>
247
+ </div>
248
+ </template>
249
+ ```
250
+
251
+ ---
252
+
253
+ ## Reference Example
254
+
255
+ See `examples/vue-users/src/views/Users.vue`
@@ -0,0 +1,15 @@
1
+ # EEN API Toolkit Configuration
2
+ # Copy this file to .env and fill in your values
3
+
4
+ # OAuth Proxy URL (required)
5
+ VITE_PROXY_URL=http://localhost:8787
6
+
7
+ # EEN OAuth Client ID (required)
8
+ VITE_EEN_CLIENT_ID=your_client_id_here
9
+
10
+ # OAuth Redirect URI (required)
11
+ # Must match the redirect URI configured in your EEN OAuth client
12
+ VITE_REDIRECT_URI=http://127.0.0.1:3333
13
+
14
+ # Enable debug logging (optional)
15
+ VITE_DEBUG=true
@@ -0,0 +1,103 @@
1
+ # Event Subscriptions Example
2
+
3
+ This example demonstrates how to use the Event Subscriptions API from the EEN API Toolkit to create SSE subscriptions and receive real-time events from cameras.
4
+
5
+ ## Features
6
+
7
+ - Create and delete event subscriptions with SSE delivery
8
+ - View active subscriptions with pagination
9
+ - Select cameras and event types for filtering
10
+ - Connect to SSE streams for live events
11
+ - Real-time event display with auto-scrolling
12
+
13
+ ## Prerequisites
14
+
15
+ 1. Node.js 20 LTS or later
16
+ 2. EEN OAuth credentials (client ID and secret)
17
+ 3. Running OAuth proxy server (from `../een-oauth-proxy`)
18
+
19
+ ## Setup
20
+
21
+ 1. Copy the environment file:
22
+ ```bash
23
+ cp .env.example .env
24
+ ```
25
+
26
+ 2. Edit `.env` with your configuration:
27
+ - `VITE_PROXY_URL`: URL of your OAuth proxy server
28
+ - `VITE_EEN_CLIENT_ID`: Your EEN OAuth client ID
29
+ - `VITE_REDIRECT_URI`: OAuth redirect URI (default: http://127.0.0.1:3333)
30
+ - `VITE_DEBUG`: Enable debug logging (optional)
31
+
32
+ 3. Install dependencies:
33
+ ```bash
34
+ npm install
35
+ ```
36
+
37
+ 4. Start the OAuth proxy (in a separate terminal):
38
+ ```bash
39
+ cd ../een-oauth-proxy
40
+ npm run dev
41
+ ```
42
+
43
+ 5. Start the example app:
44
+ ```bash
45
+ npm run dev
46
+ ```
47
+
48
+ 6. Open http://127.0.0.1:3333 in your browser
49
+
50
+ ## Usage
51
+
52
+ ### Creating a Subscription
53
+
54
+ 1. Log in with your Eagle Eye Networks account
55
+ 2. Go to "Subscriptions" page
56
+ 3. Select one or more cameras from the dropdown
57
+ 4. Select one or more event types
58
+ 5. Click "Create Subscription"
59
+
60
+ ### Viewing Live Events
61
+
62
+ 1. Go to "Live Events" page
63
+ 2. Select a subscription from the dropdown
64
+ 3. Click "Connect"
65
+ 4. Events will appear in real-time as they occur
66
+ 5. Click "Disconnect" to stop receiving events
67
+
68
+ ## API Functions Used
69
+
70
+ - `listEventSubscriptions()` - List all subscriptions
71
+ - `getEventSubscription(id)` - Get a specific subscription
72
+ - `createEventSubscription(params)` - Create a new subscription
73
+ - `deleteEventSubscription(id)` - Delete a subscription
74
+ - `connectToEventSubscription(sseUrl, options)` - Connect to SSE stream
75
+ - `getCameras()` - List cameras for filter selection
76
+ - `listEventTypes()` - List event types for filter selection
77
+
78
+ ## Event Subscription Lifecycle
79
+
80
+ SSE subscriptions are **temporary** by default:
81
+ - Automatically created with a time-to-live (TTL)
82
+ - Deleted when no client is connected after TTL expires
83
+ - Can be manually deleted at any time
84
+
85
+ ## Troubleshooting
86
+
87
+ ### No events appearing
88
+
89
+ - Ensure the camera has activity (motion, etc.)
90
+ - Check that the subscription has the correct event types
91
+ - Verify the SSE connection status shows "connected"
92
+
93
+ ### Connection errors
94
+
95
+ - Verify the OAuth proxy is running
96
+ - Check that your token is valid (re-login if needed)
97
+ - Ensure the subscription still exists (may have expired)
98
+
99
+ ### Cannot create subscription
100
+
101
+ - Ensure you have cameras in your account
102
+ - Check that event types are loaded
103
+ - Verify your account has permission to create subscriptions
@@ -0,0 +1,71 @@
1
+ import { test, expect } from '@playwright/test'
2
+
3
+ test.describe('Event Subscriptions Example - App', () => {
4
+ test.beforeEach(async ({ page }) => {
5
+ await page.goto('/')
6
+ })
7
+
8
+ test('app loads with correct title', async ({ page }) => {
9
+ await expect(page).toHaveTitle(/Event Subscriptions/)
10
+ })
11
+
12
+ test('header displays app name', async ({ page }) => {
13
+ await expect(page.locator('[data-testid="app-title"]')).toHaveText('EEN Event Subscriptions')
14
+ })
15
+
16
+ test('navigation shows Home and Login links when not authenticated', async ({ page }) => {
17
+ // Home link should be visible
18
+ await expect(page.locator('[data-testid="nav-home"]')).toBeVisible()
19
+
20
+ // Login link should be visible (not authenticated)
21
+ await expect(page.locator('[data-testid="nav-login"]')).toBeVisible()
22
+
23
+ // Subscriptions, Live, and Logout should NOT be visible (requires auth)
24
+ await expect(page.locator('[data-testid="nav-subscriptions"]')).not.toBeVisible()
25
+ await expect(page.locator('[data-testid="nav-live"]')).not.toBeVisible()
26
+ await expect(page.locator('[data-testid="nav-logout"]')).not.toBeVisible()
27
+ })
28
+
29
+ test('home page shows not logged in message', async ({ page }) => {
30
+ await expect(page.locator('[data-testid="not-authenticated"]')).toBeVisible()
31
+ await expect(page.locator('[data-testid="not-authenticated-message"]')).toBeVisible()
32
+ await expect(page.locator('[data-testid="login-button"]')).toBeVisible()
33
+ })
34
+
35
+ test('login page displays login button', async ({ page }) => {
36
+ await page.goto('/login')
37
+
38
+ await expect(page.locator('[data-testid="login-title"]')).toHaveText('Login')
39
+ await expect(page.locator('[data-testid="login-button"]')).toBeVisible()
40
+ })
41
+
42
+ test('protected route (subscriptions) redirects to login', async ({ page }) => {
43
+ await page.goto('/subscriptions')
44
+
45
+ // Should be redirected to login page
46
+ await page.waitForURL('/login')
47
+ await expect(page).toHaveURL('/login')
48
+ await expect(page.locator('[data-testid="login-title"]')).toHaveText('Login')
49
+ })
50
+
51
+ test('protected route (live) redirects to login', async ({ page }) => {
52
+ await page.goto('/live')
53
+
54
+ // Should be redirected to login page
55
+ await page.waitForURL('/login')
56
+ await expect(page).toHaveURL('/login')
57
+ await expect(page.locator('[data-testid="login-title"]')).toHaveText('Login')
58
+ })
59
+
60
+ test('navigation between pages works', async ({ page }) => {
61
+ // Click Login link
62
+ await page.click('[data-testid="nav-login"]')
63
+ await page.waitForURL('/login')
64
+ await expect(page).toHaveURL('/login')
65
+
66
+ // Click Home link
67
+ await page.click('[data-testid="nav-home"]')
68
+ await page.waitForURL('/')
69
+ await expect(page).toHaveURL('/')
70
+ })
71
+ })