start-vibing 4.3.4 → 4.4.0
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/package.json +2 -2
- package/template/.claude/agents/sd-audit.md +32 -0
- package/template/.claude/skills/e2e-audit/DESIGN.md +294 -0
- package/template/.claude/skills/e2e-audit/SKILL.md +660 -0
- package/template/.claude/skills/e2e-audit/e2e/fixtures/auth.setup.ts +70 -0
- package/template/.claude/skills/e2e-audit/e2e/fixtures/auth.ts +21 -0
- package/template/.claude/skills/e2e-audit/e2e/fixtures/base.ts +90 -0
- package/template/.claude/skills/e2e-audit/e2e/fixtures/storage/.gitkeep +0 -0
- package/template/.claude/skills/e2e-audit/e2e/fixtures/storage/admin.json +50 -0
- package/template/.claude/skills/e2e-audit/e2e/fixtures/storage/manager.json +50 -0
- package/template/.claude/skills/e2e-audit/e2e/fixtures/storage/member.json +50 -0
- package/template/.claude/skills/e2e-audit/e2e/fixtures/storage/owner.json +50 -0
- package/template/.claude/skills/e2e-audit/e2e/pages/dashboard-admin.page.ts +141 -0
- package/template/.claude/skills/e2e-audit/e2e/pages/dashboard-billing.page.ts +47 -0
- package/template/.claude/skills/e2e-audit/e2e/pages/dashboard-chat.page.ts +35 -0
- package/template/.claude/skills/e2e-audit/e2e/pages/dashboard-home.page.ts +134 -0
- package/template/.claude/skills/e2e-audit/e2e/pages/dashboard-integrations.page.ts +334 -0
- package/template/.claude/skills/e2e-audit/e2e/pages/dashboard-knowledge.page.ts +30 -0
- package/template/.claude/skills/e2e-audit/e2e/pages/dashboard-ontology.page.ts +71 -0
- package/template/.claude/skills/e2e-audit/e2e/pages/dashboard-profile.page.ts +38 -0
- package/template/.claude/skills/e2e-audit/e2e/pages/dashboard-teams.page.ts +123 -0
- package/template/.claude/skills/e2e-audit/e2e/pages/dashboard-transcripts.page.ts +109 -0
- package/template/.claude/skills/e2e-audit/e2e/specs/auth/login.spec.ts +59 -0
- package/template/.claude/skills/e2e-audit/e2e/specs/dashboard-admin.spec.ts +233 -0
- package/template/.claude/skills/e2e-audit/e2e/specs/dashboard-billing.spec.ts +44 -0
- package/template/.claude/skills/e2e-audit/e2e/specs/dashboard-chat.spec.ts +50 -0
- package/template/.claude/skills/e2e-audit/e2e/specs/dashboard-home.spec.ts +243 -0
- package/template/.claude/skills/e2e-audit/e2e/specs/dashboard-integrations.spec.ts +472 -0
- package/template/.claude/skills/e2e-audit/e2e/specs/dashboard-knowledge.spec.ts +57 -0
- package/template/.claude/skills/e2e-audit/e2e/specs/dashboard-ontology.spec.ts +72 -0
- package/template/.claude/skills/e2e-audit/e2e/specs/dashboard-profile.spec.ts +48 -0
- package/template/.claude/skills/e2e-audit/e2e/specs/dashboard-teams.spec.ts +247 -0
- package/template/.claude/skills/e2e-audit/e2e/specs/dashboard-transcripts.spec.ts +122 -0
- package/template/.claude/skills/e2e-audit/e2e/specs/security/headers.spec.ts +39 -0
- package/template/.claude/skills/e2e-audit/e2e/specs/security/rbac.spec.ts +92 -0
- package/template/.claude/skills/e2e-audit/e2e/specs/security/xss.spec.ts +74 -0
- package/template/.claude/skills/e2e-audit/e2e/utils/console-collector.ts +89 -0
- package/template/.claude/skills/e2e-audit/e2e/utils/security-helpers.ts +114 -0
- package/template/.claude/skills/e2e-audit/e2e/utils/test-data.ts +64 -0
- package/template/.claude/skills/e2e-audit/runbook.md +115 -0
- package/template/.claude/skills/super-design/SKILL.md +42 -4
- package/template/.claude/skills/super-design/scripts/discover-surfaces.sh +197 -0
- package/template/.claude/skills/super-design/scripts/extract-project-rules.sh +240 -0
- package/template/.claude/skills/super-design/scripts/verify-audit.sh +34 -1
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { test, expect } from '../fixtures/base'
|
|
2
|
+
import { DashboardHomePage } from '../pages/dashboard-home.page'
|
|
3
|
+
import { createConsoleCollector } from '../utils/console-collector'
|
|
4
|
+
import { testXssOnInput } from '../utils/security-helpers'
|
|
5
|
+
|
|
6
|
+
test.describe('Dashboard Home @smoke', () => {
|
|
7
|
+
let home: DashboardHomePage
|
|
8
|
+
|
|
9
|
+
test.beforeEach(async ({ authenticatedPage, apiErrors: _apiErrors }) => {
|
|
10
|
+
home = new DashboardHomePage(authenticatedPage)
|
|
11
|
+
await home.goto()
|
|
12
|
+
await home.waitForLoad()
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
test('page loads with correct URL and heading', async ({ authenticatedPage }) => {
|
|
16
|
+
await expect(authenticatedPage, 'Should navigate to home page').toHaveURL(/dashboard\/home/)
|
|
17
|
+
await expect(home.heading, 'Home heading should be visible').toBeVisible()
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
test('displays hero section elements', async () => {
|
|
21
|
+
await expect(home.logo).toBeVisible()
|
|
22
|
+
await expect(home.subtitle).toBeVisible()
|
|
23
|
+
await expect(home.aiAvatar).toBeVisible()
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
test('search input has placeholder and aria-label', async () => {
|
|
27
|
+
await expect(home.searchInput).toBeVisible()
|
|
28
|
+
await expect(home.searchInput).toBeEnabled()
|
|
29
|
+
const placeholder = await home.searchInput.getAttribute('placeholder')
|
|
30
|
+
expect(placeholder).toBeTruthy()
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
test('ask button is disabled when search is empty', async () => {
|
|
34
|
+
await expect(home.askButton).toBeDisabled()
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
test('ask button enables when text is entered', async () => {
|
|
38
|
+
await home.typeSearch('test query')
|
|
39
|
+
await expect(home.askButton).toBeEnabled()
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
test('organization overview section is visible', async () => {
|
|
43
|
+
await expect(home.orgOverviewTitle).toBeVisible()
|
|
44
|
+
})
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
test.describe('Dashboard Home - Ice Breakers @smoke', () => {
|
|
48
|
+
let home: DashboardHomePage
|
|
49
|
+
|
|
50
|
+
test.beforeEach(async ({ authenticatedPage, apiErrors: _apiErrors }) => {
|
|
51
|
+
home = new DashboardHomePage(authenticatedPage)
|
|
52
|
+
await home.goto()
|
|
53
|
+
await home.waitForLoad()
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
test('displays 3 ice breaker suggestion buttons', async () => {
|
|
57
|
+
const suggestions = await home.suggestionButtons.all()
|
|
58
|
+
expect(suggestions.length).toBe(3)
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
test('clicking suggestion populates search input', async () => {
|
|
62
|
+
await home.clickSuggestion(0)
|
|
63
|
+
const value = await home.searchInput.inputValue()
|
|
64
|
+
expect(value.length).toBeGreaterThan(0)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
test('suggestion buttons have aria-labels', async () => {
|
|
68
|
+
const suggestions = await home.suggestionButtons.all()
|
|
69
|
+
for (const btn of suggestions) {
|
|
70
|
+
const label = await btn.getAttribute('aria-label')
|
|
71
|
+
expect(label).toBeTruthy()
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
test.describe('Dashboard Home - Search Submit Flow @critical', () => {
|
|
77
|
+
let home: DashboardHomePage
|
|
78
|
+
|
|
79
|
+
test.beforeEach(async ({ authenticatedPage, apiErrors: _apiErrors }) => {
|
|
80
|
+
home = new DashboardHomePage(authenticatedPage)
|
|
81
|
+
await home.goto()
|
|
82
|
+
await home.waitForLoad()
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
test('typed search creates chat session and navigates to chat page', async ({ authenticatedPage }) => {
|
|
86
|
+
await home.submitSearch('E2E test: typed search submit')
|
|
87
|
+
|
|
88
|
+
// Verify toast appears
|
|
89
|
+
await expect(authenticatedPage.getByText(/chat session created|sessão de chat criada/i)).toBeVisible({ timeout: 10000 })
|
|
90
|
+
|
|
91
|
+
// Verify navigation to chat page with sessionId
|
|
92
|
+
await authenticatedPage.waitForURL(/dashboard\/chat\?sessionId=/, { timeout: 10000 })
|
|
93
|
+
expect(authenticatedPage.url()).toContain('/dashboard/chat?sessionId=')
|
|
94
|
+
|
|
95
|
+
// Verify user message appears in chat
|
|
96
|
+
await expect(authenticatedPage.getByText('E2E test: typed search submit')).toBeVisible({ timeout: 10000 })
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
test('ice breaker click + Enter creates chat session and navigates', async ({ authenticatedPage }) => {
|
|
100
|
+
// Click first ice breaker to populate input
|
|
101
|
+
await home.clickSuggestion(0)
|
|
102
|
+
const query = await home.searchInput.inputValue()
|
|
103
|
+
expect(query.length).toBeGreaterThan(0)
|
|
104
|
+
|
|
105
|
+
// Press Enter to submit
|
|
106
|
+
await home.searchInput.press('Enter')
|
|
107
|
+
|
|
108
|
+
// Verify toast appears
|
|
109
|
+
await expect(authenticatedPage.getByText(/chat session created|sessão de chat criada/i)).toBeVisible({ timeout: 10000 })
|
|
110
|
+
|
|
111
|
+
// Verify navigation to chat page
|
|
112
|
+
await authenticatedPage.waitForURL(/dashboard\/chat\?sessionId=/, { timeout: 10000 })
|
|
113
|
+
|
|
114
|
+
// Verify the ice breaker question appears as user message
|
|
115
|
+
await expect(authenticatedPage.getByText(query)).toBeVisible({ timeout: 10000 })
|
|
116
|
+
})
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
test.describe('Dashboard Home - Profile & Feedback @smoke', () => {
|
|
120
|
+
let home: DashboardHomePage
|
|
121
|
+
|
|
122
|
+
test.beforeEach(async ({ authenticatedPage, apiErrors: _apiErrors }) => {
|
|
123
|
+
home = new DashboardHomePage(authenticatedPage)
|
|
124
|
+
await home.goto()
|
|
125
|
+
await home.waitForLoad()
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
test('profile dropdown shows user info', async () => {
|
|
129
|
+
await home.openProfileDropdown()
|
|
130
|
+
await expect(home.profileMenuName).toBeVisible()
|
|
131
|
+
await expect(home.profileMenuEmail).toBeVisible()
|
|
132
|
+
await expect(home.feedbackMenuItem).toBeVisible()
|
|
133
|
+
await expect(home.logoutButton).toBeVisible()
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
test('feedback dialog opens and has all fields', async () => {
|
|
137
|
+
await home.openFeedbackDialog()
|
|
138
|
+
await expect(home.feedbackDialogTitle).toBeVisible()
|
|
139
|
+
await expect(home.feedbackTypeCombobox).toBeVisible()
|
|
140
|
+
await expect(home.feedbackMessageTextbox).toBeVisible()
|
|
141
|
+
await expect(home.feedbackContactSwitch).toBeVisible()
|
|
142
|
+
await expect(home.feedbackCancelButton).toBeVisible()
|
|
143
|
+
await expect(home.feedbackSubmitButton).toBeVisible()
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
test('feedback submit button is disabled until type and message are filled', async () => {
|
|
147
|
+
await home.openFeedbackDialog()
|
|
148
|
+
|
|
149
|
+
// Initially disabled
|
|
150
|
+
await expect(home.feedbackSubmitButton).toBeDisabled()
|
|
151
|
+
|
|
152
|
+
// Fill type and message
|
|
153
|
+
await home.fillFeedback('Relatório de Bug', 'This is an E2E test feedback message')
|
|
154
|
+
|
|
155
|
+
// Now enabled
|
|
156
|
+
await expect(home.feedbackSubmitButton).toBeEnabled()
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
test('feedback type combobox has 4 options', async ({ authenticatedPage }) => {
|
|
160
|
+
await home.openFeedbackDialog()
|
|
161
|
+
await home.feedbackTypeCombobox.click()
|
|
162
|
+
|
|
163
|
+
const options = await authenticatedPage.getByRole('option').all()
|
|
164
|
+
expect(options.length).toBe(4)
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
test('feedback message shows character counter', async ({ authenticatedPage }) => {
|
|
168
|
+
await home.openFeedbackDialog()
|
|
169
|
+
await home.feedbackMessageTextbox.fill('Test message for counter')
|
|
170
|
+
await expect(authenticatedPage.getByText(/24\/2000/)).toBeVisible()
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
test('feedback cancel button closes dialog', async () => {
|
|
174
|
+
await home.openFeedbackDialog()
|
|
175
|
+
await expect(home.feedbackDialogTitle).toBeVisible()
|
|
176
|
+
await home.feedbackCancelButton.click()
|
|
177
|
+
await expect(home.feedbackDialogTitle).not.toBeVisible()
|
|
178
|
+
})
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
test.describe('Dashboard Home - ROI Stats @smoke', () => {
|
|
182
|
+
let home: DashboardHomePage
|
|
183
|
+
|
|
184
|
+
test.beforeEach(async ({ authenticatedPage, apiErrors: _apiErrors }) => {
|
|
185
|
+
home = new DashboardHomePage(authenticatedPage)
|
|
186
|
+
await home.goto()
|
|
187
|
+
await home.waitForLoad()
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
test('ROI section title is visible', async () => {
|
|
191
|
+
await expect(home.roiSectionTitle).toBeVisible()
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
test('my stats card shows time saved, files, and paragraphs', async () => {
|
|
195
|
+
await expect(home.myStatsTitle).toBeVisible()
|
|
196
|
+
await expect(home.myTimeSaved).toBeVisible()
|
|
197
|
+
await expect(home.myFilesProcessed).toBeVisible()
|
|
198
|
+
await expect(home.myParagraphs).toBeVisible()
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
test('time saved tooltip appears on hover', async ({ authenticatedPage }) => {
|
|
202
|
+
// Hover on the first "Time Saved" stat card
|
|
203
|
+
await home.myTimeSaved.hover()
|
|
204
|
+
// Tooltip should appear
|
|
205
|
+
const tooltip = authenticatedPage.getByRole('tooltip')
|
|
206
|
+
await expect(tooltip).toBeVisible({ timeout: 3000 })
|
|
207
|
+
})
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
test.describe('Dashboard Home - ROI Team Stats (OWNER/ADMIN) @rbac', () => {
|
|
211
|
+
// Default role is 'owner', which should see team stats
|
|
212
|
+
let home: DashboardHomePage
|
|
213
|
+
|
|
214
|
+
test.beforeEach(async ({ authenticatedPage, apiErrors: _apiErrors }) => {
|
|
215
|
+
home = new DashboardHomePage(authenticatedPage)
|
|
216
|
+
await home.goto()
|
|
217
|
+
await home.waitForLoad()
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
test('owner sees team stats card', async () => {
|
|
221
|
+
await expect(home.teamStatsTitle).toBeVisible()
|
|
222
|
+
await expect(home.teamTimeSaved).toBeVisible()
|
|
223
|
+
await expect(home.teamFilesProcessed).toBeVisible()
|
|
224
|
+
await expect(home.teamParagraphs).toBeVisible()
|
|
225
|
+
})
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
test.describe('Dashboard Home - Security @security', () => {
|
|
229
|
+
test('no sensitive data in console', async ({ authenticatedPage, apiErrors: _apiErrors }) => {
|
|
230
|
+
const collector = createConsoleCollector(authenticatedPage)
|
|
231
|
+
const home = new DashboardHomePage(authenticatedPage)
|
|
232
|
+
await home.goto()
|
|
233
|
+
await home.waitForLoad()
|
|
234
|
+
collector.assertNoSensitiveLeaks()
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
test('search input resists XSS', async ({ authenticatedPage, apiErrors: _apiErrors }) => {
|
|
238
|
+
const home = new DashboardHomePage(authenticatedPage)
|
|
239
|
+
await home.goto()
|
|
240
|
+
await home.waitForLoad()
|
|
241
|
+
await testXssOnInput(authenticatedPage, home.searchInput)
|
|
242
|
+
})
|
|
243
|
+
})
|