From 59f34997993144d72c91382d5aab59e97da4e7fe Mon Sep 17 00:00:00 2001 From: Quentin BEY Date: Wed, 13 Nov 2024 16:54:54 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=85(e2e)=20add=20specific=20accounts=20fo?= =?UTF-8?q?r=20testing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This creates a bunch of accounts with various profiles to allow testing in a specific "mode" --- docker/auth/realm.json | 210 ++++++++++++++++++ docs/testsE2E.md | 51 +++++ .../demo/management/commands/create_demo.py | 72 +++++- .../demo/tests/test_commands_create_demo.py | 17 +- .../apps/e2e/__tests__/app-desk/common.ts | 25 ++- .../e2e/__tests__/app-desk/config.spec.ts | 15 +- .../app-desk/keyboard-navigation.spec.ts | 2 +- .../e2e/__tests__/app-desk/language.spec.ts | 12 +- .../mail-domain-create-mailbox.spec.ts | 3 +- .../__tests__/app-desk/mail-domain.spec.ts | 19 +- .../__tests__/app-desk/mail-domains.spec.ts | 2 +- .../__tests__/app-desk/member-delete.spec.ts | 12 +- .../apps/e2e/__tests__/app-desk/menu.spec.ts | 2 +- 13 files changed, 394 insertions(+), 48 deletions(-) create mode 100644 docs/testsE2E.md diff --git a/docker/auth/realm.json b/docker/auth/realm.json index a1d16ea..089fe4d 100644 --- a/docker/auth/realm.json +++ b/docker/auth/realm.json @@ -99,6 +99,216 @@ } ], "realmRoles": ["user"] + }, + { + "username": "jean.team-member", + "email": "jean.team-member@example.com", + "firstName": "E2E", + "lastName": "Group Member", + "enabled": true, + "credentials": [ + { + "type": "password", + "value": "password-e2e-jean.team-member" + } + ], + "realmRoles": ["user"] + }, + { + "username": "jean.team-administrator", + "email": "jean.team-administrator@example.com", + "firstName": "E2E", + "lastName": "Group Administrator", + "enabled": true, + "credentials": [ + { + "type": "password", + "value": "password-e2e-jean.team-administrator" + } + ], + "realmRoles": ["user"] + }, + { + "username": "jean.team-owner", + "email": "jean.team-owner@example.com", + "firstName": "E2E", + "lastName": "Group Owner", + "enabled": true, + "credentials": [ + { + "type": "password", + "value": "password-e2e-jean.team-owner" + } + ], + "realmRoles": ["user"] + }, + { + "username": "jean.mail-member", + "email": "jean.mail-member@example.com", + "firstName": "E2E", + "lastName": "Mailbox Member", + "enabled": true, + "credentials": [ + { + "type": "password", + "value": "password-e2e-jean.mail-member" + } + ], + "realmRoles": ["user"] + }, + { + "username": "jean.mail-administrator", + "email": "jean.mail-administrator@example.com", + "firstName": "E2E", + "lastName": "Mailbox Administrator", + "enabled": true, + "credentials": [ + { + "type": "password", + "value": "password-e2e-jean.mail-administrator" + } + ], + "realmRoles": ["user"] + }, + { + "username": "jean.mail-owner", + "email": "jean.mail-owner@example.com", + "firstName": "E2E", + "lastName": "Mailbox Owner", + "enabled": true, + "credentials": [ + { + "type": "password", + "value": "password-e2e-jean.mail-owner" + } + ], + "realmRoles": ["user"] + }, + { + "username": "jean.team-member-mail-member", + "email": "jean.team-member-mail-member@example.com", + "firstName": "E2E", + "lastName": "Group Member Mailbox Member", + "enabled": true, + "credentials": [ + { + "type": "password", + "value": "password-e2e-jean.team-member-mail-member" + } + ], + "realmRoles": ["user"] + }, + { + "username": "jean.team-member-mail-administrator", + "email": "jean.team-member-mail-administrator@example.com", + "firstName": "E2E", + "lastName": "Group Member Mailbox Administrator", + "enabled": true, + "credentials": [ + { + "type": "password", + "value": "password-e2e-jean.team-member-mail-administrator" + } + ], + "realmRoles": ["user"] + }, + { + "username": "jean.team-member-mail-owner", + "email": "jean.team-member-mail-owner@example.com", + "firstName": "E2E", + "lastName": "Group Member Mailbox Owner", + "enabled": true, + "credentials": [ + { + "type": "password", + "value": "password-e2e-jean.team-member-mail-owner" + } + ], + "realmRoles": ["user"] + }, + { + "username": "jean.team-administrator-mail-member", + "email": "jean.team-administrator-mail-member@example.com", + "firstName": "E2E", + "lastName": "Group Administrator Mailbox Member", + "enabled": true, + "credentials": [ + { + "type": "password", + "value": "password-e2e-jean.team-administrator-mail-member" + } + ], + "realmRoles": ["user"] + }, + { + "username": "jean.team-administrator-mail-administrator", + "email": "jean.team-administrator-mail-administrator@example.com", + "firstName": "E2E", + "lastName": "Group Administrator Mailbox Administrator", + "enabled": true, + "credentials": [ + { + "type": "password", + "value": "password-e2e-jean.team-administrator-mail-administrator" + } + ], + "realmRoles": ["user"] + }, + { + "username": "jean.team-administrator-mail-owner", + "email": "jean.team-administrator-mail-owner@example.com", + "firstName": "E2E", + "lastName": "Group Administrator Mailbox Owner", + "enabled": true, + "credentials": [ + { + "type": "password", + "value": "password-e2e-jean.team-administrator-mail-owner" + } + ], + "realmRoles": ["user"] + }, + { + "username": "jean.team-owner-mail-member", + "email": "jean.team-owner-mail-member@example.com", + "firstName": "E2E", + "lastName": "Group Owner Mailbox Member", + "enabled": true, + "credentials": [ + { + "type": "password", + "value": "password-e2e-jean.team-owner-mail-member" + } + ], + "realmRoles": ["user"] + }, + { + "username": "jean.team-owner-mail-administrator", + "email": "jean.team-owner-mail-administrator@example.com", + "firstName": "E2E", + "lastName": "Group Owner Mailbox Administrator", + "enabled": true, + "credentials": [ + { + "type": "password", + "value": "password-e2e-jean.team-owner-mail-administrator" + } + ], + "realmRoles": ["user"] + }, + { + "username": "jean.team-owner-mail-owner", + "email": "jean.team-owner-mail-owner@example.com", + "firstName": "E2E", + "lastName": "Mailbox Owner", + "enabled": true, + "credentials": [ + { + "type": "password", + "value": "password-e2e-jean.team-owner-mail-owner" + } + ], + "realmRoles": ["user"] } ], "roles": { diff --git a/docs/testsE2E.md b/docs/testsE2E.md new file mode 100644 index 0000000..03d8ba2 --- /dev/null +++ b/docs/testsE2E.md @@ -0,0 +1,51 @@ +# E2E tests + +## Run E2E tests + +``` bash +# you need the dockers to be up and running +make bootstrap + +# you will need to have few accounts in the database +make demo FLUSH_ARGS='--no-input' + +# run the tests +cd src/frontend/apps/e2e +yarn test:ui --workers=1 +``` + +A new browser window will open and you will be able to run the tests. + +## Available accounts + +The `make demo` command creates the following accounts: + - `jean.team-@example.com` where `` is one of `administrator`, `member`, `owner`: + this account only belong to a team with the specified role. + - `jean.mail-@example.com` where `` is one of `administrator`, `member`, `owner`: + this account only have a mailbox with the specified role access. + - `jean.team--mail-@example.com` with a combination of roles as for the + previous accounts. + +For each account, the password is `password-e2e-`, for instance `password-e2e-jean.team-member`. + +In the E2E tests you can use these accounts to benefit from there accesses, +using the `keyCloakSignIn(page, browserName, )`. The account name is the +username without the prefix `jean.`. + +``` typescript jsx +await keyCloakSignIn(page, browserName, 'mail-owner'); +``` + +The `keyCloakSignIn` function will sign in the user on Keycloak using the proper username and password. + +.. note:: + This only works because the OIDC setting is set to fallback on user email. + +## Add a new account + +In case you need to add a new account for specific tests you need: +- to create a new user with the same format in the backend database: + update `[create_demo.py](../src/backend/demo/management/commands/create_demo.py)` +- to create a new account in Keycloak: update [realm.json](../docker/auth/realm.json) +- if the keycloak was running locally, you need to destroy its database and + restart the database and the keycloak containers. diff --git a/src/backend/demo/management/commands/create_demo.py b/src/backend/demo/management/commands/create_demo.py index 2198915..bcb4efe 100755 --- a/src/backend/demo/management/commands/create_demo.py +++ b/src/backend/demo/management/commands/create_demo.py @@ -106,7 +106,7 @@ class Timeit: return elapsed_time -def create_demo(stdout): +def create_demo(stdout): # pylint: disable=too-many-locals """ Create a database with demo data for developers to work in a realistic environment. The code is engineered to create a huge number of objects fast. @@ -190,7 +190,6 @@ def create_demo(stdout): queue.push( models.TeamAccess(team_id=team_id, user_id=user_id, role=role[0]) ) - queue.flush() with Timeit(stdout, "Creating domains"): created = set() @@ -224,6 +223,75 @@ def create_demo(stdout): role=models.RoleChoices.OWNER, ) ) + + queue.flush() + + with Timeit(stdout, "Creating specific users"): + # ⚠️ Warning: this users also need to be created in the keycloak + # realm.json AND the OIDC setting to fallback on user email + # should be set to True, because we don't pilot the sub. + for role in models.RoleChoices.values: + team_user = models.User( + sub=uuid4(), + email=f"jean.team-{role}@example.com", + name=f"Jean Group {role.capitalize()}", + password="!", + is_superuser=False, + is_active=True, + is_staff=False, + language=random.choice(settings.LANGUAGES)[0], + ) + queue.push(team_user) + queue.push( + models.TeamAccess(team_id=teams_ids[0], user_id=team_user.pk, role=role) + ) + + for role in models.RoleChoices.values: + user_with_mail = models.User( + sub=uuid4(), + email=f"jean.mail-{role}@example.com", + name=f"Jean Mail {role.capitalize()}", + password="!", + is_superuser=False, + is_active=True, + is_staff=False, + language=random.choice(settings.LANGUAGES)[0], + ) + queue.push(user_with_mail) + queue.push( + mailbox_models.MailDomainAccess( + domain_id=domains_ids[0], + user_id=user_with_mail.pk, + role=role, + ) + ) + + for team_role in models.RoleChoices.values: + for domain_role in models.RoleChoices.values: + team_mail_user = models.User( + sub=uuid4(), + email=f"jean.team-{team_role}-mail-{domain_role}@example.com", + name=f"Jean Group {team_role.capitalize()} Mail {domain_role.capitalize()}", + password="!", + is_superuser=False, + is_active=True, + is_staff=False, + language=random.choice(settings.LANGUAGES)[0], + ) + queue.push(team_mail_user) + queue.push( + models.TeamAccess( + team_id=teams_ids[0], user_id=team_mail_user.pk, role=team_role + ) + ) + queue.push( + mailbox_models.MailDomainAccess( + domain_id=domains_ids[0], + user_id=team_mail_user.pk, + role=domain_role, + ) + ) + queue.flush() diff --git a/src/backend/demo/tests/test_commands_create_demo.py b/src/backend/demo/tests/test_commands_create_demo.py index b6ec35f..f19412f 100644 --- a/src/backend/demo/tests/test_commands_create_demo.py +++ b/src/backend/demo/tests/test_commands_create_demo.py @@ -28,13 +28,22 @@ def test_commands_create_demo(): """The create_demo management command should create objects as expected.""" call_command("create_demo") - assert ( - models.User.objects.count() == TEST_NB_OBJECTS["users"] + 3 - ) # Monique Test, Jeanne Test and Jean Something (quick fix for e2e) + # Monique Test, Jeanne Test and Jean Something (quick fix for e2e) + # 3 user with team rights + # 3 user with domain rights + # 3 x 3 user with both rights + assert models.User.objects.count() == TEST_NB_OBJECTS["users"] + 3 + 3 + 3 + 9 + assert models.Team.objects.count() == TEST_NB_OBJECTS["teams"] assert models.TeamAccess.objects.count() >= TEST_NB_OBJECTS["teams"] assert mailbox_models.MailDomain.objects.count() == TEST_NB_OBJECTS["domains"] - assert mailbox_models.MailDomainAccess.objects.count() == TEST_NB_OBJECTS["domains"] + + # 3 domain access for each user with domain rights + # 3 x 3 domain access for each user with both rights + assert ( + mailbox_models.MailDomainAccess.objects.count() + == TEST_NB_OBJECTS["domains"] + 3 + 9 + ) def test_commands_createsuperuser(): diff --git a/src/frontend/apps/e2e/__tests__/app-desk/common.ts b/src/frontend/apps/e2e/__tests__/app-desk/common.ts index a5b84c7..04ed2f8 100644 --- a/src/frontend/apps/e2e/__tests__/app-desk/common.ts +++ b/src/frontend/apps/e2e/__tests__/app-desk/common.ts @@ -1,18 +1,27 @@ import { Page, expect } from '@playwright/test'; -export const keyCloakSignIn = async (page: Page, browserName: string) => { +export const keyCloakSignIn = async ( + page: Page, + browserName: string, + accountName?: string, +) => { + // Use the account name to use a specific account defined in + // the Keycloak/backend demo data creation script. const title = await page.locator('h1').first().textContent({ timeout: 5000, }); - if (title?.includes('Sign in to your account')) { - await page - .getByRole('textbox', { name: 'username' }) - .fill(`user-e2e-${browserName}`); + const username = accountName + ? `jean.${accountName}` + : `user-e2e-${browserName}`; + const password = accountName + ? `password-e2e-jean.${accountName}` + : `password-e2e-${browserName}`; - await page - .getByRole('textbox', { name: 'password' }) - .fill(`password-e2e-${browserName}`); + if (title?.includes('Sign in to your account')) { + await page.getByRole('textbox', { name: 'username' }).fill(username); + + await page.getByRole('textbox', { name: 'password' }).fill(password); await page.click('input[type="submit"]', { force: true }); } diff --git a/src/frontend/apps/e2e/__tests__/app-desk/config.spec.ts b/src/frontend/apps/e2e/__tests__/app-desk/config.spec.ts index fbe68f5..ded1924 100644 --- a/src/frontend/apps/e2e/__tests__/app-desk/config.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-desk/config.spec.ts @@ -3,12 +3,10 @@ import { expect, test } from '@playwright/test'; import { keyCloakSignIn } from './common'; test.describe('Config', () => { - test.beforeEach(async ({ page, browserName }) => { + test('it checks the config api is called', async ({ page, browserName }) => { await page.goto('/'); await keyCloakSignIn(page, browserName); - }); - test('it checks the config api is called', async ({ page }) => { const responsePromise = page.waitForResponse( (response) => response.url().includes('/config/') && response.status() === 200, @@ -35,7 +33,12 @@ test.describe('Config', () => { test('it checks that the config can deactivate the feature "teams"', async ({ page, + browserName, }) => { + await page.goto('/'); + // Login with a user who has the visibility on the groups + await keyCloakSignIn(page, browserName, 'team-member'); + await page.route('**/api/v1.0/config/', async (route) => { const request = route.request(); if (request.method().includes('GET')) { @@ -61,10 +64,6 @@ test.describe('Config', () => { }), ).toBeHidden(); - await expect( - page.getByRole('button', { - name: 'Add a mail domain', - }), - ).toBeVisible(); + await expect(page.getByText('Mail Domains')).toBeVisible(); }); }); diff --git a/src/frontend/apps/e2e/__tests__/app-desk/keyboard-navigation.spec.ts b/src/frontend/apps/e2e/__tests__/app-desk/keyboard-navigation.spec.ts index 594bf10..f38b115 100644 --- a/src/frontend/apps/e2e/__tests__/app-desk/keyboard-navigation.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-desk/keyboard-navigation.spec.ts @@ -41,7 +41,7 @@ test.describe('Keyboard navigation', () => { const page = await browser.newPage(); await page.goto('/'); - await keyCloakSignIn(page, browserName); + await keyCloakSignIn(page, browserName, 'team-owner-mail-member'); void mockApiRequests(page); diff --git a/src/frontend/apps/e2e/__tests__/app-desk/language.spec.ts b/src/frontend/apps/e2e/__tests__/app-desk/language.spec.ts index 3b82299..a10ff1f 100644 --- a/src/frontend/apps/e2e/__tests__/app-desk/language.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-desk/language.spec.ts @@ -10,22 +10,14 @@ test.describe('Language', () => { }); test('checks the language picker', async ({ page }) => { - await expect( - page.getByRole('button', { - name: 'Create a new team', - }), - ).toBeVisible(); + await expect(page.getByText('Groups')).toBeVisible(); const header = page.locator('header').first(); await header.getByRole('combobox').getByText('EN').click(); await header.getByRole('option', { name: 'FR' }).click(); await expect(header.getByRole('combobox').getByText('FR')).toBeVisible(); - await expect( - page.getByRole('button', { - name: 'Créer un nouveau groupe', - }), - ).toBeVisible(); + await expect(page.getByText('Groupes')).toBeVisible(); }); test('checks lang attribute of html tag updates when user changes language', async ({ diff --git a/src/frontend/apps/e2e/__tests__/app-desk/mail-domain-create-mailbox.spec.ts b/src/frontend/apps/e2e/__tests__/app-desk/mail-domain-create-mailbox.spec.ts index 5b02875..90fccc6 100644 --- a/src/frontend/apps/e2e/__tests__/app-desk/mail-domain-create-mailbox.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-desk/mail-domain-create-mailbox.spec.ts @@ -130,7 +130,8 @@ const navigateToMailboxCreationFormForMailDomainFr = async ( test.describe('Mail domain create mailbox', () => { test.beforeEach(async ({ page, browserName }) => { await page.goto('/'); - await keyCloakSignIn(page, browserName); + // Login with a user who has the visibility on the mail domains + await keyCloakSignIn(page, browserName, 'mail-member'); }); test('checks user can create a mailbox when he has post ability', async ({ diff --git a/src/frontend/apps/e2e/__tests__/app-desk/mail-domain.spec.ts b/src/frontend/apps/e2e/__tests__/app-desk/mail-domain.spec.ts index e85eebd..99f349f 100644 --- a/src/frontend/apps/e2e/__tests__/app-desk/mail-domain.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-desk/mail-domain.spec.ts @@ -113,14 +113,13 @@ const assertFilledMailboxesTableElementsAreVisible = async ( }; test.describe('Mail domain', () => { - test.beforeEach(async ({ page, browserName }) => { - await page.goto('/'); - await keyCloakSignIn(page, browserName); - }); - test('redirects to 404 page when the mail domain requested does not exist', async ({ page, + browserName, }) => { + await page.goto('/'); + await keyCloakSignIn(page, browserName); + await page.route('**/api/v1.0/mail-domains/?page=*', async (route) => { await route.fulfill({ json: { @@ -143,6 +142,11 @@ test.describe('Mail domain', () => { }); test.describe('user is administrator or owner', () => { + test.beforeEach(async ({ page, browserName }) => { + await page.goto('/'); + await keyCloakSignIn(page, browserName, 'mail-owner'); + }); + test.describe('mail domain is enabled', () => { const mailDomainsFixtures: MailDomain[] = [ { @@ -479,6 +483,11 @@ test.describe('Mail domain', () => { }); test.describe('user is member', () => { + test.beforeEach(async ({ page, browserName }) => { + await page.goto('/'); + await keyCloakSignIn(page, browserName, 'mail-member'); + }); + test.describe('mail domain is enabled', () => { const mailDomainsFixtures: MailDomain[] = [ { diff --git a/src/frontend/apps/e2e/__tests__/app-desk/mail-domains.spec.ts b/src/frontend/apps/e2e/__tests__/app-desk/mail-domains.spec.ts index f40f8dc..66919fa 100644 --- a/src/frontend/apps/e2e/__tests__/app-desk/mail-domains.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-desk/mail-domains.spec.ts @@ -75,7 +75,7 @@ test.describe('Mail domains', () => { test.describe('checks all the elements are visible', () => { test.beforeEach(async ({ page, browserName }) => { await page.goto('/'); - await keyCloakSignIn(page, browserName); + await keyCloakSignIn(page, browserName, 'mail-member'); }); test('checks the sort button', async ({ page }) => { diff --git a/src/frontend/apps/e2e/__tests__/app-desk/member-delete.spec.ts b/src/frontend/apps/e2e/__tests__/app-desk/member-delete.spec.ts index 8d6a563..c4cf046 100644 --- a/src/frontend/apps/e2e/__tests__/app-desk/member-delete.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-desk/member-delete.spec.ts @@ -4,7 +4,7 @@ import { addNewMember, createTeam, keyCloakSignIn } from './common'; test.beforeEach(async ({ page, browserName }) => { await page.goto('/'); - await keyCloakSignIn(page, browserName); + await keyCloakSignIn(page, browserName, 'team-member'); }); test.describe('Members Delete', () => { @@ -17,9 +17,7 @@ test.describe('Members Delete', () => { const table = page.getByLabel('List members card').getByRole('table'); const cells = table.getByRole('row').nth(1).getByRole('cell'); - await expect(cells.nth(1)).toHaveText( - new RegExp(`E2E ${browserName}`, 'i'), - ); + await expect(cells.nth(1)).toHaveText('Jean Group Member'); await cells.nth(4).getByLabel('Member options').click(); await page.getByLabel('Open the modal to delete this member').click(); @@ -46,7 +44,7 @@ test.describe('Members Delete', () => { // find row where regexp match the name const cells = table .getByRole('row') - .filter({ hasText: new RegExp(`E2E ${browserName}`, 'i') }) + .filter({ hasText: 'E2E Group Member' }) .getByRole('cell'); await cells.nth(4).getByLabel('Member options').click(); await page.getByLabel('Open the modal to delete this member').click(); @@ -118,7 +116,7 @@ test.describe('Members Delete', () => { // find row where regexp match the name const myCells = table .getByRole('row') - .filter({ hasText: new RegExp(`E2E ${browserName}`, 'i') }) + .filter({ hasText: 'E2E Group Member' }) .getByRole('cell'); await myCells.nth(4).getByLabel('Member options').click(); @@ -148,7 +146,7 @@ test.describe('Members Delete', () => { // find row where regexp match the name const myCells = table .getByRole('row') - .filter({ hasText: new RegExp(`E2E ${browserName}`, 'i') }) + .filter({ hasText: 'E2E Group Member' }) .getByRole('cell'); await myCells.nth(4).getByLabel('Member options').click(); diff --git a/src/frontend/apps/e2e/__tests__/app-desk/menu.spec.ts b/src/frontend/apps/e2e/__tests__/app-desk/menu.spec.ts index ba4aacf..f9ee991 100644 --- a/src/frontend/apps/e2e/__tests__/app-desk/menu.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-desk/menu.spec.ts @@ -4,7 +4,7 @@ import { createTeam, keyCloakSignIn } from './common'; test.beforeEach(async ({ page, browserName }) => { await page.goto('/'); - await keyCloakSignIn(page, browserName); + await keyCloakSignIn(page, browserName, 'team-owner-mail-member'); }); test.describe('Menu', () => {