een-api-toolkit 0.0.18 → 0.1.4

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 (55) hide show
  1. package/CHANGELOG.md +8 -5
  2. package/README.md +29 -41
  3. package/dist/index.cjs +1 -1
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.d.ts +339 -821
  6. package/dist/index.js +295 -340
  7. package/dist/index.js.map +1 -1
  8. package/docs/AI-CONTEXT.md +151 -237
  9. package/examples/vue-bridges/.env.example +13 -0
  10. package/examples/vue-bridges/e2e/app.spec.ts +73 -0
  11. package/examples/vue-bridges/e2e/auth.spec.ts +206 -0
  12. package/examples/{vue-basic → vue-bridges}/index.html +1 -1
  13. package/examples/vue-bridges/package-lock.json +1583 -0
  14. package/examples/vue-bridges/package.json +28 -0
  15. package/examples/vue-bridges/playwright.config.ts +46 -0
  16. package/examples/vue-bridges/src/App.vue +108 -0
  17. package/examples/vue-bridges/src/router/index.ts +68 -0
  18. package/examples/vue-bridges/src/views/BridgeDetail.vue +279 -0
  19. package/examples/vue-bridges/src/views/Bridges.vue +297 -0
  20. package/examples/vue-bridges/src/views/Callback.vue +76 -0
  21. package/examples/vue-bridges/src/views/Home.vue +150 -0
  22. package/examples/vue-bridges/src/views/Login.vue +33 -0
  23. package/examples/vue-bridges/src/views/Logout.vue +66 -0
  24. package/examples/vue-bridges/src/vite-env.d.ts +12 -0
  25. package/examples/vue-cameras/e2e/app.spec.ts +2 -2
  26. package/examples/vue-cameras/e2e/auth.spec.ts +206 -0
  27. package/examples/vue-cameras/src/App.vue +4 -4
  28. package/examples/vue-cameras/src/views/CameraDetail.vue +57 -9
  29. package/examples/vue-cameras/src/views/Cameras.vue +69 -18
  30. package/examples/vue-cameras/src/views/Home.vue +36 -11
  31. package/examples/{vue-basic → vue-users}/README.md +4 -4
  32. package/examples/{vue-basic → vue-users}/e2e/app.spec.ts +3 -3
  33. package/examples/{vue-basic → vue-users}/e2e/auth.spec.ts +2 -2
  34. package/examples/vue-users/index.html +13 -0
  35. package/examples/{vue-basic → vue-users}/package-lock.json +3 -3
  36. package/examples/{vue-basic → vue-users}/package.json +1 -1
  37. package/examples/{vue-basic → vue-users}/src/App.vue +1 -1
  38. package/examples/vue-users/src/main.ts +23 -0
  39. package/examples/{vue-basic → vue-users}/src/views/Home.vue +27 -12
  40. package/examples/{vue-basic → vue-users}/src/views/Users.vue +51 -10
  41. package/examples/vue-users/tsconfig.json +21 -0
  42. package/examples/vue-users/tsconfig.node.json +10 -0
  43. package/examples/vue-users/vite.config.ts +12 -0
  44. package/package.json +1 -1
  45. /package/examples/{vue-basic → vue-bridges}/src/main.ts +0 -0
  46. /package/examples/{vue-basic → vue-bridges}/tsconfig.json +0 -0
  47. /package/examples/{vue-basic → vue-bridges}/tsconfig.node.json +0 -0
  48. /package/examples/{vue-basic → vue-bridges}/vite.config.ts +0 -0
  49. /package/examples/{vue-basic → vue-users}/.env.example +0 -0
  50. /package/examples/{vue-basic → vue-users}/playwright.config.ts +0 -0
  51. /package/examples/{vue-basic → vue-users}/src/router/index.ts +0 -0
  52. /package/examples/{vue-basic → vue-users}/src/views/Callback.vue +0 -0
  53. /package/examples/{vue-basic → vue-users}/src/views/Login.vue +0 -0
  54. /package/examples/{vue-basic → vue-users}/src/views/Logout.vue +0 -0
  55. /package/examples/{vue-basic → vue-users}/src/vite-env.d.ts +0 -0
@@ -0,0 +1,206 @@
1
+ import { test, expect, Page } from '@playwright/test'
2
+
3
+ /**
4
+ * E2E tests for the Vue Bridges Example - OAuth Login Flow
5
+ *
6
+ * Tests the OAuth login flow through the UI:
7
+ * 1. Click login button in the example app
8
+ * 2. Enter credentials on EEN OAuth page
9
+ * 3. Complete the OAuth callback
10
+ * 4. Verify authenticated state and landing URL
11
+ *
12
+ * Required environment variables:
13
+ * - VITE_PROXY_URL: OAuth proxy URL (e.g., http://127.0.0.1:8787)
14
+ * - VITE_EEN_CLIENT_ID: EEN OAuth client ID
15
+ * - TEST_USER: Test user email
16
+ * - TEST_PASSWORD: Test user password
17
+ */
18
+
19
+ const TIMEOUTS = {
20
+ OAUTH_REDIRECT: 30000,
21
+ ELEMENT_VISIBLE: 15000,
22
+ PASSWORD_VISIBLE: 10000,
23
+ AUTH_COMPLETE: 30000,
24
+ UI_UPDATE: 10000,
25
+ PROXY_CHECK: 5000
26
+ } as const
27
+
28
+ const TEST_USER = process.env.TEST_USER
29
+ const TEST_PASSWORD = process.env.TEST_PASSWORD
30
+ const PROXY_URL = process.env.VITE_PROXY_URL
31
+
32
+ async function isProxyAccessible(): Promise<boolean> {
33
+ if (!PROXY_URL) return false
34
+ const controller = new AbortController()
35
+ const timeoutId = setTimeout(() => controller.abort(), TIMEOUTS.PROXY_CHECK)
36
+
37
+ try {
38
+ const response = await fetch(PROXY_URL, {
39
+ method: 'HEAD',
40
+ signal: controller.signal
41
+ })
42
+ return response.ok || response.status === 404
43
+ } catch {
44
+ return false
45
+ } finally {
46
+ clearTimeout(timeoutId)
47
+ }
48
+ }
49
+
50
+ async function performLogin(page: Page, username: string, password: string): Promise<void> {
51
+ await page.goto('/')
52
+
53
+ // Click login button on home page to go to login page
54
+ await page.click('[data-testid="login-button"]')
55
+ await page.waitForURL('/login')
56
+
57
+ // Click the OAuth login button on the login page
58
+ await Promise.all([
59
+ page.waitForURL(/.*eagleeyenetworks.com.*/, { timeout: TIMEOUTS.OAUTH_REDIRECT }),
60
+ page.click('button:has-text("Login with Eagle Eye Networks")')
61
+ ])
62
+
63
+ const emailInput = page.locator('#authentication--input__email')
64
+ await emailInput.waitFor({ state: 'visible', timeout: TIMEOUTS.ELEMENT_VISIBLE })
65
+ await emailInput.fill(username)
66
+
67
+ await page.getByRole('button', { name: 'Next' }).click()
68
+
69
+ const passwordInput = page.locator('#authentication--input__password')
70
+ await passwordInput.waitFor({ state: 'visible', timeout: TIMEOUTS.PASSWORD_VISIBLE })
71
+ await passwordInput.fill(password)
72
+
73
+ await page.locator('#next, button:has-text("Sign in")').first().click()
74
+
75
+ // After login, the bridges app redirects to /bridges
76
+ await page.waitForURL('**/bridges', { timeout: TIMEOUTS.AUTH_COMPLETE })
77
+ }
78
+
79
+ async function clearAuthState(page: Page): Promise<void> {
80
+ try {
81
+ const url = page.url()
82
+ if (url && url.startsWith('http')) {
83
+ await page.evaluate(() => {
84
+ try {
85
+ localStorage.clear()
86
+ sessionStorage.clear()
87
+ } catch {
88
+ // Ignore
89
+ }
90
+ })
91
+ }
92
+ } catch {
93
+ // Ignore
94
+ }
95
+ }
96
+
97
+ test.describe('Vue Bridges Example - Auth', () => {
98
+ let proxyAccessible = false
99
+
100
+ function skipIfNoProxy() {
101
+ test.skip(!proxyAccessible, 'OAuth proxy not accessible')
102
+ }
103
+
104
+ function skipIfNoCredentials() {
105
+ test.skip(!TEST_USER || !TEST_PASSWORD, 'Test credentials not available')
106
+ }
107
+
108
+ test.beforeAll(async () => {
109
+ proxyAccessible = await isProxyAccessible()
110
+ if (!proxyAccessible) {
111
+ console.log('OAuth proxy not accessible - OAuth tests will be skipped')
112
+ }
113
+ })
114
+
115
+ test.afterEach(async ({ page }) => {
116
+ await clearAuthState(page)
117
+ })
118
+
119
+ test('shows login button when not authenticated', async ({ page }) => {
120
+ await page.goto('/')
121
+ await expect(page.locator('[data-testid="not-authenticated"]')).toBeVisible()
122
+ await expect(page.locator('[data-testid="nav-login"]')).toBeVisible()
123
+ await expect(page.locator('[data-testid="nav-logout"]')).not.toBeVisible()
124
+ await expect(page.locator('[data-testid="nav-bridges"]')).not.toBeVisible()
125
+ })
126
+
127
+ test('bridges page redirects to login when not authenticated', async ({ page }) => {
128
+ await page.goto('/bridges')
129
+ // Should redirect to login page
130
+ await expect(page.locator('h2')).toContainText('Login')
131
+ })
132
+
133
+ test('login button redirects to OAuth page', async ({ page }) => {
134
+ skipIfNoProxy()
135
+ skipIfNoCredentials()
136
+
137
+ await page.goto('/')
138
+ await expect(page.locator('[data-testid="login-button"]')).toBeVisible()
139
+
140
+ // Click login button to go to login page
141
+ await page.click('[data-testid="login-button"]')
142
+ await page.waitForURL('/login')
143
+
144
+ // Click the OAuth login button
145
+ await Promise.all([
146
+ page.waitForURL(/.*eagleeyenetworks.com.*/, { timeout: TIMEOUTS.OAUTH_REDIRECT }),
147
+ page.click('button:has-text("Login with Eagle Eye Networks")')
148
+ ])
149
+
150
+ const emailInput = page.locator('#authentication--input__email')
151
+ await expect(emailInput).toBeVisible({ timeout: TIMEOUTS.ELEMENT_VISIBLE })
152
+ })
153
+
154
+ test('complete OAuth login flow and verify landing URL', async ({ page }) => {
155
+ skipIfNoProxy()
156
+ skipIfNoCredentials()
157
+
158
+ // Verify initially not authenticated
159
+ await page.goto('/')
160
+ await expect(page.locator('[data-testid="not-authenticated"]')).toBeVisible()
161
+
162
+ // Perform login
163
+ await performLogin(page, TEST_USER!, TEST_PASSWORD!)
164
+
165
+ // Verify landing URL is the bridges page (vue-bridges app redirects to /bridges after login)
166
+ await expect(page).toHaveURL('http://127.0.0.1:3333/bridges')
167
+
168
+ // Verify authenticated state - we're on bridges page so check nav elements
169
+ await expect(page.locator('[data-testid="nav-bridges"]')).toBeVisible({ timeout: TIMEOUTS.UI_UPDATE })
170
+ await expect(page.locator('[data-testid="nav-logout"]')).toBeVisible()
171
+ await expect(page.locator('[data-testid="nav-login"]')).not.toBeVisible()
172
+ })
173
+
174
+ test('can view bridges list after login', async ({ page }) => {
175
+ skipIfNoProxy()
176
+ skipIfNoCredentials()
177
+
178
+ await performLogin(page, TEST_USER!, TEST_PASSWORD!)
179
+ await expect(page.locator('[data-testid="nav-bridges"]')).toBeVisible({ timeout: TIMEOUTS.UI_UPDATE })
180
+
181
+ // Navigate to bridges page
182
+ await page.click('[data-testid="nav-bridges"]')
183
+ await page.waitForURL('/bridges')
184
+
185
+ // Verify landing URL is bridges page
186
+ await expect(page).toHaveURL('http://127.0.0.1:3333/bridges')
187
+
188
+ // Should see bridges grid (not error state)
189
+ await expect(page.locator('.bridge-grid, .no-bridges')).toBeVisible({ timeout: TIMEOUTS.ELEMENT_VISIBLE })
190
+ await expect(page.locator('.error')).not.toBeVisible()
191
+ })
192
+
193
+ test('can logout after login', async ({ page }) => {
194
+ skipIfNoProxy()
195
+ skipIfNoCredentials()
196
+
197
+ await performLogin(page, TEST_USER!, TEST_PASSWORD!)
198
+ await expect(page.locator('[data-testid="nav-logout"]')).toBeVisible({ timeout: TIMEOUTS.UI_UPDATE })
199
+
200
+ await page.click('[data-testid="nav-logout"]')
201
+
202
+ await page.waitForURL('**/')
203
+ await expect(page.locator('[data-testid="not-authenticated"]')).toBeVisible({ timeout: TIMEOUTS.UI_UPDATE })
204
+ await expect(page.locator('[data-testid="nav-login"]')).toBeVisible()
205
+ })
206
+ })
@@ -4,7 +4,7 @@
4
4
  <meta charset="UTF-8" />
5
5
  <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
- <title>EEN API Toolkit Example</title>
7
+ <title>EEN Bridges Example</title>
8
8
  </head>
9
9
  <body>
10
10
  <div id="app"></div>