Add data testing attributes and adapt tests for team account invitations
This commit adds data testing attributes to key elements in the team account invitations features. It also modifies e2e tests to make use of these attributes. Additionally, it introduces minor tweaks to other parts of the system to better facilitate testing, such as adjustments in timeouts and update of some log messages.
This commit is contained in:
@@ -188,7 +188,9 @@ export function AccountSelector({
|
||||
>
|
||||
{(accounts ?? []).map((account) => (
|
||||
<CommandItem
|
||||
data-test={'account-selector-team-' + account.value}
|
||||
data-test={'account-selector-team'}
|
||||
data-name={account.label}
|
||||
data-slug={account.value}
|
||||
className={'group'}
|
||||
key={account.value}
|
||||
value={account.value ?? ''}
|
||||
|
||||
@@ -60,7 +60,11 @@ export function AcceptInvitationContainer(props: {
|
||||
</div>
|
||||
|
||||
<div className={'flex flex-col space-y-2.5'}>
|
||||
<form className={'w-full'} action={acceptInvitationAction}>
|
||||
<form
|
||||
data-test={'join-team-form'}
|
||||
className={'w-full'}
|
||||
action={acceptInvitationAction}
|
||||
>
|
||||
<input type="hidden" name={'inviteToken'} value={props.inviteToken} />
|
||||
|
||||
<input
|
||||
|
||||
@@ -65,7 +65,11 @@ export function AccountInvitationsTable({
|
||||
placeholder={t(`searchInvitations`)}
|
||||
/>
|
||||
|
||||
<DataTable columns={columns} data={filteredInvitations} />
|
||||
<DataTable
|
||||
data-cy={'invitations-table'}
|
||||
columns={columns}
|
||||
data={filteredInvitations}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -87,7 +91,10 @@ function useGetColumns(permissions: {
|
||||
const email = member.email;
|
||||
|
||||
return (
|
||||
<span className={'flex items-center space-x-4 text-left'}>
|
||||
<span
|
||||
data-test={'invitation-email'}
|
||||
className={'flex items-center space-x-4 text-left'}
|
||||
>
|
||||
<span>
|
||||
<ProfileAvatar text={email} />
|
||||
</span>
|
||||
@@ -166,19 +173,28 @@ function ActionsDropdown({
|
||||
|
||||
<DropdownMenuContent>
|
||||
<If condition={permissions.canUpdateInvitation}>
|
||||
<DropdownMenuItem onClick={() => setIsUpdatingRole(true)}>
|
||||
<DropdownMenuItem
|
||||
data-test={'update-invitation-trigger'}
|
||||
onClick={() => setIsUpdatingRole(true)}
|
||||
>
|
||||
<Trans i18nKey={'teams:updateInvitation'} />
|
||||
</DropdownMenuItem>
|
||||
|
||||
<If condition={getIsInviteExpired(invitation.expires_at)}>
|
||||
<DropdownMenuItem onClick={() => setIsRenewingInvite(true)}>
|
||||
<DropdownMenuItem
|
||||
data-test={'renew-invitation-trigger'}
|
||||
onClick={() => setIsRenewingInvite(true)}
|
||||
>
|
||||
<Trans i18nKey={'teams:renewInvitation'} />
|
||||
</DropdownMenuItem>
|
||||
</If>
|
||||
</If>
|
||||
|
||||
<If condition={permissions.canRemoveInvitation}>
|
||||
<DropdownMenuItem onClick={() => setIsDeletingInvite(true)}>
|
||||
<DropdownMenuItem
|
||||
data-test={'remove-invitation-trigger'}
|
||||
onClick={() => setIsDeletingInvite(true)}
|
||||
>
|
||||
<Trans i18nKey={'teams:removeInvitation'} />
|
||||
</DropdownMenuItem>
|
||||
</If>
|
||||
|
||||
@@ -66,7 +66,7 @@ function DeleteInvitationForm({
|
||||
};
|
||||
|
||||
return (
|
||||
<form action={onInvitationRemoved}>
|
||||
<form data-test={'delete-invitation-form'} action={onInvitationRemoved}>
|
||||
<div className={'flex flex-col space-y-6'}>
|
||||
<p className={'text-muted-foreground text-sm'}>
|
||||
<Trans i18nKey={'common:modalConfirmationQuestion'} />
|
||||
@@ -82,7 +82,7 @@ function DeleteInvitationForm({
|
||||
</AlertDialogCancel>
|
||||
|
||||
<Button
|
||||
data-test={'confirm-delete-invitation'}
|
||||
type={'submit'}
|
||||
variant={'destructive'}
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
|
||||
@@ -9,7 +9,7 @@ export function InvitationSubmitButton(props: { accountName: string }) {
|
||||
const { pending } = useFormStatus();
|
||||
|
||||
return (
|
||||
<Button className={'w-full'} disabled={pending}>
|
||||
<Button type={'submit'} className={'w-full'} disabled={pending}>
|
||||
<Trans
|
||||
i18nKey={pending ? 'teams:joiningTeam' : 'teams:joinTeam'}
|
||||
values={{
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
import { If } from '@kit/ui/if';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
|
||||
import { UpdateMemberRoleSchema } from '../../schema/update-member-role.schema';
|
||||
import { RoleSchema } from '../../schema/update-member-role.schema';
|
||||
import { updateInvitationAction } from '../../server/actions/team-invitations-server-actions';
|
||||
import { MembershipRoleSelector } from '../members/membership-role-selector';
|
||||
import { RolesDataProvider } from '../members/roles-data-provider';
|
||||
@@ -47,37 +47,30 @@ export const UpdateInvitationDialog: React.FC<{
|
||||
userRoleHierarchy,
|
||||
account,
|
||||
}) => {
|
||||
return (
|
||||
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
<Trans i18nKey={'teams:updateMemberRoleModalHeading'} />
|
||||
</DialogTitle>
|
||||
return (
|
||||
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
<Trans i18nKey={'teams:updateMemberRoleModalHeading'} />
|
||||
</DialogTitle>
|
||||
|
||||
<DialogDescription>
|
||||
<Trans i18nKey={'teams:updateMemberRoleModalDescription'} />
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<DialogDescription>
|
||||
<Trans i18nKey={'teams:updateMemberRoleModalDescription'} />
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<RolesDataProvider
|
||||
accountId={account}
|
||||
maxRoleHierarchy={userRoleHierarchy}
|
||||
>
|
||||
{(roles) => (
|
||||
<UpdateInvitationForm
|
||||
account={account}
|
||||
invitationId={invitationId}
|
||||
userRole={userRole}
|
||||
userRoleHierarchy={roles.length}
|
||||
setIsOpen={setIsOpen}
|
||||
/>
|
||||
)}
|
||||
</RolesDataProvider>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
<UpdateInvitationForm
|
||||
account={account}
|
||||
invitationId={invitationId}
|
||||
userRole={userRole}
|
||||
userRoleHierarchy={userRoleHierarchy}
|
||||
setIsOpen={setIsOpen}
|
||||
/>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
function UpdateInvitationForm({
|
||||
account,
|
||||
@@ -113,7 +106,7 @@ function UpdateInvitationForm({
|
||||
|
||||
const form = useForm({
|
||||
resolver: zodResolver(
|
||||
UpdateMemberRoleSchema.refine(
|
||||
RoleSchema.refine(
|
||||
(data) => {
|
||||
return data.role !== userRole;
|
||||
},
|
||||
@@ -133,6 +126,7 @@ function UpdateInvitationForm({
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form
|
||||
data-test={'update-invitation-form'}
|
||||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
className={'flex flex-col space-y-6'}
|
||||
>
|
||||
@@ -177,7 +171,7 @@ function UpdateInvitationForm({
|
||||
}}
|
||||
/>
|
||||
|
||||
<Button data-test={'confirm-update-member-role'} disabled={pending}>
|
||||
<Button type={'submit'} disabled={pending}>
|
||||
<Trans i18nKey={'teams:updateRoleSubmitLabel'} />
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
@@ -17,7 +17,7 @@ export class AccountPerSeatBillingService {
|
||||
|
||||
logger.info(
|
||||
ctx,
|
||||
`Getting per-seat subscription item for account ${accountId}...`,
|
||||
`Retrieving per-seat subscription item for account ${accountId}...`,
|
||||
);
|
||||
|
||||
const { data, error } = await this.client
|
||||
@@ -34,7 +34,7 @@ export class AccountPerSeatBillingService {
|
||||
`,
|
||||
)
|
||||
.eq('account_id', accountId)
|
||||
.eq('subscription_items.type', 'per-seat')
|
||||
.eq('subscription_items.type', 'per_seat')
|
||||
.maybeSingle();
|
||||
|
||||
if (error) {
|
||||
@@ -52,7 +52,7 @@ export class AccountPerSeatBillingService {
|
||||
if (!data?.subscription_items) {
|
||||
logger.info(
|
||||
ctx,
|
||||
`No per-seat subscription item found for account ${accountId}. Exiting...`,
|
||||
`Account is not subscribed to a per-seat subscription. Exiting...`,
|
||||
);
|
||||
|
||||
return;
|
||||
|
||||
@@ -59,6 +59,7 @@ export function DataTable<TData, TValue>({
|
||||
table.getRowModel().rows.map((row) => (
|
||||
<TableRow
|
||||
key={row.id}
|
||||
data-row-id={row.id}
|
||||
data-state={row.getIsSelected() && 'selected'}
|
||||
>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
|
||||
Reference in New Issue
Block a user