0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-16 20:26:19 -05:00

chore(test): add M2M RBAC console test (#4598)

* chore(test): add m2m rbac console test

* fix: fix m2m rbac console ui test
This commit is contained in:
Darcy Ye 2023-09-28 16:53:21 +08:00 committed by GitHub
parent a8b5a020fd
commit c11fafd72c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 381 additions and 9 deletions

View file

@ -25,7 +25,7 @@ export const expectFrameworksInGroup = async (page: Page, groupSelector: string)
/* eslint-enable no-await-in-loop */
};
export const expectToClickFramework = async (page: Page, framework: string) => {
export const expectToChooseAndClickApplicationFramework = async (page: Page, framework: string) => {
const frameworkCard = await expect(page).toMatchElement(
'div[class*=card]:has(div[class$=header] div[class$=name])',
{
@ -42,9 +42,9 @@ export const expectFrameworkExists = async (page: Page, framework: string) => {
});
};
export const expectToProceedCreationFrom = async (
export const expectToProceedApplicationCreationFrom = async (
page: Page,
{ name, description }: ApplicationCase
{ name, description }: { name: string; description: string }
) => {
// Expect the creation form to be open
await expectModalWithTitle(page, 'Create Application');

View file

@ -20,8 +20,8 @@ import {
} from './constants.js';
import {
expectFrameworkExists,
expectToClickFramework,
expectToProceedCreationFrom,
expectToChooseAndClickApplicationFramework,
expectToProceedApplicationCreationFrom,
expectToProceedSdkGuide,
expectToProceedAppDeletion,
expectFrameworksInGroup,
@ -54,9 +54,9 @@ describe('applications', () => {
});
it('create the initial application from the table placeholder', async () => {
await expectToClickFramework(page, initialApp.framework);
await expectToChooseAndClickApplicationFramework(page, initialApp.framework);
await expectToProceedCreationFrom(page, initialApp);
await expectToProceedApplicationCreationFrom(page, initialApp);
await expectToProceedSdkGuide(page, initialApp, true);
@ -126,9 +126,9 @@ describe('applications', () => {
// Expect the framework exists after filtering
await expectFrameworkExists(page, testApp.framework);
await expectToClickFramework(page, testApp.framework);
await expectToChooseAndClickApplicationFramework(page, testApp.framework);
await expectToProceedCreationFrom(page, testApp);
await expectToProceedApplicationCreationFrom(page, testApp);
await expectToProceedSdkGuide(page, testApp);

View file

@ -0,0 +1,306 @@
import { logtoConsoleUrl as logtoConsoleUrlString } from '#src/constants.js';
import {
expectConfirmModalAndAct,
expectModalWithTitle,
expectToClickModalAction,
goToAdminConsole,
waitForToast,
} from '#src/ui-helpers/index.js';
import {
expectNavigation,
appendPathname,
generateResourceName,
generateResourceIndicator,
generateScopeName,
generateRoleName,
} from '#src/utils.js';
import {
expectToChooseAndClickApplicationFramework,
expectToProceedApplicationCreationFrom,
} from '../applications/helpers.js';
import { createM2mRoleAndAssignPermissions } from './utils.js';
await page.setViewport({ width: 1920, height: 1080 });
describe('M2M RBAC', () => {
const logtoConsoleUrl = new URL(logtoConsoleUrlString);
const managementApiResourceName = 'Logto Management API';
const managementApiPermission = 'all';
const apiResourceName = generateResourceName();
const apiResourceIndicator = generateResourceIndicator();
const permissionName = generateScopeName();
const permissionDescription = 'Dummy permission description';
const roleName = generateRoleName();
const roleDescription = 'Dummy role description';
const rbacTestAppname = 'm2m-app-001';
const m2mFramework = 'Machine-to-machine';
beforeAll(async () => {
await goToAdminConsole();
});
describe('create api resource and permissions', () => {
it('navigate to api resources page', async () => {
await expectNavigation(
page.goto(appendPathname('/console/api-resources', logtoConsoleUrl).href)
);
await expect(page).toMatchElement(
'div[class$=main] div[class$=headline] div[class$=titleEllipsis]',
{
text: 'API Resources',
}
);
});
it('create an api resource', async () => {
await expect(page).toClick('div[class$=headline] button span', {
text: 'Create API Resource',
});
await expectModalWithTitle(page, 'Start with tutorials');
// Click bottom button to skip tutorials
await expect(page).toClick('.ReactModalPortal nav[class$=actionBar] button span', {
text: 'Continue without tutorial',
});
await expectModalWithTitle(page, 'Create API Resource');
await expect(page).toFillForm('.ReactModalPortal form', {
name: apiResourceName,
indicator: apiResourceIndicator,
});
await expectToClickModalAction(page, 'Create API Resource');
await waitForToast(page, {
text: `The API resource ${apiResourceName} has been successfully created`,
});
await expect(page).toMatchElement('div[class$=header] div[class$=info] div[class$=name]', {
text: apiResourceName,
});
});
it('create permission for api resource', async () => {
await expect(page).toClick('nav div[class$=item] div[class$=link] a', {
text: 'Permissions',
});
await expect(page).toClick('div[class$=filter] button[class$=createButton] span', {
text: 'Create Permission',
});
await expectModalWithTitle(page, 'Create permission');
await expect(page).toFillForm('.ReactModalPortal form', {
name: permissionName,
description: permissionDescription,
});
await expectToClickModalAction(page, 'Create permission');
await waitForToast(page, {
text: `The permission ${permissionName} has been successfully created`,
});
await expect(page).toMatchElement('table tbody tr td div[class$=name]', {
text: permissionName,
});
});
});
describe('create m2m app', () => {
it('navigate to applications page', async () => {
await expectNavigation(
page.goto(appendPathname('/console/applications', logtoConsoleUrl).href)
);
await expect(page).toMatchElement(
'div[class$=main] div[class$=headline] div[class$=titleEllipsis]',
{
text: 'Applications',
}
);
});
it('create a m2m app for rbac testing', async () => {
await expectToChooseAndClickApplicationFramework(page, m2mFramework);
await expectToProceedApplicationCreationFrom(page, {
name: rbacTestAppname,
description: rbacTestAppname,
});
// Skip guide
await page.keyboard.press('Escape');
});
});
describe('create m2m role', () => {
it('navigate to roles page', async () => {
await expectNavigation(page.goto(appendPathname('/console/roles', logtoConsoleUrl).href));
await expect(page).toMatchElement(
'div[class$=main] div[class$=headline] div[class$=titleEllipsis]',
{
text: 'Roles',
}
);
});
it('create a m2m role and assign permissions to the role', async () => {
await createM2mRoleAndAssignPermissions(page, { roleName, roleDescription }, [
{ apiResourceName, permissionName },
{ apiResourceName: managementApiResourceName, permissionName: managementApiPermission },
]);
});
it('delete a permission from a role on the role details page', async () => {
await expect(page).toClick('nav div[class$=item] div[class$=link] a', {
text: 'Permissions',
});
const permissionRow = await expect(page).toMatchElement(
'table tbody tr:has(td div[class$=name])',
{ text: permissionName }
);
await expect(permissionRow).toClick('td[class$=deleteColumn] button');
await expectConfirmModalAndAct(page, {
title: 'Reminder',
actionText: 'Remove',
});
await waitForToast(page, {
text: `The permission "${permissionName}" was successfully removed from this role`,
});
});
it('assign a permission to a role on the role details page', async () => {
await expect(page).toClick('div[class$=filter] button span', {
text: 'Assign Permissions',
});
await expectModalWithTitle(page, 'Assign permissions');
await expect(page).toClick(
'.ReactModalPortal div[class$=resourceItem] div[class$=title] div[class$=name]',
{
text: apiResourceName,
}
);
await expect(page).toClick(
'.ReactModalPortal div[class$=resourceItem] div[class$=sourceScopeItem] div[role=button]',
{
text: permissionName,
}
);
await expectToClickModalAction(page, 'Assign Permissions');
await waitForToast(page, {
text: 'The selected permissions were successfully assigned to this role',
});
await expect(page).toMatchElement('table tbody tr:has(td div[class$=name])', {
text: permissionName,
});
});
});
describe('assign a role to a m2m app (on role details page)', () => {
it('assign a role to a m2m app on the role details page', async () => {
await expect(page).toClick('nav div[class$=item] div[class$=link] a', {
text: 'Machine-to-machine apps',
});
await expect(page).toClick('div[class$=filter] button span', {
text: 'Assign applications',
});
await expectModalWithTitle(page, 'Assign apps');
await expect(page).toClick(
'.ReactModalPortal div[class$=rolesTransfer] div[class$=item] div[class$=title]',
{
text: rbacTestAppname,
}
);
await expectToClickModalAction(page, 'Assign applications');
await waitForToast(page, {
text: 'The selected applications were successfully assigned to this role',
});
await expect(page).toMatchElement(
'div[class$=applicationsTable] td div[class$=item] a[class$=title]',
{
text: rbacTestAppname,
}
);
});
});
describe('assign/remove a role to/from a m2m app (on m2m app details page)', () => {
it('remove a role form a m2m app on the app details page', async () => {
// Navigate to app details page
await expect(page).toClick('table tbody tr td a[class$=title]', {
text: rbacTestAppname,
});
await expect(page).toMatchElement(
'div[class$=header] > div[class$=metadata] div[class$=name]',
{
text: rbacTestAppname,
}
);
// Go to roles tab
await expect(page).toClick('nav div[class$=item] div[class$=link] a', {
text: 'Roles',
});
const roleRow = await expect(page).toMatchElement('table tbody tr:has(td a[class$=title])', {
text: roleName,
});
// Click remove button
await expect(roleRow).toClick('td:last-of-type button');
await expectConfirmModalAndAct(page, {
title: 'Reminder',
actionText: 'Remove',
});
await waitForToast(page, {
text: `${roleName} was successfully removed from this user.`,
});
});
it('add a role to m2m app on the application details page', async () => {
await expect(page).toClick('div[class$=filter] button span', {
text: 'Assign roles',
});
await expectModalWithTitle(page, `Assign roles to ${rbacTestAppname}`);
await expect(page).toClick(
'.ReactModalPortal div[class$=rolesTransfer] div[class$=item] div[class$=name]',
{
text: roleName,
}
);
await expectToClickModalAction(page, 'Assign roles');
await waitForToast(page, {
text: 'Successfully assigned role(s)',
});
});
});
});

View file

@ -0,0 +1,66 @@
import { type Page } from 'puppeteer';
import {
expectModalWithTitle,
expectToClickModalAction,
waitForToast,
} from '#src/ui-helpers/index.js';
export const createM2mRoleAndAssignPermissions = async (
page: Page,
{ roleName, roleDescription }: { roleName: string; roleDescription: string },
apiResources: Array<{ apiResourceName: string; permissionName: string }>
) => {
await expect(page).toClick('div[class$=headline] button span', {
text: 'Create Role',
});
await expectModalWithTitle(page, 'Create Role');
// Expand role type selection
await expect(page).toClick('button[class$=roleTypeSelectionSwitch] span', {
text: 'Show more options',
});
await expect(page).toClick('div[class*=radioGroup][class$=roleTypes] div[class$=content]', {
text: 'Machine-to-machine app role',
});
await expect(page).toFillForm('.ReactModalPortal form', {
name: roleName,
description: roleDescription,
});
/* eslint-disable no-await-in-loop */
for (const apiResource of apiResources) {
const { apiResourceName, permissionName } = apiResource;
// Assign customized permission
await expect(page).toClick(
'.ReactModalPortal div[class$=resourceItem] div[class$=title] div[class$=name]',
{
text: apiResourceName,
}
);
await expect(page).toClick(
'.ReactModalPortal div[class$=resourceItem] div[class$=sourceScopeItem] div[role=button]',
{
text: permissionName,
}
);
}
/* eslint-enable no-await-in-loop */
await expectToClickModalAction(page, 'Create Role');
await waitForToast(page, {
text: `The role ${roleName} has been successfully created.`,
});
await expectModalWithTitle(page, 'Assign apps');
await expectToClickModalAction(page, 'Skip for now');
await expect(page).toMatchElement('div[class$=header] div[class$=info] div[class$=name]', {
text: roleName,
});
};