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.
- package/package.json +2 -2
- package/template/.claude/commands/e2e-audit.md +16 -0
- package/template/.claude/hooks/e2e-audit-session-start.sh +4 -0
- package/template/.claude/settings.json +4 -0
- package/template/.claude/skills/e2e-audit/SKILL.md +216 -660
- package/template/.claude/skills/e2e-audit/findings.schema.json +98 -0
- package/template/.claude/skills/e2e-audit/references/api-contract-playbook.md +66 -0
- package/template/.claude/skills/e2e-audit/references/auth-setup-playbook.md +78 -0
- package/template/.claude/skills/e2e-audit/references/coverage-gap-playbook.md +95 -0
- package/template/.claude/skills/e2e-audit/references/post-run-feedback-playbook.md +80 -0
- package/template/.claude/skills/e2e-audit/scripts/detect-stack.sh +205 -0
- package/template/.claude/skills/e2e-audit/scripts/detect-uncovered.sh +137 -0
- package/template/.claude/skills/e2e-audit/scripts/discover-api-surface.sh +242 -0
- package/template/.claude/skills/e2e-audit/scripts/discover-routes.sh +163 -0
- package/template/.claude/skills/e2e-audit/scripts/inventory-existing-tests.sh +161 -0
- package/template/.claude/skills/e2e-audit/scripts/verify-audit.sh +88 -0
- package/template/.claude/skills/e2e-audit/templates/auth-setup.ts.tpl +24 -0
- package/template/.claude/skills/e2e-audit/templates/base-fixture.ts.tpl +75 -0
- package/template/.claude/skills/e2e-audit/templates/findings-report.md.tpl +54 -0
- package/template/.claude/skills/e2e-audit/templates/post-run-feedback.md.tpl +36 -0
- package/template/.claude/skills/e2e-audit/DESIGN.md +0 -294
- package/template/.claude/skills/e2e-audit/e2e/fixtures/auth.setup.ts +0 -70
- package/template/.claude/skills/e2e-audit/e2e/fixtures/auth.ts +0 -21
- package/template/.claude/skills/e2e-audit/e2e/fixtures/base.ts +0 -90
- package/template/.claude/skills/e2e-audit/e2e/fixtures/storage/.gitkeep +0 -0
- package/template/.claude/skills/e2e-audit/e2e/fixtures/storage/admin.json +0 -50
- package/template/.claude/skills/e2e-audit/e2e/fixtures/storage/manager.json +0 -50
- package/template/.claude/skills/e2e-audit/e2e/fixtures/storage/member.json +0 -50
- package/template/.claude/skills/e2e-audit/e2e/fixtures/storage/owner.json +0 -50
- package/template/.claude/skills/e2e-audit/e2e/pages/dashboard-admin.page.ts +0 -141
- package/template/.claude/skills/e2e-audit/e2e/pages/dashboard-billing.page.ts +0 -47
- package/template/.claude/skills/e2e-audit/e2e/pages/dashboard-chat.page.ts +0 -35
- package/template/.claude/skills/e2e-audit/e2e/pages/dashboard-home.page.ts +0 -134
- package/template/.claude/skills/e2e-audit/e2e/pages/dashboard-integrations.page.ts +0 -334
- package/template/.claude/skills/e2e-audit/e2e/pages/dashboard-knowledge.page.ts +0 -30
- package/template/.claude/skills/e2e-audit/e2e/pages/dashboard-ontology.page.ts +0 -71
- package/template/.claude/skills/e2e-audit/e2e/pages/dashboard-profile.page.ts +0 -38
- package/template/.claude/skills/e2e-audit/e2e/pages/dashboard-teams.page.ts +0 -123
- package/template/.claude/skills/e2e-audit/e2e/pages/dashboard-transcripts.page.ts +0 -109
- package/template/.claude/skills/e2e-audit/e2e/specs/auth/login.spec.ts +0 -59
- package/template/.claude/skills/e2e-audit/e2e/specs/dashboard-admin.spec.ts +0 -233
- package/template/.claude/skills/e2e-audit/e2e/specs/dashboard-billing.spec.ts +0 -44
- package/template/.claude/skills/e2e-audit/e2e/specs/dashboard-chat.spec.ts +0 -50
- package/template/.claude/skills/e2e-audit/e2e/specs/dashboard-home.spec.ts +0 -243
- package/template/.claude/skills/e2e-audit/e2e/specs/dashboard-integrations.spec.ts +0 -472
- package/template/.claude/skills/e2e-audit/e2e/specs/dashboard-knowledge.spec.ts +0 -57
- package/template/.claude/skills/e2e-audit/e2e/specs/dashboard-ontology.spec.ts +0 -72
- package/template/.claude/skills/e2e-audit/e2e/specs/dashboard-profile.spec.ts +0 -48
- package/template/.claude/skills/e2e-audit/e2e/specs/dashboard-teams.spec.ts +0 -247
- package/template/.claude/skills/e2e-audit/e2e/specs/dashboard-transcripts.spec.ts +0 -122
- package/template/.claude/skills/e2e-audit/e2e/specs/security/headers.spec.ts +0 -39
- package/template/.claude/skills/e2e-audit/e2e/specs/security/rbac.spec.ts +0 -92
- package/template/.claude/skills/e2e-audit/e2e/specs/security/xss.spec.ts +0 -74
- package/template/.claude/skills/e2e-audit/e2e/utils/console-collector.ts +0 -89
- package/template/.claude/skills/e2e-audit/e2e/utils/security-helpers.ts +0 -114
- package/template/.claude/skills/e2e-audit/e2e/utils/test-data.ts +0 -64
- 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
|
-
}
|