Skip to content

Commit 637b618

Browse files
committed
chore(playwright): add full cross service e2e tests
Signed-off-by: William Phetsinorath <william.phetsinorath-open@interieur.gouv.fr>
1 parent 0155268 commit 637b618

3 files changed

Lines changed: 124 additions & 0 deletions

File tree

apps/client/src/components/AdminRoleForm.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ function closeModal() {
206206
v-for="user in users"
207207
:id="`${user.id}-cbx`"
208208
:key="user.email"
209+
:data-testid="`user-${user.id}`"
209210
:label="`${user.lastName} ${user.firstName}`"
210211
:hint="user.email"
211212
:name="`checkbox-${user.id}`"

apps/client/src/views/admin/ListPlugins.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ const activeAccordion = ref<number>()
8787
<DsfrAccordion
8888
:id="service.name"
8989
:title="service.title || service.name"
90+
:data-testid="`accordion-${service.name}`"
9091
>
9192
<p
9293
v-if="service.description"
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import { test, expect, type APIRequestContext } from '@playwright/test'
2+
import { clientURL, signInCloudPiNative, adminUser, tcolinUser } from '../config/console'
3+
import { loadKeycloakConfig } from '../config/keycloak'
4+
import { faker } from '@faker-js/faker'
5+
6+
test.describe('Cross-Service Role Propagation', () => {
7+
let oidcGroupSuffix: string
8+
let oidcGroupPath: string
9+
let roleName: string
10+
11+
test.beforeEach(() => {
12+
oidcGroupSuffix = faker.string.alpha(10).toLowerCase()
13+
oidcGroupPath = `/console/test-admin-${oidcGroupSuffix}`
14+
roleName = `CrossTestRole-${oidcGroupSuffix}`
15+
})
16+
17+
test('Should propagate admin role to GitLab, SonarQube, and Keycloak', { tag: '@e2e' }, async ({ page, request }) => {
18+
// Sign in to Console
19+
await page.goto(clientURL)
20+
await signInCloudPiNative({ page, credentials: adminUser })
21+
22+
// Configure Plugins to use the test OIDC group
23+
// Configure GitLab
24+
await page.getByTestId('menuAdministrationBtn').click()
25+
await page.getByTestId('menuAdministrationPlugins').click()
26+
await page.getByTestId('accordion-gitlab').click()
27+
28+
// Fill Admin Group Path
29+
const gitlabAdminInput = page.getByTestId('accordion-gitlab').getByLabel('Chemin du groupe OIDC Admin')
30+
await gitlabAdminInput.fill(oidcGroupPath)
31+
await page.getByTestId('saveBtn').click()
32+
await expect(page.getByText('Paramètres sauvegardés')).toBeVisible()
33+
await page.getByTestId('accordion-gitlab').click() // Close accordion
34+
35+
// Create the Role in Console
36+
await page.getByTestId('menuAdministrationRoles').click()
37+
await expect(page.getByTestId('role-list')).not.toContainText(roleName)
38+
await page.getByTestId('addRoleBtn').click()
39+
await expect(page.getByTestId('snackbar')).toContainText('Rôle ajouté')
40+
await expect(page.getByTestId('saveBtn')).toBeDisabled()
41+
await expect(page.getByTestId('roleNameInput')).toHaveValue('Nouveau rôle')
42+
await page.getByTestId('roleNameInput').fill(roleName)
43+
await page.getByTestId('oidcGroupInput').fill(oidcGroupPath)
44+
await page.getByTestId('saveBtn').click()
45+
await expect(page.getByTestId('role-list')).toContainText(roleName)
46+
47+
// Navigate to the role's members
48+
await page.getByTestId('test-members').click()
49+
50+
// Add user
51+
await expect(page.getByTestId('addUserBtn')).toBeDisabled()
52+
await page
53+
.getByTestId('addUserSuggestionInput')
54+
.locator('input')
55+
.fill(tcolinUser.email)
56+
await page.getByTestId('addUserBtn').click()
57+
await page.getByTestId('menuAdministrationUsers').click()
58+
await expect(page.getByTestId(`user-${tcolinUser.id}`)).toContainText(
59+
roleName,
60+
)
61+
62+
// Check if user is in group `oidcGroupPath`
63+
// Keycloak API
64+
const kcToken = await getKeycloakToken(request)
65+
const kcGroup = await getKeycloakGroup(request, kcToken, oidcGroupPath)
66+
expect(kcGroup, 'Group should exist in Keycloak').toBeDefined()
67+
const kcMembers = await getKeycloakGroupMembers(request, kcToken, kcGroup.id)
68+
const kcMember = kcMembers.find((m: any) => m.email === tcolinUser.email)
69+
expect(kcMember, 'User should be in Keycloak group').toBeDefined()
70+
71+
// Check if user is in group (or is admin if we configured admin path)
72+
// Since we configured `adminGroupPath` to `oidcGroupPath`, the user should become an Admin.
73+
const glUser = await getGitLabUser(request, tcolinUser.email)
74+
expect(glUser.is_admin, 'User should be GitLab Admin').toBe(true)
75+
})
76+
})
77+
78+
// Helpers
79+
async function getKeycloakToken(request: APIRequestContext) {
80+
const config = loadKeycloakConfig()
81+
const adminResponse = await request.post(`${config.url}/realms/master/protocol/openid-connect/token`, {
82+
form: {
83+
username: config.adminUser,
84+
password: config.adminPass,
85+
grant_type: 'password',
86+
client_id: 'admin-cli',
87+
},
88+
})
89+
const json = await adminResponse.json()
90+
return json.access_token
91+
}
92+
93+
async function getKeycloakGroup(request: APIRequestContext, token: string, path: string) {
94+
const config = loadKeycloakConfig()
95+
const search = path.split('/').at(-1)
96+
if (!search) {
97+
throw new Error('Invalid group path')
98+
}
99+
const response = await request.get(`${config.url}/admin/realms/${config.realm}/groups`, {
100+
headers: { Authorization: `Bearer ${token}` },
101+
params: { search },
102+
})
103+
const groups = await response.json()
104+
return groups.find((g: any) => g.path === path)
105+
}
106+
107+
async function getKeycloakGroupMembers(request: APIRequestContext, token: string, groupId: string) {
108+
const config = loadKeycloakConfig()
109+
const response = await request.get(`${config.url}/admin/realms/${config.realm}/groups/${groupId}/members`, {
110+
headers: { Authorization: `Bearer ${token}` },
111+
})
112+
return await response.json()
113+
}
114+
115+
async function getGitLabUser(request: APIRequestContext, email: string) {
116+
const response = await request.get(`${process.env.GITLAB_URL}/api/v4/users`, {
117+
headers: { 'PRIVATE-TOKEN': process.env.GITLAB_TOKEN || '' },
118+
params: { search: email },
119+
})
120+
const users = await response.json()
121+
return users[0]
122+
}

0 commit comments

Comments
 (0)