fb7bf6d01e
refs https://github.com/TryGhost/Product/issues/3832 --- <!-- Leave the line below if you'd like GitHub Copilot to generate a summary from your commit --> <!-- copilot:summary --> ### <samp>🤖 Generated by Copilot at 7eda74c</samp> This pull request improves the validation, customization, and feedback of various form components and modals in the admin-x-settings app. It also adds new components for user detail modal sections and modifies the user type to allow null values for social accounts. Additionally, it adds `dirty` props to some integration modals and a `data-testid` attribute to the exit settings button. It also deletes an unused file.
185 lines
7.7 KiB
TypeScript
185 lines
7.7 KiB
TypeScript
import {expect, test} from '@playwright/test';
|
|
import {globalDataRequests, limitRequests, mockApi, responseFixtures} from '../../../utils/acceptance';
|
|
|
|
test.describe('User invitations', async () => {
|
|
test('Supports inviting a user', async ({page}) => {
|
|
const futureDate = new Date();
|
|
futureDate.setDate(futureDate.getDate() + 1);
|
|
|
|
const {lastApiRequests} = await mockApi({page, requests: {
|
|
...globalDataRequests,
|
|
browseUsers: {method: 'GET', path: '/users/?limit=100&include=roles', response: responseFixtures.users},
|
|
browseInvites: {method: 'GET', path: '/invites/', response: responseFixtures.invites},
|
|
browseRoles: {method: 'GET', path: '/roles/?limit=all', response: responseFixtures.roles},
|
|
browseAssignableRoles: {method: 'GET', path: '/roles/?limit=all&permissions=assign', response: responseFixtures.roles},
|
|
addInvite: {method: 'POST', path: '/invites/', response: {
|
|
invites: [
|
|
{
|
|
id: 'new-invite-id',
|
|
role_id: '645453f3d254799990dd0e18',
|
|
status: 'sent',
|
|
email: 'newuser@test.com',
|
|
expires: futureDate.getTime(),
|
|
created_at: new Date().toISOString(),
|
|
updated_at: new Date().toISOString()
|
|
}
|
|
]
|
|
}}
|
|
}});
|
|
|
|
await page.goto('/');
|
|
|
|
const section = page.getByTestId('users');
|
|
|
|
await section.getByRole('button', {name: 'Invite people'}).click();
|
|
|
|
const modal = page.getByTestId('invite-user-modal');
|
|
|
|
// Validation failures
|
|
|
|
await modal.getByRole('button', {name: 'Send invitation now'}).click();
|
|
await expect(modal).toContainText('Please enter a valid email address');
|
|
|
|
// Reset error with keydown event
|
|
await modal.getByLabel('Email address').focus();
|
|
await page.keyboard.press('Backspace');
|
|
|
|
await modal.getByLabel('Email address').fill('test');
|
|
await expect(modal).not.toContainText('Please enter a valid email address');
|
|
await modal.getByRole('button', {name: 'Send invitation now'}).click();
|
|
await expect(modal).toContainText('Please enter a valid email address');
|
|
|
|
await modal.getByLabel('Email address').fill('author@test.com');
|
|
await modal.getByRole('button', {name: 'Send invitation now'}).click();
|
|
await expect(modal).toContainText('A user with that email address already exists.');
|
|
|
|
await modal.getByLabel('Email address').fill('invitee@test.com');
|
|
await modal.getByRole('button', {name: 'Send invitation now'}).click();
|
|
await expect(modal).toContainText('A user with that email address was already invited.');
|
|
|
|
// Successful invitation
|
|
|
|
await modal.getByLabel('Email address').fill('newuser@test.com');
|
|
await modal.locator('input[value=author]').check();
|
|
await modal.getByRole('button', {name: 'Send invitation now'}).click();
|
|
|
|
await expect(page.getByTestId('toast-success')).toHaveText(/Invitation successfully sent to newuser@test\.com/);
|
|
|
|
await section.getByRole('tab', {name: 'Invited'}).click();
|
|
|
|
const listItem = section.getByTestId('user-invite').last();
|
|
|
|
await expect(listItem.getByText('newuser@test.com')).toBeVisible();
|
|
await expect(listItem.getByText('Author')).toBeVisible();
|
|
|
|
expect(lastApiRequests.addInvite?.body).toEqual({
|
|
invites: [{
|
|
email: 'newuser@test.com',
|
|
expires: null,
|
|
role_id: '645453f3d254799990dd0e18',
|
|
status: null,
|
|
token: null
|
|
}]
|
|
});
|
|
});
|
|
|
|
test('Supports resending invitations', async ({page}) => {
|
|
const {lastApiRequests} = await mockApi({page, requests: {
|
|
...globalDataRequests,
|
|
browseUsers: {method: 'GET', path: '/users/?limit=100&include=roles', response: responseFixtures.users},
|
|
browseInvites: {method: 'GET', path: '/invites/', response: responseFixtures.invites},
|
|
deleteInvite: {method: 'DELETE', path: `/invites/${responseFixtures.invites.invites[0].id}/`, response: {}},
|
|
addInvite: {method: 'POST', path: '/invites/', response: responseFixtures.invites}
|
|
}});
|
|
|
|
await page.goto('/');
|
|
|
|
const section = page.getByTestId('users');
|
|
await section.getByRole('tab', {name: 'Invited'}).click();
|
|
|
|
const listItem = section.getByTestId('user-invite');
|
|
await listItem.hover();
|
|
|
|
await listItem.getByRole('button', {name: 'Resend'}).click();
|
|
|
|
await expect(page.getByTestId('toast-success')).toHaveText(/Invitation resent! \(invitee@test\.com\)/);
|
|
|
|
// Resending works by deleting and re-adding the invite
|
|
|
|
expect(lastApiRequests.deleteInvite?.url).toMatch(new RegExp(`/invites/${responseFixtures.invites.invites[0].id}`));
|
|
|
|
expect(lastApiRequests.addInvite?.body).toEqual({
|
|
invites: [{
|
|
email: 'invitee@test.com',
|
|
expires: null,
|
|
role_id: '645453f3d254799990dd0e18',
|
|
status: null,
|
|
token: null
|
|
}]
|
|
});
|
|
});
|
|
|
|
test('Supports revoking invitations', async ({page}) => {
|
|
const {lastApiRequests} = await mockApi({page, requests: {
|
|
...globalDataRequests,
|
|
browseUsers: {method: 'GET', path: '/users/?limit=100&include=roles', response: responseFixtures.users},
|
|
browseInvites: {method: 'GET', path: '/invites/', response: responseFixtures.invites},
|
|
deleteInvite: {method: 'DELETE', path: `/invites/${responseFixtures.invites.invites[0].id}/`, response: {}}
|
|
}});
|
|
|
|
await page.goto('/');
|
|
|
|
const section = page.getByTestId('users');
|
|
await section.getByRole('tab', {name: 'Invited'}).click();
|
|
|
|
const listItem = section.getByTestId('user-invite');
|
|
await listItem.hover();
|
|
|
|
await listItem.getByRole('button', {name: 'Revoke'}).click();
|
|
|
|
await expect(page.getByTestId('toast-success')).toHaveText(/Invitation revoked \(invitee@test\.com\)/);
|
|
|
|
expect(lastApiRequests.deleteInvite?.url).toMatch(new RegExp(`/invites/${responseFixtures.invites.invites[0].id}`));
|
|
});
|
|
|
|
test('Limits inviting too many staff users', async ({page}) => {
|
|
await mockApi({page, requests: {
|
|
...globalDataRequests,
|
|
...limitRequests,
|
|
browseAssignableRoles: {method: 'GET', path: '/roles/?limit=all&permissions=assign', response: responseFixtures.roles},
|
|
browseConfig: {
|
|
...globalDataRequests.browseConfig,
|
|
response: {
|
|
config: {
|
|
...responseFixtures.config.config,
|
|
hostSettings: {
|
|
limits: {
|
|
staff: {
|
|
max: 1,
|
|
error: 'Your plan does not support more staff'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}});
|
|
|
|
await page.goto('/');
|
|
|
|
const section = page.getByTestId('users');
|
|
|
|
await section.getByRole('button', {name: 'Invite people'}).click();
|
|
|
|
const modal = page.getByTestId('invite-user-modal');
|
|
|
|
await modal.locator('input[value=author]').check();
|
|
|
|
await expect(modal).toHaveText(/Your plan does not support more staff/);
|
|
|
|
await modal.locator('input[value=contributor]').check();
|
|
|
|
await expect(modal).not.toHaveText(/Your plan does not support more staff/);
|
|
});
|
|
});
|