Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
239 changes: 239 additions & 0 deletions cms-ui/apps/admin-ui/e2e/api-tokens.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
import {
createClient,
EntityImporter,
findNotification,
findTableAction,
GroupImportData,
IMPORT_ID,
IMPORT_TYPE,
IMPORT_TYPE_GROUP,
IMPORT_TYPE_USER,
ImportPermissions,
loginWithForm,
matchRequest,
navigateToApp,
openContext,
TestSize,
UserImportData,
} from '@gentics/e2e-utils';
import { expect, Page, test } from '@playwright/test';
import { cloneWithSymbols } from '@gentics/common';
import { AccessControlledType, GcmsPermission, LoginResponse } from '@gentics/cms-models';

const API_MODAL = 'gtx-api-tokens-modal';
const API_CREATE_MODAL = 'gtx-api-tokens-create-modal';
const BASE_GROUP: GroupImportData = {
[IMPORT_TYPE]: IMPORT_TYPE_GROUP,
[IMPORT_ID]: 'apiTokenMaintenance_group_base',
name: 'apiTokenMaintenance_base',
description: 'Api-Token-Maintenance: Base',
permissions: [],
};

const TEST_USER: UserImportData = {
[IMPORT_TYPE]: IMPORT_TYPE_USER,
[IMPORT_ID]: 'apiTokenMaintenance_user_test',

email: 'test@example.com',
firstName: 'Content-Maintenance',
lastName: 'Test',
group: BASE_GROUP,

login: 'apiTokenMaintenance_test',
password: 'foobar2026',
};

test.describe('Api Tokens', () => {

const IMPORTER = new EntityImporter();
let login: LoginResponse;

test.beforeAll(async ({ request }) => {
await test.step('Client Setup', async () => {
IMPORTER.setApiContext(request);
await IMPORTER.clearClient();
});

await test.step('Test Bootstrapping', async () => {
await IMPORTER.cleanupTest();
await IMPORTER.bootstrapSuite(TestSize.MINIMAL);
});
});

test.beforeEach(async ({ page, request, context }) => {
await test.step('Client Setup', async () => {
IMPORTER.setApiContext(request);
await context.clearCookies();
await IMPORTER.clearClient();
});

await test.step('Common Test Setup', async () => {
await IMPORTER.cleanupTest();
await IMPORTER.syncPackages(TestSize.MINIMAL);
await IMPORTER.setupTest(TestSize.MINIMAL);
});

await navigateToApp(page);

await setupWithPermissions(IMPORTER, login, page, [
{
type: AccessControlledType.ADMIN,
perms: [
{ type: GcmsPermission.SET_PERMISSION, value: true },
],
},
]);
});

test('should show api modal', async ({ page }) => {
await openApiTokenModal(page);

const table = page.locator(API_MODAL).locator('gtx-manage-api-tokens-table');

await expect(table).toBeVisible();
});

test('should show api creation modal', async ({ page }) => {
await openApiTokenModal(page);

await openApiTokenCreateModal(page);

await expect(page.locator(API_CREATE_MODAL)).toBeVisible();
});

test('can save when form is valid', async ({ page }) => {
await openApiTokenModal(page);

await openApiTokenCreateModal(page);

const form = page.locator(API_CREATE_MODAL).locator('form');

await expect(form).toBeVisible();

const submitBtn = page.locator(API_CREATE_MODAL).locator('.modal-footer').locator('[data-action="confirm"]');

await expect(submitBtn.locator('button')).toBeDisabled();

// fill only name, keep date empty
const nameInput = form.locator('input[type="text"]');
await nameInput.fill('Test');

await expect(submitBtn.locator('button')).toBeEnabled();

const dateInput = form.locator('input[type="date"]');

// fill with date from yesterday
const yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1);

await dateInput.fill(yesterday.toISOString().split('T')[0]);

await expect(submitBtn.locator('button')).toBeDisabled();

// fill with date from tomorrow
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);

await dateInput.fill(tomorrow.toISOString().split('T')[0]);

await expect(submitBtn.locator('button')).toBeEnabled();

await submitBtn.click();

await expect(page.locator('.success-message')).toBeVisible();
await expect(findNotification(page, 'api-token-create-success')).toBeVisible();
await expect(page.locator('.success-message').locator('.content')).toHaveText(/\S+/);
});

test('can delete Api Tokens', async ({ page }) => {
await addEntries(login, page, 1);
await openApiTokenModal(page);

const table = page.locator(API_MODAL).locator('gtx-manage-api-tokens-table');

await expect(table).toBeVisible();

const row = table.locator('.data-row').first();

const deleteButton = findTableAction(row, 'delete');

await deleteButton.click();

await expect(page.locator('[data-action="api-token-delete"]')).toBeVisible();

await page.locator('[data-action="api-token-delete"]').click();

await expect(findNotification(page, 'api-token-delete-success')).toBeVisible();

await expect(row).not.toBeAttached();
});

test('can delete multiple Api Tokens', async ({ page }) => {
await addEntries(login, page, 2);
await openApiTokenModal(page);

const table = page.locator(API_MODAL).locator('gtx-manage-api-tokens-table');

await expect(table).toBeVisible();

const row = table.locator('.header-row');

await row.locator('gtx-checkbox').click();

await findTableAction(row, 'delete').click();

await expect(page.locator('[data-action="api-token-delete"]')).toBeVisible();

await page.locator('[data-action="api-token-delete"]').click();

await expect(findNotification(page, 'api-token-delete-success')).toBeVisible();

await expect(table.locator('.data-row')).toHaveCount(0);
});

});

async function openApiTokenModal(page: Page) {
await page.locator('gtx-user-menu .toggle-button').click();
const menu = page.locator('gtx-user-menu');
const dropdown = await openContext(menu.locator('.user-name gtx-dropdown-list'));
await dropdown.locator('[data-action="manage-api-tokens"]').click();
}

async function openApiTokenCreateModal(page: Page) {
const modalTable = page.locator(API_MODAL).locator('gtx-manage-api-tokens-table');
const createNewBtn = modalTable.locator('[data-action="create"]');
await createNewBtn.click();
}

async function addEntries(login: LoginResponse, page: Page, amount: number) {
const client = await createClient({
context: page.context().request,
isPageContext: true,
});

client.sid = login?.sid ?? null;

for (let i = 0; i < amount; i++) {
await client.admin.addApiTokens({ name: `token ${i + 1}` }).send();
}
}

async function setupWithPermissions(importer: EntityImporter, login: LoginResponse, page: Page, permissions: ImportPermissions[]): Promise<void> {
await test.step('Test User Setup', async () => {
const TEST_GROUP = cloneWithSymbols(BASE_GROUP);
TEST_GROUP.permissions = permissions;

await importer.importData([
TEST_GROUP,
TEST_USER,
]);
});

await test.step('Open ADMIN-UI', async () => {
await navigateToApp(page);
const loginReq = page.waitForResponse(matchRequest('POST', '/rest/auth/login'));
await loginWithForm(page, TEST_USER);
login = await (await loginReq).json();
});
}
2 changes: 1 addition & 1 deletion cms-ui/apps/admin-ui/public/i18n/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -753,7 +753,7 @@
"change_password_label": "Passwort ändern",
"log_in": "Anmelden",
"log_out": "Abmelden",
"logged_in_as": "'Angemeldet als <strong>{{name}}</strong>'",
"logged_in_as": "'Angemeldet als ",
"password": "Passwort",
"user_name": "Benutzername",
"confirm_delete": "Nochmals klicken um zu löschen",
Expand Down
2 changes: 1 addition & 1 deletion cms-ui/apps/admin-ui/public/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -753,7 +753,7 @@
"change_password_label": "Change password",
"log_in": "Log In",
"log_out": "Log out",
"logged_in_as": "'Logged in as <strong>{{name}}</strong>'",
"logged_in_as": "'Logged in as ",
"password": "Password",
"user_name": "User Name",
"confirm_delete": "Click again to confirm delete",
Expand Down
1 change: 1 addition & 0 deletions cms-ui/apps/admin-ui/src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
(toggle)="userMenuOpened = $event"
(setLanguage)="setLanguageConfirmation($event)"
(showPasswordModal)="onShowPasswordModal()"

(logout)="onLogoutClick()"
>
<gtx-tabs
Expand Down
Loading