douyin-release-gate / verify (push) Failing after 18m54s
Add Douyin notification polling, event details, and manual multi-account private messaging. Refine environment memory settings, account operations, login collection recovery, and message UI; update tests and documentation.
145 lines
8.0 KiB
JavaScript
145 lines
8.0 KiB
JavaScript
const assert = require('node:assert/strict');
|
|
const { readFileSync } = require('node:fs');
|
|
const { test } = require('node:test');
|
|
const ts = require('typescript');
|
|
|
|
const source = ts.createSourceFile(
|
|
'AccountManagementList.tsx',
|
|
readFileSync(`${__dirname}/../src/components/accounts/AccountManagementList.tsx`, 'utf8'),
|
|
ts.ScriptTarget.Latest,
|
|
true,
|
|
ts.ScriptKind.TSX,
|
|
);
|
|
const code = source.getFullText();
|
|
|
|
function accountRenderer(file, title = '账号') {
|
|
const ast = ts.createSourceFile(file, readFileSync(`${__dirname}/../src/components/accounts/${file}`, 'utf8'), ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
|
|
let renderer;
|
|
function visit(node) {
|
|
if (!renderer && ts.isObjectLiteralExpression(node) && node.properties.some(p => ts.isPropertyAssignment(p) && p.name.getText(ast) === 'title' && p.initializer.getText(ast) === `'${title}'`)) {
|
|
renderer = node.properties.find(p => ts.isPropertyAssignment(p) && p.name.getText(ast) === 'render').initializer.getText(ast);
|
|
}
|
|
ts.forEachChild(node, visit);
|
|
}
|
|
visit(ast);
|
|
assert.ok(renderer, `${file} 缺少账号列`);
|
|
const { createRequire } = require('node:module');
|
|
const requireWeb = createRequire(`${__dirname}/../package.json`);
|
|
const imports = { ...requireWeb('antd'), history: { push() {} }, formatCount: (value) => value == null ? '—' : String(value), dateTime: (value) => value };
|
|
const js = ts.transpileModule(`const render = ${renderer};`, { compilerOptions: { jsx: ts.JsxEmit.ReactJSX, module: ts.ModuleKind.CommonJS } }).outputText;
|
|
return require('node:vm').runInNewContext(`${js}\nrender`, { ...imports, exports: {}, require: requireWeb });
|
|
}
|
|
|
|
test('我的账号列不显示 UID,头像与监控账号一致且详情入口不变', () => {
|
|
const renderOwned = accountRenderer('AccountManagementList.tsx');
|
|
const renderMonitoring = accountRenderer('MonitoringAccountList.tsx');
|
|
for (const [name, avatar] of [['豪的抖音', 'https://example.com/avatar.jpg'], ['a', ''], ['', undefined]]) {
|
|
const data = { id: 'account-1', name, nickname: name, avatar_url: avatar, platform_account_key: '99491952055' };
|
|
const owned = renderOwned(undefined, data);
|
|
const monitored = renderMonitoring(undefined, data);
|
|
assert.equal(owned.type, monitored.type);
|
|
assert.equal(owned.props.align, 'center');
|
|
assert.equal(owned.props.gap, monitored.props.gap);
|
|
const [ownedAvatar, ownedName] = owned.props.children;
|
|
const [monitoredAvatar, monitoredName] = monitored.props.children;
|
|
assert.deepEqual(Object.keys(ownedAvatar.props), Object.keys(monitoredAvatar.props));
|
|
for (const key of Object.keys(monitoredAvatar.props)) assert.equal(ownedAvatar.props[key], monitoredAvatar.props[key]);
|
|
assert.equal(ownedName.props.children, name || '待登录');
|
|
if (name) assert.equal(ownedName.props.children, monitoredName.props.children);
|
|
assert.equal(data.name, name, '显示占位文案不修改原始昵称');
|
|
const requireWeb = require('node:module').createRequire(`${__dirname}/../package.json`);
|
|
const html = requireWeb('react-dom/server').renderToStaticMarkup(owned);
|
|
assert.ok(!html.includes(data.platform_account_key), '账号列不应显示 UID');
|
|
}
|
|
assert.match(code, /history\.push\(`\/accounts\/\$\{account\.id\}`\)/);
|
|
});
|
|
|
|
test('运行环境只展示状态、网关和代理,不显示名称或别名', () => {
|
|
const render = accountRenderer('AccountManagementList.tsx', '运行环境');
|
|
const requireWeb = require('node:module').createRequire(`${__dirname}/../package.json`);
|
|
const { renderToStaticMarkup } = requireWeb('react-dom/server');
|
|
for (const [environment, expected] of [
|
|
[{ runtime_id: 'runtime-1', gateway: 'gateway-1', network_exit_id: 'proxy-1', network_exit_health: 'healthy' }, ['运行中', '网关 gateway-1', '出口 proxy-1 · 健康']],
|
|
[{ runtime_cleanup_pending: true, runtime_id: 'runtime-1', gateway: 'gateway-2', network_exit_id: 'proxy-2', network_exit_health: 'unhealthy' }, ['停止待确认', '网关 gateway-2', '出口 proxy-2 · unhealthy']],
|
|
[{}, ['未启动', '网关 —', '机器直连']],
|
|
[{ network_exit_id: 'proxy-3' }, ['未启动', '网关 —', '出口 proxy-3 · 状态未知']],
|
|
]) {
|
|
for (const pending_environment of [undefined, 'env-de0f681e61ba10330090bfe8']) {
|
|
const html = renderToStaticMarkup(render(undefined, { pending_environment, environment: {
|
|
...environment, name: '不应展示的环境名称', alias: 'env-de0f681e61ba10330090bfe8',
|
|
} }));
|
|
assert.ok(!html.includes('不应展示的环境名称'));
|
|
assert.ok(!html.includes('env-de0f681e61ba10330090bfe8'));
|
|
for (const text of expected) assert.ok(html.includes(text), `应保留 ${text}`);
|
|
}
|
|
}
|
|
assert.match(renderToStaticMarkup(render(undefined, {})), /未绑定运行环境/);
|
|
});
|
|
|
|
test('作品数据列只用普通字体展示 N/M', () => {
|
|
const render = accountRenderer('AccountManagementList.tsx', '作品数据');
|
|
const requireWeb = require('node:module').createRequire(`${__dirname}/../package.json`);
|
|
const { renderToStaticMarkup } = requireWeb('react-dom/server');
|
|
const cell = render(undefined, { public_work_count: 100, work_count: 7, latest_published_at: '2026-01-01' });
|
|
const textContent = element => [element.props.children].flat(Infinity).join('');
|
|
assert.equal(textContent(cell), '7/100');
|
|
assert.equal(cell.props.type, undefined, '使用正常字体颜色');
|
|
assert.equal(cell.props.style, undefined, '不使用小号字体');
|
|
assert.doesNotMatch(renderToStaticMarkup(cell), /公开作品|已采集|最近发布/);
|
|
const unknown = render(undefined, { work_count: 7 });
|
|
assert.equal(textContent(unknown), '7/未知');
|
|
const zero = render(undefined, { public_work_count: 0, work_count: 0 });
|
|
assert.equal(textContent(zero), '0/0');
|
|
});
|
|
|
|
test('最近发布使用独立列', () => {
|
|
const render = accountRenderer('AccountManagementList.tsx', '最近发布');
|
|
assert.equal(render(undefined, { latest_published_at: '2026-01-01' }), '2026-01-01');
|
|
});
|
|
|
|
test('未登录环境没有真实作品总数,不把占位零当作抖音数据', () => {
|
|
const render = accountRenderer('AccountManagementList.tsx', '作品数据');
|
|
const requireWeb = require('node:module').createRequire(`${__dirname}/../package.json`);
|
|
const html = requireWeb('react-dom/server').renderToStaticMarkup(render(undefined, {
|
|
pending_environment: true, public_work_count: 0, work_count: 0,
|
|
}));
|
|
assert.match(html, /登录后显示作品数据/);
|
|
assert.doesNotMatch(html, /0 个公开作品|公开作品总数 0/);
|
|
});
|
|
|
|
test('列表展示粉丝、关注、获赞、互关、评论独立数值列', () => {
|
|
for (const [title, value] of [
|
|
['粉丝', 'account.follower_count'],
|
|
['关注', 'account.following_count'],
|
|
['获赞', 'account.total_favorited'],
|
|
['互关', 'account.friend_count'],
|
|
['评论', 'account.comment_total'],
|
|
]) {
|
|
assert.ok(
|
|
code.includes(`title: '${title}',\n render: (_, account) => formatCount(${value}),`),
|
|
`${title} 应为独立数值列`,
|
|
);
|
|
}
|
|
});
|
|
|
|
test('操作列使用状态感知主操作和更多菜单,不再固定展示启动', () => {
|
|
assert.match(code, /accountPrimaryAction\(account, browser, verifiedLogin\[account\.id\]\)/);
|
|
assert.match(code, /performPrimary\(account, primary\.key\)/);
|
|
assert.match(code, /<Dropdown/);
|
|
assert.match(code, /\{primary\.label\}/);
|
|
assert.doesNotMatch(code, /startAccount\(account\)/);
|
|
});
|
|
|
|
test('登录弹窗共享自动流程,成功刷新列表,关闭后卸载弹窗', () => {
|
|
assert.match(code, /loginTarget \? \(/);
|
|
assert.match(code, /onClose=\{\(\) => setLoginTarget\(null\)\}/);
|
|
assert.match(code, /\[loginTarget\.id\]: 'logged_in'/);
|
|
assert.match(code, /void load\(\)/);
|
|
assert.doesNotMatch(code, /刷新登录画面|核验浏览器身份|requestLoginQR|verifyLogin/);
|
|
});
|
|
|
|
test('操作失败走 conflictMessage 透传原因并保留排查信息', () => {
|
|
assert.ok(code.includes("messageApi.error(conflictMessage(cause, '账号操作未完成。'))"));
|
|
assert.ok(code.includes("console.error('账号主操作失败'"));
|
|
});
|