mirror of
https://github.com/n8n-io/n8n.git
synced 2026-05-23 21:06:51 +02:00
99 lines
3.0 KiB
TypeScript
99 lines
3.0 KiB
TypeScript
import type { User } from '@n8n/api-types';
|
|
import { customAlphabet } from 'nanoid';
|
|
|
|
import type { ApiHelpers } from './api-helper';
|
|
import { TestError } from '../Types';
|
|
|
|
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz0123456789', 8);
|
|
|
|
export interface TestUser {
|
|
id: string;
|
|
email: string;
|
|
password: string;
|
|
firstName: string;
|
|
lastName: string;
|
|
role: 'global:owner' | 'global:admin' | 'global:member';
|
|
}
|
|
|
|
/**
|
|
* Creates test users via n8n's invitation API.
|
|
* Note: Using this with n8n.api will affect browser cookies. Use with the isolated api fixture instead unless you want to overwrite the existing user
|
|
*/
|
|
export class UserApiHelper {
|
|
constructor(private api: ApiHelpers) {}
|
|
|
|
/**
|
|
* Create and activate a test user
|
|
*/
|
|
async create(options: Partial<TestUser> = {}): Promise<TestUser> {
|
|
const user = {
|
|
email: options.email?.toLowerCase() ?? `testuser${nanoid()}@test.com`,
|
|
password: options.password ?? 'PlaywrightTest123',
|
|
firstName: options.firstName ?? 'Test',
|
|
lastName: options.lastName ?? `User${nanoid()}`,
|
|
role: options.role ?? 'global:member',
|
|
};
|
|
|
|
// Invite user
|
|
const inviteResponse = await this.api.request.post('/rest/invitations', {
|
|
data: [{ email: user.email, role: user.role }],
|
|
});
|
|
if (!inviteResponse.ok()) {
|
|
throw new TestError(`Failed to invite user: ${inviteResponse.status()}`);
|
|
}
|
|
const inviteData = await inviteResponse.json();
|
|
const { id, inviteAcceptUrl } = inviteData.data[0].user;
|
|
|
|
// Accept invitation (invite URL contains token, e.g. /signup?token=...)
|
|
const url = new URL(inviteAcceptUrl);
|
|
const token = url.searchParams.get('token');
|
|
if (!token) {
|
|
throw new TestError(`Invite URL has no token: ${inviteAcceptUrl}`);
|
|
}
|
|
|
|
const acceptResponse = await this.api.request.post('/rest/invitations/accept', {
|
|
data: {
|
|
token,
|
|
firstName: user.firstName,
|
|
lastName: user.lastName,
|
|
password: user.password,
|
|
},
|
|
});
|
|
if (!acceptResponse.ok()) {
|
|
throw new TestError(`Failed to accept invitation: ${acceptResponse.status()}`);
|
|
}
|
|
|
|
return { id, ...user };
|
|
}
|
|
|
|
/**
|
|
* Get all users, with optional filtering by email, firstName, lastName, or fullText search
|
|
*/
|
|
async getUsers(options?: {
|
|
filter?: { email?: string; firstName?: string; lastName?: string; fullText?: string };
|
|
}): Promise<User[]> {
|
|
const params = new URLSearchParams();
|
|
if (options?.filter) {
|
|
params.set('filter', JSON.stringify(options.filter));
|
|
}
|
|
|
|
const response = await this.api.request.get('/rest/users', { params });
|
|
if (!response.ok()) {
|
|
throw new TestError(`Failed to get users: ${response.status()}`);
|
|
}
|
|
const json = await response.json();
|
|
// API returns { data: { count, items: [...users] } }
|
|
return json.data?.items ?? [];
|
|
}
|
|
|
|
/**
|
|
* Get a single user by email address
|
|
* @param email - The email address to search for
|
|
* @returns User object if found, null if no user exists with that email
|
|
*/
|
|
async getUserByEmail(email: string): Promise<User | null> {
|
|
const users = await this.getUsers({ filter: { email } });
|
|
return users[0] ?? null;
|
|
}
|
|
}
|