start-vibing 4.4.0 → 4.4.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.
Files changed (57) hide show
  1. package/package.json +2 -2
  2. package/template/.claude/commands/e2e-audit.md +16 -0
  3. package/template/.claude/hooks/e2e-audit-session-start.sh +4 -0
  4. package/template/.claude/settings.json +4 -0
  5. package/template/.claude/skills/e2e-audit/SKILL.md +216 -660
  6. package/template/.claude/skills/e2e-audit/findings.schema.json +98 -0
  7. package/template/.claude/skills/e2e-audit/references/api-contract-playbook.md +66 -0
  8. package/template/.claude/skills/e2e-audit/references/auth-setup-playbook.md +78 -0
  9. package/template/.claude/skills/e2e-audit/references/coverage-gap-playbook.md +95 -0
  10. package/template/.claude/skills/e2e-audit/references/post-run-feedback-playbook.md +80 -0
  11. package/template/.claude/skills/e2e-audit/scripts/detect-stack.sh +205 -0
  12. package/template/.claude/skills/e2e-audit/scripts/detect-uncovered.sh +137 -0
  13. package/template/.claude/skills/e2e-audit/scripts/discover-api-surface.sh +242 -0
  14. package/template/.claude/skills/e2e-audit/scripts/discover-routes.sh +163 -0
  15. package/template/.claude/skills/e2e-audit/scripts/inventory-existing-tests.sh +161 -0
  16. package/template/.claude/skills/e2e-audit/scripts/verify-audit.sh +88 -0
  17. package/template/.claude/skills/e2e-audit/templates/auth-setup.ts.tpl +24 -0
  18. package/template/.claude/skills/e2e-audit/templates/base-fixture.ts.tpl +75 -0
  19. package/template/.claude/skills/e2e-audit/templates/findings-report.md.tpl +54 -0
  20. package/template/.claude/skills/e2e-audit/templates/post-run-feedback.md.tpl +36 -0
  21. package/template/.claude/skills/e2e-audit/DESIGN.md +0 -294
  22. package/template/.claude/skills/e2e-audit/e2e/fixtures/auth.setup.ts +0 -70
  23. package/template/.claude/skills/e2e-audit/e2e/fixtures/auth.ts +0 -21
  24. package/template/.claude/skills/e2e-audit/e2e/fixtures/base.ts +0 -90
  25. package/template/.claude/skills/e2e-audit/e2e/fixtures/storage/.gitkeep +0 -0
  26. package/template/.claude/skills/e2e-audit/e2e/fixtures/storage/admin.json +0 -50
  27. package/template/.claude/skills/e2e-audit/e2e/fixtures/storage/manager.json +0 -50
  28. package/template/.claude/skills/e2e-audit/e2e/fixtures/storage/member.json +0 -50
  29. package/template/.claude/skills/e2e-audit/e2e/fixtures/storage/owner.json +0 -50
  30. package/template/.claude/skills/e2e-audit/e2e/pages/dashboard-admin.page.ts +0 -141
  31. package/template/.claude/skills/e2e-audit/e2e/pages/dashboard-billing.page.ts +0 -47
  32. package/template/.claude/skills/e2e-audit/e2e/pages/dashboard-chat.page.ts +0 -35
  33. package/template/.claude/skills/e2e-audit/e2e/pages/dashboard-home.page.ts +0 -134
  34. package/template/.claude/skills/e2e-audit/e2e/pages/dashboard-integrations.page.ts +0 -334
  35. package/template/.claude/skills/e2e-audit/e2e/pages/dashboard-knowledge.page.ts +0 -30
  36. package/template/.claude/skills/e2e-audit/e2e/pages/dashboard-ontology.page.ts +0 -71
  37. package/template/.claude/skills/e2e-audit/e2e/pages/dashboard-profile.page.ts +0 -38
  38. package/template/.claude/skills/e2e-audit/e2e/pages/dashboard-teams.page.ts +0 -123
  39. package/template/.claude/skills/e2e-audit/e2e/pages/dashboard-transcripts.page.ts +0 -109
  40. package/template/.claude/skills/e2e-audit/e2e/specs/auth/login.spec.ts +0 -59
  41. package/template/.claude/skills/e2e-audit/e2e/specs/dashboard-admin.spec.ts +0 -233
  42. package/template/.claude/skills/e2e-audit/e2e/specs/dashboard-billing.spec.ts +0 -44
  43. package/template/.claude/skills/e2e-audit/e2e/specs/dashboard-chat.spec.ts +0 -50
  44. package/template/.claude/skills/e2e-audit/e2e/specs/dashboard-home.spec.ts +0 -243
  45. package/template/.claude/skills/e2e-audit/e2e/specs/dashboard-integrations.spec.ts +0 -472
  46. package/template/.claude/skills/e2e-audit/e2e/specs/dashboard-knowledge.spec.ts +0 -57
  47. package/template/.claude/skills/e2e-audit/e2e/specs/dashboard-ontology.spec.ts +0 -72
  48. package/template/.claude/skills/e2e-audit/e2e/specs/dashboard-profile.spec.ts +0 -48
  49. package/template/.claude/skills/e2e-audit/e2e/specs/dashboard-teams.spec.ts +0 -247
  50. package/template/.claude/skills/e2e-audit/e2e/specs/dashboard-transcripts.spec.ts +0 -122
  51. package/template/.claude/skills/e2e-audit/e2e/specs/security/headers.spec.ts +0 -39
  52. package/template/.claude/skills/e2e-audit/e2e/specs/security/rbac.spec.ts +0 -92
  53. package/template/.claude/skills/e2e-audit/e2e/specs/security/xss.spec.ts +0 -74
  54. package/template/.claude/skills/e2e-audit/e2e/utils/console-collector.ts +0 -89
  55. package/template/.claude/skills/e2e-audit/e2e/utils/security-helpers.ts +0 -114
  56. package/template/.claude/skills/e2e-audit/e2e/utils/test-data.ts +0 -64
  57. package/template/.claude/skills/e2e-audit/runbook.md +0 -115
@@ -1,141 +0,0 @@
1
- import type { Page, Locator } from '@playwright/test'
2
-
3
- export class DashboardAdminPage {
4
- readonly page: Page
5
-
6
- // Page header
7
- readonly heading: Locator
8
- readonly adminBadge: Locator
9
- readonly welcomeText: Locator
10
-
11
- // Tabs
12
- readonly organizationsTab: Locator
13
- readonly usersTab: Locator
14
- readonly systemOverviewTab: Locator
15
-
16
- // --- Organizations Tab ---
17
- readonly orgHeading: Locator
18
- readonly orgRefreshButton: Locator
19
- readonly orgSearchInput: Locator
20
- readonly orgTable: Locator
21
- readonly newOrgButton: Locator
22
-
23
- // --- Users Tab ---
24
- readonly usersHeading: Locator
25
- readonly usersRefreshButton: Locator
26
- readonly usersSearchInput: Locator
27
- readonly usersTable: Locator
28
-
29
- // --- System Overview Tab ---
30
- readonly systemHeading: Locator
31
- readonly systemErrorHeading: Locator
32
- readonly systemRetryButton: Locator
33
-
34
- // --- Organization Detail Dialog ---
35
- readonly orgDetailDialog: Locator
36
- readonly orgDetailOverviewTab: Locator
37
- readonly orgDetailMembersTab: Locator
38
- readonly orgDetailSettingsTab: Locator
39
- readonly orgDetailEditButton: Locator
40
- readonly orgDetailDeleteButton: Locator
41
-
42
- // --- New Organization Dialog ---
43
- readonly newOrgNameInput: Locator
44
- readonly newOrgSubdomainInput: Locator
45
- readonly newOrgPrevButton: Locator
46
- readonly newOrgNextButton: Locator
47
-
48
- // --- User Edit Dialog ---
49
- readonly userEditNameInput: Locator
50
- readonly userEditStatusCombobox: Locator
51
- readonly userEditSaveButton: Locator
52
-
53
- constructor(page: Page) {
54
- this.page = page
55
-
56
- // Page header
57
- this.heading = page.getByRole('heading', { level: 1 })
58
- this.adminBadge = page.getByText('Administrador').first()
59
- this.welcomeText = page.getByText(/Bem-vindo/i)
60
-
61
- // Tabs
62
- this.organizationsTab = page.getByRole('tab', { name: /Organiza/i })
63
- this.usersTab = page.getByRole('tab', { name: /Usu/i })
64
- this.systemOverviewTab = page.getByRole('tab', { name: /Vis.*Sistema/i })
65
-
66
- // --- Organizations Tab ---
67
- this.orgHeading = page.getByRole('heading', { name: /Gerenciamento de Organiza/i })
68
- this.orgRefreshButton = page.getByRole('button', { name: /Atualizar/i })
69
- this.orgSearchInput = page.getByRole('textbox', { name: /buscar.*nome.*subdom/i })
70
- this.orgTable = page.getByRole('table')
71
- this.newOrgButton = page.getByRole('button', { name: /Nova Organiza/i })
72
-
73
- // --- Users Tab ---
74
- this.usersHeading = page.getByRole('heading', { name: /Gerenciamento de Usu/i })
75
- this.usersRefreshButton = page.getByRole('button', { name: /Atualizar/i })
76
- this.usersSearchInput = page.getByRole('textbox', { name: /buscar.*nome.*e-mail/i })
77
- this.usersTable = page.getByRole('table')
78
-
79
- // --- System Overview Tab ---
80
- this.systemHeading = page.getByRole('heading', { name: /Vis.*Geral.*Sistema/i, level: 2 })
81
- this.systemErrorHeading = page.getByRole('heading', { name: /Falha ao carregar/i })
82
- this.systemRetryButton = page.getByRole('button', { name: /Tentar Novamente/i })
83
-
84
- // --- Organization Detail Dialog ---
85
- this.orgDetailDialog = page.getByRole('dialog')
86
- this.orgDetailOverviewTab = page.getByRole('dialog').getByRole('tab', { name: /Vis.*Geral/i })
87
- this.orgDetailMembersTab = page.getByRole('dialog').getByRole('tab', { name: /Membros/i })
88
- this.orgDetailSettingsTab = page.getByRole('dialog').getByRole('tab', { name: /Configura/i })
89
- this.orgDetailEditButton = page.getByRole('dialog').getByRole('button', { name: /Editar Organiza/i })
90
- this.orgDetailDeleteButton = page.getByRole('dialog').getByRole('button', { name: /Excluir Organiza/i })
91
-
92
- // --- New Organization Dialog ---
93
- this.newOrgNameInput = page.getByRole('dialog').getByRole('textbox', { name: /Nome da Organiza/i })
94
- this.newOrgSubdomainInput = page.getByRole('dialog').getByRole('textbox', { name: /Subdom/i })
95
- this.newOrgPrevButton = page.getByRole('dialog').getByRole('button', { name: /Anterior/i })
96
- this.newOrgNextButton = page.getByRole('dialog').getByRole('button', { name: /Próximo/i })
97
-
98
- // --- User Edit Dialog ---
99
- this.userEditNameInput = page.getByRole('dialog').getByRole('textbox', { name: /Nome/i })
100
- this.userEditStatusCombobox = page.getByRole('dialog').getByRole('combobox').first()
101
- this.userEditSaveButton = page.getByRole('dialog').getByRole('button', { name: /Salvar/i })
102
- }
103
-
104
- async goto(tab?: 'organizations' | 'users' | 'system') {
105
- const url = tab ? `/dashboard/admin?tab=${tab}` : '/dashboard/admin'
106
- await this.page.goto(url)
107
- }
108
-
109
- async waitForLoad() {
110
- await this.heading.waitFor({ timeout: 15000 })
111
- }
112
-
113
- async switchTab(tab: 'organizations' | 'users' | 'system') {
114
- const tabLocator = {
115
- organizations: this.organizationsTab,
116
- users: this.usersTab,
117
- system: this.systemOverviewTab,
118
- }[tab]
119
- await tabLocator.click()
120
- }
121
-
122
- async clickOrgRow(orgName: string) {
123
- await this.page.getByRole('row').filter({ hasText: orgName }).click()
124
- }
125
-
126
- async clickUserRow(userName: string) {
127
- await this.page.getByRole('row').filter({ hasText: userName }).click()
128
- }
129
-
130
- async getOrgRowCount() {
131
- return this.orgTable.getByRole('row').filter({ hasText: /Linha \d+/ }).count()
132
- }
133
-
134
- async getUserRowCount() {
135
- return this.usersTable.getByRole('row').filter({ hasText: /Linha \d+/ }).count()
136
- }
137
-
138
- async closeDialog() {
139
- await this.page.getByRole('dialog').getByRole('button', { name: /Close/i }).click()
140
- }
141
- }
@@ -1,47 +0,0 @@
1
- import type { Page, Locator } from '@playwright/test'
2
-
3
- export class DashboardBillingPage {
4
- readonly page: Page
5
-
6
- // Page header
7
- readonly heading: Locator
8
- readonly description: Locator
9
-
10
- // Subscription card
11
- readonly planLogo: Locator
12
- readonly planName: Locator
13
- readonly planLabel: Locator
14
- readonly activeUsersLabel: Locator
15
- readonly statusLabel: Locator
16
- readonly subscribeButton: Locator
17
-
18
- // Footer
19
- readonly stripeSecurityText: Locator
20
-
21
- constructor(page: Page) {
22
- this.page = page
23
-
24
- // Page header
25
- this.heading = page.getByRole('heading', { level: 1 })
26
- this.description = page.getByText(/Gerencie sua assinatura/i)
27
-
28
- // Subscription card
29
- this.planLogo = page.locator('main img').first()
30
- this.planName = page.getByText('Hakutaku Standard').first()
31
- this.planLabel = page.getByText(/Plano/i).first()
32
- this.activeUsersLabel = page.getByText(/Usuários ativos/i)
33
- this.statusLabel = page.getByText(/Status/i).first()
34
- this.subscribeButton = page.getByRole('button', { name: /Assinar agora/i })
35
-
36
- // Footer
37
- this.stripeSecurityText = page.getByText(/Pagamentos processados com segurança/i)
38
- }
39
-
40
- async goto() {
41
- await this.page.goto('/dashboard/billing')
42
- }
43
-
44
- async waitForLoad() {
45
- await this.heading.waitFor({ timeout: 15000 })
46
- }
47
- }
@@ -1,35 +0,0 @@
1
- import type { Page, Locator } from '@playwright/test'
2
-
3
- export class DashboardChatPage {
4
- readonly page: Page
5
-
6
- readonly heading: Locator
7
- readonly messageInput: Locator
8
- readonly sendButton: Locator
9
- readonly messageList: Locator
10
- readonly newChatButton: Locator
11
- readonly sessionList: Locator
12
-
13
- constructor(page: Page) {
14
- this.page = page
15
- this.heading = page.getByRole('heading').first()
16
- this.messageInput = page.getByRole('textbox').first()
17
- this.sendButton = page.getByRole('button', { name: /send/i })
18
- this.messageList = page.locator('[data-testid="message-list"]')
19
- this.newChatButton = page.getByRole('button', { name: /new.*chat/i })
20
- this.sessionList = page.locator('[data-testid="session-list"]')
21
- }
22
-
23
- async goto() {
24
- await this.page.goto('/dashboard/chat')
25
- }
26
-
27
- async waitForLoad() {
28
- await this.page.waitForLoadState('networkidle')
29
- }
30
-
31
- async sendMessage(text: string) {
32
- await this.messageInput.fill(text)
33
- await this.sendButton.click()
34
- }
35
- }
@@ -1,134 +0,0 @@
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
- }
@@ -1,334 +0,0 @@
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
- }