Files
DMehaffy bfcef1911b enhancement: add list & delete admin user cli commands (#23022)
* enhancement: add delete user cli command

* fix: admin service reference to proper deleteById

* fix: add delete command to exported functions

* enhancement: add the list users

* fix: handle last super admin error gracefully

* enhancement: pr feedback and add active/block user commands

* fix: clean up boolean casting on active and block

* enhancement: add list-users command alias "list"

* test(cli): add user cli commands

* chore: fix linting

* test: reset stdin after tests

* chore: remove bad comment

* fix(cli): boolean admin flags, async inquirer, and stable list-users / CLI fixtures

---------

Co-authored-by: Ben Irvin <ben.irvin@strapi.io>
Co-authored-by: Ben Irvin <ben@innerdvations.com>
2026-04-16 14:39:34 +02:00

118 lines
3.9 KiB
JavaScript

'use strict';
const path = require('path');
const dotenv = require('dotenv');
const fs = require('node:fs/promises');
const stripAnsi = require('strip-ansi');
const normalizeLineEndings = (str) => str.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
const trimLines = (str) => {
const lines = normalizeLineEndings(str)
.split('\n')
.map((line) => stripAnsi(line).trim());
// Remove leading empty lines
while (lines.length > 0 && lines[0].length === 0) {
lines.shift();
}
// Remove trailing empty lines
while (lines.length > 0 && lines[lines.length - 1].length === 0) {
lines.pop();
}
// TODO: FIXME this is a workaround to fix the malformed sequences cliTable.toString() produces instead of dashes
// Filter out lines that do not contain any alphanumeric characters
return lines.filter((line) => /[a-zA-Z0-9]/.test(line));
};
const expectConsoleLinesToEqual = (received, expected) => {
const receivedLines = trimLines(received);
const expectedLines = trimLines(expected);
expect(receivedLines).toEqual(expectedLines);
};
const expectConsoleLinesToInclude = (received, expected) => {
const receivedLines = trimLines(received);
const expectedLines = trimLines(expected);
expectedLines.forEach((line) => {
expect(receivedLines).toContain(line);
});
};
/** Lines Strapi logs to stdout during CLI (timestamps change every run). */
const LOGGER_LINE =
/^\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}\] (warn|info|error|debug|verbose):/;
/**
* Normalize CLI table/list stdout for Jest snapshots (strip ANSI, trim lines, drop
* empty/garbled lines — same rules as expectConsoleLinesToInclude). Also drops
* Winston-style log lines so snapshots stay stable.
*
* @param {string} received
* @returns {string}
*/
const normalizeCliOutputForSnapshot = (received) =>
trimLines(received)
.filter((line) => !LOGGER_LINE.test(line))
.join('\n');
/**
* Masks the numeric ID in `admin:list-users` table rows for a known fixture email so
* snapshots do not depend on DB auto-increment (varies with leftover rows / import order).
*
* @param {string} normalized - output already passed through {@link normalizeCliOutputForSnapshot}
* @param {string} fixtureEmail - email column value identifying the row to mask (default: list-users CLI test)
* @returns {string}
*/
const maskVolatileAdminListUsersTableIds = (normalized, fixtureEmail = 'test.list@strapi.io') => {
const escaped = fixtureEmail.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const row = new RegExp(`^(│)(\\s*)(\\d+)(\\s*)(│\\s*${escaped}\\b.*)$`, 'gm');
return normalized.replace(row, (_, pipe, sp1, digits, sp2, tail) => {
return `${pipe}${sp1}${'*'.repeat(digits.length)}${sp2}${tail}`;
});
};
/**
* Load environment variables from a test app's .env file
* This ensures consistent environment setup for both browser and CLI tests
* when loading Strapi directly (not as a server process)
*
* @param {string} appPath - Path to the test app directory
* @returns {Promise<void>}
*/
const loadTestAppEnv = async (appPath) => {
const envPath = path.join(appPath, '.env');
try {
await fs.access(envPath);
// Load .env file into process.env
dotenv.config({ path: envPath });
} catch (err) {
// .env file doesn't exist, which is okay - use defaults
// Set default values if .env doesn't exist
if (!process.env.APP_KEYS) {
process.env.APP_KEYS = 'test-key-1,test-key-2,test-key-3,test-key-4';
}
if (!process.env.ADMIN_JWT_SECRET) {
process.env.ADMIN_JWT_SECRET = process.env.JWT_SECRET || 'test-jwt-secret';
}
if (!process.env.API_TOKEN_SALT) {
process.env.API_TOKEN_SALT = 'test-api-token-salt';
}
if (!process.env.TRANSFER_TOKEN_SALT) {
process.env.TRANSFER_TOKEN_SALT = 'test-transfer-token-salt';
}
}
};
module.exports = {
expectConsoleLinesToEqual,
expectConsoleLinesToInclude,
loadTestAppEnv,
normalizeCliOutputForSnapshot,
maskVolatileAdminListUsersTableIds,
};