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,134 @@
|
|
|
1
|
+
import type { Page, Locator } from '@playwright/test'
|
|
2
|
+
|
|
3
|
+
export class DashboardHomePage {
|
|
4
|
+
readonly page: Page
|
|
5
|
+
|
|
6
|
+
// Hero — AI Search
|
|
7
|
+
readonly logo: Locator
|
|
8
|
+
readonly heading: Locator
|
|
9
|
+
readonly subtitle: Locator
|
|
10
|
+
readonly aiAvatar: Locator
|
|
11
|
+
readonly searchInput: Locator
|
|
12
|
+
readonly askButton: Locator
|
|
13
|
+
|
|
14
|
+
// Ice Breaker Suggestions (3 dynamic buttons)
|
|
15
|
+
readonly suggestionButtons: Locator
|
|
16
|
+
|
|
17
|
+
// Profile Dropdown
|
|
18
|
+
readonly profileButton: Locator
|
|
19
|
+
readonly profileMenuName: Locator
|
|
20
|
+
readonly profileMenuEmail: Locator
|
|
21
|
+
readonly feedbackMenuItem: Locator
|
|
22
|
+
readonly logoutButton: Locator
|
|
23
|
+
|
|
24
|
+
// Feedback Dialog
|
|
25
|
+
readonly feedbackDialogTitle: Locator
|
|
26
|
+
readonly feedbackTypeCombobox: Locator
|
|
27
|
+
readonly feedbackMessageTextbox: Locator
|
|
28
|
+
readonly feedbackContactSwitch: Locator
|
|
29
|
+
readonly feedbackCancelButton: Locator
|
|
30
|
+
readonly feedbackSubmitButton: Locator
|
|
31
|
+
readonly feedbackCloseButton: Locator
|
|
32
|
+
|
|
33
|
+
// ROI Stats — My Stats
|
|
34
|
+
readonly roiSectionTitle: Locator
|
|
35
|
+
readonly myStatsTitle: Locator
|
|
36
|
+
readonly myTimeSaved: Locator
|
|
37
|
+
readonly myFilesProcessed: Locator
|
|
38
|
+
readonly myParagraphs: Locator
|
|
39
|
+
|
|
40
|
+
// ROI Stats — Team Stats (OWNER/ADMIN only)
|
|
41
|
+
readonly teamStatsTitle: Locator
|
|
42
|
+
readonly teamTimeSaved: Locator
|
|
43
|
+
readonly teamFilesProcessed: Locator
|
|
44
|
+
readonly teamParagraphs: Locator
|
|
45
|
+
|
|
46
|
+
// Organization Overview
|
|
47
|
+
readonly orgOverviewTitle: Locator
|
|
48
|
+
|
|
49
|
+
constructor(page: Page) {
|
|
50
|
+
this.page = page
|
|
51
|
+
|
|
52
|
+
// Hero — AI Search
|
|
53
|
+
this.logo = page.getByRole('img', { name: /logo hakutaku/i })
|
|
54
|
+
this.heading = page.getByRole('heading', { level: 1 })
|
|
55
|
+
this.subtitle = page.getByText(/faça qualquer pergunta|ask any question/i)
|
|
56
|
+
this.aiAvatar = page.getByRole('img', { name: /assistente de ia|ai assistant/i })
|
|
57
|
+
this.searchInput = page.getByRole('textbox', { name: /buscar nos seus documentos|search your documents/i })
|
|
58
|
+
this.askButton = page.getByRole('button', { name: /fazer pergunta|ask question/i })
|
|
59
|
+
|
|
60
|
+
// Ice Breaker Suggestions
|
|
61
|
+
this.suggestionButtons = page.getByRole('button', { name: /perguntar:/i })
|
|
62
|
+
|
|
63
|
+
// Profile Dropdown
|
|
64
|
+
this.profileButton = page.getByRole('button', { name: /bem-vindo|welcome/i })
|
|
65
|
+
this.profileMenuName = page.getByText('João Lima')
|
|
66
|
+
this.profileMenuEmail = page.getByText('joao.lima@hakutaku.ai')
|
|
67
|
+
this.feedbackMenuItem = page.getByRole('menuitem', { name: /enviar feedback|send feedback/i })
|
|
68
|
+
this.logoutButton = page.getByRole('button', { name: /sair|logout|sign out/i })
|
|
69
|
+
|
|
70
|
+
// Feedback Dialog
|
|
71
|
+
this.feedbackDialogTitle = page.getByRole('heading', { name: /enviar feedback|send feedback/i })
|
|
72
|
+
this.feedbackTypeCombobox = page.getByRole('combobox', { name: /tipo de feedback|feedback type/i })
|
|
73
|
+
this.feedbackMessageTextbox = page.getByRole('textbox', { name: /mensagem|message/i })
|
|
74
|
+
this.feedbackContactSwitch = page.getByRole('switch', { name: /permitir que nos contatem|allow us to contact/i })
|
|
75
|
+
this.feedbackCancelButton = page.getByRole('button', { name: /cancelar|cancel/i })
|
|
76
|
+
this.feedbackSubmitButton = page.getByRole('button', { name: /enviar feedback|send feedback/i }).last()
|
|
77
|
+
this.feedbackCloseButton = page.getByRole('button', { name: /close/i })
|
|
78
|
+
|
|
79
|
+
// ROI Stats — My Stats
|
|
80
|
+
this.roiSectionTitle = page.getByRole('heading', { name: /seu impacto este mês|your impact this month/i })
|
|
81
|
+
this.myStatsTitle = page.getByRole('heading', { name: /suas estatísticas|your statistics/i })
|
|
82
|
+
this.myTimeSaved = page.getByText(/tempo economizado|time saved/i).first()
|
|
83
|
+
this.myFilesProcessed = page.getByText(/arquivos processados|files processed/i).first()
|
|
84
|
+
this.myParagraphs = page.getByText(/parágrafos|paragraphs/i).first()
|
|
85
|
+
|
|
86
|
+
// ROI Stats — Team Stats
|
|
87
|
+
this.teamStatsTitle = page.getByRole('heading', { name: /estatísticas da equipe|team statistics/i })
|
|
88
|
+
this.teamTimeSaved = page.getByText(/tempo economizado|time saved/i).last()
|
|
89
|
+
this.teamFilesProcessed = page.getByText(/arquivos processados|files processed/i).last()
|
|
90
|
+
this.teamParagraphs = page.getByText(/parágrafos|paragraphs/i).last()
|
|
91
|
+
|
|
92
|
+
// Organization Overview
|
|
93
|
+
this.orgOverviewTitle = page.getByText(/visão geral da organização|organization overview/i)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async goto() {
|
|
97
|
+
await this.page.goto('/dashboard/home')
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async waitForLoad() {
|
|
101
|
+
await this.heading.waitFor({ timeout: 15000 })
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async typeSearch(query: string) {
|
|
105
|
+
await this.searchInput.fill(query)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async submitSearch(query: string) {
|
|
109
|
+
await this.searchInput.fill(query)
|
|
110
|
+
await this.askButton.click()
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async clickSuggestion(index: number) {
|
|
114
|
+
const buttons = await this.suggestionButtons.all()
|
|
115
|
+
if (buttons[index]) {
|
|
116
|
+
await buttons[index].click()
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async openProfileDropdown() {
|
|
121
|
+
await this.profileButton.click()
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async openFeedbackDialog() {
|
|
125
|
+
await this.openProfileDropdown()
|
|
126
|
+
await this.feedbackMenuItem.click()
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async fillFeedback(type: string, message: string) {
|
|
130
|
+
await this.feedbackTypeCombobox.click()
|
|
131
|
+
await this.page.getByRole('option', { name: type }).click()
|
|
132
|
+
await this.feedbackMessageTextbox.fill(message)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
import type { Page, Locator } from '@playwright/test'
|
|
2
|
+
|
|
3
|
+
export class DashboardIntegrationsPage {
|
|
4
|
+
readonly page: Page
|
|
5
|
+
|
|
6
|
+
// Page Header
|
|
7
|
+
readonly heading: Locator
|
|
8
|
+
readonly subheading: Locator
|
|
9
|
+
|
|
10
|
+
// Header Card
|
|
11
|
+
readonly headerCardTitle: Locator
|
|
12
|
+
readonly usageMeter: Locator
|
|
13
|
+
readonly addIntegrationButton: Locator
|
|
14
|
+
|
|
15
|
+
// Search & Filters
|
|
16
|
+
readonly searchInput: Locator
|
|
17
|
+
readonly refreshButton: Locator
|
|
18
|
+
readonly statusFilter: Locator
|
|
19
|
+
readonly typeFilter: Locator
|
|
20
|
+
readonly clearFiltersButton: Locator
|
|
21
|
+
|
|
22
|
+
// Integration Cards
|
|
23
|
+
readonly integrationCards: Locator
|
|
24
|
+
|
|
25
|
+
// Card Action Buttons (first card)
|
|
26
|
+
readonly firstCardPauseButton: Locator
|
|
27
|
+
readonly firstCardResumeButton: Locator
|
|
28
|
+
readonly firstCardDeleteButton: Locator
|
|
29
|
+
|
|
30
|
+
// Delete Confirmation Dialog
|
|
31
|
+
readonly deleteDialogHeading: Locator
|
|
32
|
+
readonly deleteDialogConfirmButton: Locator
|
|
33
|
+
readonly deleteDialogCancelButton: Locator
|
|
34
|
+
|
|
35
|
+
// Empty State
|
|
36
|
+
readonly emptyStateHeading: Locator
|
|
37
|
+
readonly emptyStateText: Locator
|
|
38
|
+
readonly emptyStateClearButton: Locator
|
|
39
|
+
|
|
40
|
+
// Pagination
|
|
41
|
+
readonly paginationText: Locator
|
|
42
|
+
|
|
43
|
+
constructor(page: Page) {
|
|
44
|
+
this.page = page
|
|
45
|
+
|
|
46
|
+
// Page Header
|
|
47
|
+
this.heading = page.getByRole('heading', { level: 1, name: /integrações|integrations/i })
|
|
48
|
+
this.subheading = page.getByText(/conecte e gerencie serviços externos|connect and manage external/i)
|
|
49
|
+
|
|
50
|
+
// Header Card
|
|
51
|
+
this.headerCardTitle = page.getByRole('heading', { level: 2, name: /integrações|integrations/i })
|
|
52
|
+
this.usageMeter = page.getByText(/uso de integrações|integration usage/i)
|
|
53
|
+
this.addIntegrationButton = page.getByRole('button', { name: /adicionar integração|add integration/i })
|
|
54
|
+
|
|
55
|
+
// Search & Filters
|
|
56
|
+
this.searchInput = page.getByRole('textbox', { name: /buscar integrações|search integrations/i })
|
|
57
|
+
this.refreshButton = page.getByRole('button', { name: /atualizar|refresh/i })
|
|
58
|
+
this.statusFilter = page.getByRole('combobox').first()
|
|
59
|
+
this.typeFilter = page.getByRole('combobox').nth(1)
|
|
60
|
+
this.clearFiltersButton = page.getByRole('button', { name: /limpar filtros|clear filters/i })
|
|
61
|
+
|
|
62
|
+
// Integration Cards
|
|
63
|
+
this.integrationCards = page.getByRole('heading', { level: 3 }).locator('visible=true')
|
|
64
|
+
|
|
65
|
+
// Card Action Buttons
|
|
66
|
+
this.firstCardPauseButton = page.getByRole('button', { name: /pausar sync/i }).first()
|
|
67
|
+
this.firstCardResumeButton = page.getByRole('button', { name: /retomar sync/i }).first()
|
|
68
|
+
this.firstCardDeleteButton = page.getByRole('button', { name: /excluir|delete/i }).first()
|
|
69
|
+
|
|
70
|
+
// Delete Confirmation Dialog
|
|
71
|
+
this.deleteDialogHeading = page.getByRole('heading', { name: /excluir integração|delete integration/i })
|
|
72
|
+
this.deleteDialogConfirmButton = page.getByRole('alertdialog').getByRole('button', { name: /excluir|delete/i })
|
|
73
|
+
this.deleteDialogCancelButton = page.getByRole('alertdialog').getByRole('button', { name: /cancelar|cancel/i })
|
|
74
|
+
|
|
75
|
+
// Empty State
|
|
76
|
+
this.emptyStateHeading = page.getByRole('heading', { level: 3, name: /nenhuma integração encontrada|no integration found/i })
|
|
77
|
+
this.emptyStateText = page.getByText(/tente ajustar sua busca|try adjusting your search/i)
|
|
78
|
+
this.emptyStateClearButton = page.getByRole('button', { name: /limpar filtros|clear filters/i })
|
|
79
|
+
|
|
80
|
+
// Pagination
|
|
81
|
+
this.paginationText = page.getByText(/exibindo \d+ - \d+ de \d+|showing \d+ - \d+ of \d+/i)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async goto() {
|
|
85
|
+
await this.page.goto('/dashboard/integrations')
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async waitForLoad() {
|
|
89
|
+
await this.heading.waitFor({ timeout: 15000 })
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async search(query: string) {
|
|
93
|
+
await this.searchInput.fill(query)
|
|
94
|
+
await this.page.waitForTimeout(500)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async clearSearch() {
|
|
98
|
+
await this.searchInput.clear()
|
|
99
|
+
await this.page.waitForTimeout(500)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async selectStatusFilter(option: string) {
|
|
103
|
+
await this.statusFilter.click()
|
|
104
|
+
await this.page.getByRole('option', { name: option }).click()
|
|
105
|
+
await this.page.waitForTimeout(500)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async selectTypeFilter(option: string) {
|
|
109
|
+
await this.typeFilter.click()
|
|
110
|
+
await this.page.getByRole('option', { name: option }).click()
|
|
111
|
+
await this.page.waitForTimeout(500)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async clearFilters() {
|
|
115
|
+
await this.clearFiltersButton.click()
|
|
116
|
+
await this.page.waitForTimeout(500)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async getCardCount(): Promise<number> {
|
|
120
|
+
return this.integrationCards.count()
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async clickAddIntegration() {
|
|
124
|
+
await this.addIntegrationButton.click()
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async clickIntegrationCard(name: string) {
|
|
128
|
+
await this.page.getByRole('heading', { level: 3, name }).click()
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async pauseFirstCard() {
|
|
132
|
+
await this.firstCardPauseButton.click()
|
|
133
|
+
await this.page.waitForTimeout(500)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async resumeFirstCard() {
|
|
137
|
+
await this.firstCardResumeButton.click()
|
|
138
|
+
await this.page.waitForTimeout(500)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async deleteFirstCard() {
|
|
142
|
+
await this.firstCardDeleteButton.click()
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async confirmDelete() {
|
|
146
|
+
await this.deleteDialogConfirmButton.click()
|
|
147
|
+
await this.page.waitForTimeout(1000)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async getCardStatus(cardIndex: number): Promise<string | null> {
|
|
151
|
+
const cards = this.page.locator('[class*="card"], [class*="Card"]')
|
|
152
|
+
const statusBadge = cards.nth(cardIndex).getByText(/connected|paused|error|disconnected/i)
|
|
153
|
+
return statusBadge.textContent()
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export class CreateIntegrationPage {
|
|
158
|
+
readonly page: Page
|
|
159
|
+
|
|
160
|
+
// Navigation
|
|
161
|
+
readonly backButton: Locator
|
|
162
|
+
|
|
163
|
+
// Page Header
|
|
164
|
+
readonly heading: Locator
|
|
165
|
+
|
|
166
|
+
// Wizard Steps
|
|
167
|
+
readonly stepButtons: Locator
|
|
168
|
+
|
|
169
|
+
// Integration Limit Card
|
|
170
|
+
readonly limitHeading: Locator
|
|
171
|
+
readonly limitProgress: Locator
|
|
172
|
+
|
|
173
|
+
// Step 1: Type Selection
|
|
174
|
+
readonly typeSelectionHeading: Locator
|
|
175
|
+
readonly uploadButton: Locator
|
|
176
|
+
readonly googleDriveButton: Locator
|
|
177
|
+
readonly oneDriveButton: Locator
|
|
178
|
+
readonly googleMeetButton: Locator
|
|
179
|
+
readonly notionButton: Locator
|
|
180
|
+
readonly slackButton: Locator
|
|
181
|
+
readonly githubButton: Locator
|
|
182
|
+
|
|
183
|
+
// Wizard Footer
|
|
184
|
+
readonly cancelButton: Locator
|
|
185
|
+
readonly previousButton: Locator
|
|
186
|
+
readonly nextButton: Locator
|
|
187
|
+
readonly stepIndicator: Locator
|
|
188
|
+
|
|
189
|
+
constructor(page: Page) {
|
|
190
|
+
this.page = page
|
|
191
|
+
|
|
192
|
+
// Navigation
|
|
193
|
+
this.backButton = page.getByRole('link', { name: /voltar para integrações|back to integrations/i })
|
|
194
|
+
|
|
195
|
+
// Page Header
|
|
196
|
+
this.heading = page.getByRole('heading', { level: 1, name: /criar nova integração|create new integration/i })
|
|
197
|
+
|
|
198
|
+
// Wizard Steps
|
|
199
|
+
this.stepButtons = page.getByRole('button', { name: /^[1-5]$/ })
|
|
200
|
+
|
|
201
|
+
// Limit Card
|
|
202
|
+
this.limitHeading = page.getByRole('heading', { level: 3, name: /limite de integrações|integration limit/i })
|
|
203
|
+
this.limitProgress = page.getByRole('progressbar')
|
|
204
|
+
|
|
205
|
+
// Step 1: Type Selection
|
|
206
|
+
this.typeSelectionHeading = page.getByRole('heading', { level: 2, name: /escolha o tipo|choose the type/i })
|
|
207
|
+
this.uploadButton = page.getByRole('button', { name: /upload de arquivos|file upload/i })
|
|
208
|
+
this.googleDriveButton = page.getByRole('button', { name: /google drive/i })
|
|
209
|
+
this.oneDriveButton = page.getByRole('button', { name: /onedrive|sharepoint/i })
|
|
210
|
+
this.googleMeetButton = page.getByRole('button', { name: /google meet/i })
|
|
211
|
+
this.notionButton = page.getByRole('button', { name: /notion/i })
|
|
212
|
+
this.slackButton = page.getByRole('button', { name: /slack/i })
|
|
213
|
+
this.githubButton = page.getByRole('button', { name: /github/i })
|
|
214
|
+
|
|
215
|
+
// Wizard Footer
|
|
216
|
+
this.cancelButton = page.getByRole('button', { name: /cancelar|cancel/i })
|
|
217
|
+
this.previousButton = page.getByRole('button', { name: /anterior|previous/i })
|
|
218
|
+
this.nextButton = page.getByRole('button', { name: /próximo|next/i })
|
|
219
|
+
this.stepIndicator = page.getByText(/etapa \d+ de \d+|step \d+ of \d+/i)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
async goto() {
|
|
223
|
+
await this.page.goto('/dashboard/integrations/new')
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
async waitForLoad() {
|
|
227
|
+
await this.heading.waitFor({ timeout: 15000 })
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async selectType(type: 'upload' | 'google-drive' | 'onedrive' | 'notion' | 'slack' | 'github') {
|
|
231
|
+
const buttonMap = {
|
|
232
|
+
'upload': this.uploadButton,
|
|
233
|
+
'google-drive': this.googleDriveButton,
|
|
234
|
+
'onedrive': this.oneDriveButton,
|
|
235
|
+
'notion': this.notionButton,
|
|
236
|
+
'slack': this.slackButton,
|
|
237
|
+
'github': this.githubButton,
|
|
238
|
+
}
|
|
239
|
+
await buttonMap[type].click()
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
async clickNext() {
|
|
243
|
+
await this.nextButton.click()
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
async clickPrevious() {
|
|
247
|
+
await this.previousButton.click()
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
async clickCancel() {
|
|
251
|
+
await this.cancelButton.click()
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export class IntegrationDetailPage {
|
|
256
|
+
readonly page: Page
|
|
257
|
+
|
|
258
|
+
// Header
|
|
259
|
+
readonly integrationName: Locator
|
|
260
|
+
readonly editNameButton: Locator
|
|
261
|
+
readonly backButton: Locator
|
|
262
|
+
|
|
263
|
+
// Action Buttons
|
|
264
|
+
readonly syncNowButton: Locator
|
|
265
|
+
readonly forceSyncButton: Locator
|
|
266
|
+
readonly pauseButton: Locator
|
|
267
|
+
readonly resumeButton: Locator
|
|
268
|
+
readonly configureButton: Locator
|
|
269
|
+
readonly deleteButton: Locator
|
|
270
|
+
|
|
271
|
+
// Stats
|
|
272
|
+
readonly statusBadge: Locator
|
|
273
|
+
|
|
274
|
+
// Settings Tab
|
|
275
|
+
readonly departmentCombobox: Locator
|
|
276
|
+
readonly teamCombobox: Locator
|
|
277
|
+
readonly newDepartmentButton: Locator
|
|
278
|
+
readonly newTeamButton: Locator
|
|
279
|
+
readonly saveConfigButton: Locator
|
|
280
|
+
|
|
281
|
+
// Tabs
|
|
282
|
+
readonly filesTab: Locator
|
|
283
|
+
readonly historyTab: Locator
|
|
284
|
+
readonly settingsTab: Locator
|
|
285
|
+
|
|
286
|
+
constructor(page: Page) {
|
|
287
|
+
this.page = page
|
|
288
|
+
|
|
289
|
+
// Header
|
|
290
|
+
this.integrationName = page.getByRole('heading', { level: 1 })
|
|
291
|
+
this.editNameButton = page.getByRole('button', { name: /editar nome|edit name/i })
|
|
292
|
+
this.backButton = page.getByRole('button', { name: /voltar para integrações|back to integrations/i })
|
|
293
|
+
|
|
294
|
+
// Action Buttons
|
|
295
|
+
this.syncNowButton = page.getByRole('button', { name: /sincronizar agora|sync now/i })
|
|
296
|
+
this.forceSyncButton = page.getByRole('button', { name: /forçar sync|force sync/i })
|
|
297
|
+
this.pauseButton = page.getByRole('button', { name: /^pausar$|^pause$/i })
|
|
298
|
+
this.resumeButton = page.getByRole('button', { name: /retomar|resume/i })
|
|
299
|
+
this.configureButton = page.getByRole('button', { name: /configurar|configure/i })
|
|
300
|
+
this.deleteButton = page.getByRole('button', { name: /excluir|delete/i })
|
|
301
|
+
|
|
302
|
+
// Stats
|
|
303
|
+
this.statusBadge = page.getByText(/connected|paused|disconnected|error/i).first()
|
|
304
|
+
|
|
305
|
+
// Settings Tab
|
|
306
|
+
this.departmentCombobox = page.getByRole('combobox', { name: /departamentos|departments/i })
|
|
307
|
+
this.teamCombobox = page.getByRole('combobox', { name: /equipes|teams/i })
|
|
308
|
+
this.newDepartmentButton = page.getByRole('button', { name: /^novo$|^new$/i })
|
|
309
|
+
this.newTeamButton = page.getByRole('button', { name: /^nova$|^new$/i })
|
|
310
|
+
this.saveConfigButton = page.getByRole('button', { name: /salvar configuração|save configuration/i })
|
|
311
|
+
|
|
312
|
+
// Tabs
|
|
313
|
+
this.filesTab = page.getByRole('tab', { name: /arquivos|files/i })
|
|
314
|
+
this.historyTab = page.getByRole('tab', { name: /histórico|history/i })
|
|
315
|
+
this.settingsTab = page.getByRole('tab', { name: /configurações|settings/i })
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
async goto(id: string) {
|
|
319
|
+
await this.page.goto(`/dashboard/integrations/${id}`)
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
async waitForLoad() {
|
|
323
|
+
await this.integrationName.waitFor({ timeout: 15000 })
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
async clickTab(tab: 'files' | 'history' | 'settings') {
|
|
327
|
+
const tabMap = {
|
|
328
|
+
files: this.filesTab,
|
|
329
|
+
history: this.historyTab,
|
|
330
|
+
settings: this.settingsTab,
|
|
331
|
+
}
|
|
332
|
+
await tabMap[tab].click()
|
|
333
|
+
}
|
|
334
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Page, Locator } from '@playwright/test'
|
|
2
|
+
|
|
3
|
+
export class DashboardKnowledgePage {
|
|
4
|
+
readonly page: Page
|
|
5
|
+
|
|
6
|
+
readonly heading: Locator
|
|
7
|
+
readonly searchInput: Locator
|
|
8
|
+
readonly fileList: Locator
|
|
9
|
+
readonly emptyState: Locator
|
|
10
|
+
|
|
11
|
+
constructor(page: Page) {
|
|
12
|
+
this.page = page
|
|
13
|
+
this.heading = page.getByRole('heading').first()
|
|
14
|
+
this.searchInput = page.getByPlaceholder(/search/i)
|
|
15
|
+
this.fileList = page.locator('table, [role="grid"]').first()
|
|
16
|
+
this.emptyState = page.getByText(/no files|no knowledge|empty/i)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async goto() {
|
|
20
|
+
await this.page.goto('/dashboard/knowledge')
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async waitForLoad() {
|
|
24
|
+
await this.page.waitForLoadState('networkidle')
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async search(query: string) {
|
|
28
|
+
await this.searchInput.fill(query)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { Page, Locator } from '@playwright/test'
|
|
2
|
+
|
|
3
|
+
export class DashboardOntologyPage {
|
|
4
|
+
readonly page: Page
|
|
5
|
+
|
|
6
|
+
// Page header
|
|
7
|
+
readonly heading: Locator
|
|
8
|
+
readonly description: Locator
|
|
9
|
+
|
|
10
|
+
// Stats cards
|
|
11
|
+
readonly entitiesStat: Locator
|
|
12
|
+
readonly factsStat: Locator
|
|
13
|
+
readonly episodesStat: Locator
|
|
14
|
+
readonly entityTypesStat: Locator
|
|
15
|
+
|
|
16
|
+
// Tabs
|
|
17
|
+
readonly graphTab: Locator
|
|
18
|
+
readonly entitiesTab: Locator
|
|
19
|
+
readonly factsTab: Locator
|
|
20
|
+
readonly timelineTab: Locator
|
|
21
|
+
readonly searchTab: Locator
|
|
22
|
+
|
|
23
|
+
// Tab content
|
|
24
|
+
readonly graphEmptyState: Locator
|
|
25
|
+
readonly searchInput: Locator
|
|
26
|
+
|
|
27
|
+
constructor(page: Page) {
|
|
28
|
+
this.page = page
|
|
29
|
+
|
|
30
|
+
// Page header
|
|
31
|
+
this.heading = page.getByRole('heading', { level: 1 })
|
|
32
|
+
this.description = page.getByText(/Explore entidades/i)
|
|
33
|
+
|
|
34
|
+
// Stats cards
|
|
35
|
+
this.entitiesStat = page.getByText(/Entidades/i).first()
|
|
36
|
+
this.factsStat = page.getByText(/Fatos/i).first()
|
|
37
|
+
this.episodesStat = page.getByText(/Episódios/i)
|
|
38
|
+
this.entityTypesStat = page.getByText(/Tipos de Entidade/i)
|
|
39
|
+
|
|
40
|
+
// Tabs
|
|
41
|
+
this.graphTab = page.getByRole('tab', { name: /Grafo/i })
|
|
42
|
+
this.entitiesTab = page.getByRole('tab', { name: /Entidades/i })
|
|
43
|
+
this.factsTab = page.getByRole('tab', { name: /Fatos/i })
|
|
44
|
+
this.timelineTab = page.getByRole('tab', { name: /Linha do Tempo/i })
|
|
45
|
+
this.searchTab = page.getByRole('tab', { name: /Busca/i })
|
|
46
|
+
|
|
47
|
+
// Tab content
|
|
48
|
+
this.graphEmptyState = page.getByText(/No data yet/i)
|
|
49
|
+
this.searchInput = page.getByRole('textbox', { name: /Search the knowledge graph/i })
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async goto(tab?: 'graph' | 'entities' | 'facts' | 'timeline' | 'search') {
|
|
53
|
+
const url = tab ? `/dashboard/ontology?tab=${tab}` : '/dashboard/ontology'
|
|
54
|
+
await this.page.goto(url)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async waitForLoad() {
|
|
58
|
+
await this.heading.waitFor({ timeout: 15000 })
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async switchTab(tab: 'graph' | 'entities' | 'facts' | 'timeline' | 'search') {
|
|
62
|
+
const tabLocator = {
|
|
63
|
+
graph: this.graphTab,
|
|
64
|
+
entities: this.entitiesTab,
|
|
65
|
+
facts: this.factsTab,
|
|
66
|
+
timeline: this.timelineTab,
|
|
67
|
+
search: this.searchTab,
|
|
68
|
+
}[tab]
|
|
69
|
+
await tabLocator.click()
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { Page, Locator } from '@playwright/test'
|
|
2
|
+
|
|
3
|
+
export class DashboardProfilePage {
|
|
4
|
+
readonly page: Page
|
|
5
|
+
|
|
6
|
+
readonly heading: Locator
|
|
7
|
+
readonly avatar: Locator
|
|
8
|
+
readonly displayName: Locator
|
|
9
|
+
readonly displayEmail: Locator
|
|
10
|
+
|
|
11
|
+
// Account Info Card
|
|
12
|
+
readonly accountCardTitle: Locator
|
|
13
|
+
readonly nameInput: Locator
|
|
14
|
+
readonly emailInput: Locator
|
|
15
|
+
readonly userIdInput: Locator
|
|
16
|
+
|
|
17
|
+
constructor(page: Page) {
|
|
18
|
+
this.page = page
|
|
19
|
+
this.heading = page.getByRole('heading', { level: 1 })
|
|
20
|
+
this.avatar = page.getByRole('img', { name: /joão lima/i })
|
|
21
|
+
this.displayName = page.getByText('João Lima').first()
|
|
22
|
+
this.displayEmail = page.getByText('joao.lima@hakutaku.ai').first()
|
|
23
|
+
|
|
24
|
+
// Account Info Card
|
|
25
|
+
this.accountCardTitle = page.getByText(/informações da conta|account info/i)
|
|
26
|
+
this.nameInput = page.getByRole('textbox', { name: /nome completo|full name/i })
|
|
27
|
+
this.emailInput = page.getByRole('textbox', { name: /endereço de e-mail|email address/i })
|
|
28
|
+
this.userIdInput = page.getByRole('textbox', { name: /id do usuário|user id/i })
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async goto() {
|
|
32
|
+
await this.page.goto('/dashboard/profile')
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async waitForLoad() {
|
|
36
|
+
await this.heading.waitFor({ timeout: 15000 })
|
|
37
|
+
}
|
|
38
|
+
}
|