现在有的接口对接测试完成
This commit is contained in:
@@ -25,10 +25,12 @@ test('API client exposes latest document-defined PC operations', () => {
|
||||
'clientId', 'tenantId', 'tokenKey', 'getToken', 'setToken', 'clearToken', 'buildApiUrl',
|
||||
'register', 'login', 'loginBySms', 'sendSmsCode',
|
||||
'currentProfile', 'updateProfile', 'changePassword', 'resetPassword', 'changePhone',
|
||||
'deactivateAccount', 'logout', 'uploadFile', 'initResumableUpload', 'uploadResumableChunk',
|
||||
'completeResumableUpload', 'createFileReference', 'deleteFileReference',
|
||||
'captchaRequirement', 'captchaChallenge', 'captchaVerify',
|
||||
'deactivateAccount', 'logout', 'initResumableUpload', 'uploadResumableChunk',
|
||||
'completeResumableUpload',
|
||||
'captchaRequirement', 'captchaChallenge', 'captchaVerify', 'captchaChallengeUrl', 'captchaVerifyUrl',
|
||||
'regionChildren', 'regionPath', 'regionSearch', 'regionDetail',
|
||||
'genealogyQuota',
|
||||
'notifications', 'unreadNotificationCount', 'markNotificationRead', 'markAllNotificationsRead',
|
||||
'feeds', 'feedsPage', 'feedDetail', 'createFeed', 'updateFeed', 'deleteFeed',
|
||||
'likeFeed', 'unlikeFeed', 'feedComments', 'feedCommentsPage', 'createFeedComment',
|
||||
'feedCommentReplies', 'feedCommentRepliesPage', 'deleteFeedComment',
|
||||
@@ -36,7 +38,12 @@ test('API client exposes latest document-defined PC operations', () => {
|
||||
'previewGenerationPoems', 'saveGenerationPoems',
|
||||
'lineagePersons', 'lineagePersonsPage', 'lineagePersonOptions', 'lineageTree',
|
||||
'createLineagePerson', 'lineagePersonDetail', 'updateLineagePerson', 'disableLineagePerson',
|
||||
'createLineageChild', 'createLineageParent', 'createLineageSibling', 'createLineageSpouse'
|
||||
'createLineageChild', 'createLineageParent', 'createLineageSibling', 'createLineageSpouse',
|
||||
'growthRecords', 'createGrowthRecord', 'growthRecordDetail', 'updateGrowthRecord', 'deleteGrowthRecord',
|
||||
'relativeRecords', 'createRelativeRecord', 'relativeRecordDetail', 'updateRelativeRecord', 'deleteRelativeRecord',
|
||||
'memos', 'createMemo', 'memoDetail', 'updateMemo', 'deleteMemo', 'deleteMeritRecord',
|
||||
'deleteArticle', 'deleteAlbum', 'deleteAlbumPhoto', 'deleteVideo', 'deleteCeremony', 'deleteCeremonyGift',
|
||||
'replaceCeremonyInvitees', 'ceremonyInvitations', 'respondCeremonyInvitation', 'myCeremonyInvitations'
|
||||
].sort();
|
||||
|
||||
assert.deepEqual(Object.keys(client).sort(), allowed);
|
||||
@@ -66,34 +73,80 @@ test('password login stores only the returned token', async () => {
|
||||
assert.equal(client.getToken(), 'access-token');
|
||||
});
|
||||
|
||||
test('sending an SMS code uses the latest documented request fields', async () => {
|
||||
test('sending an SMS code uses the PC operation path and documented request body', async () => {
|
||||
const client = GenealogyApi.createClient({
|
||||
baseUrl: 'https://api.example.test',
|
||||
tokenStore: { getItem() { return null; }, setItem() {}, removeItem() {} },
|
||||
axiosInstance: {
|
||||
request(config) {
|
||||
assert.equal(config.url, '/genealogy/pc/auth/sms/code');
|
||||
assert.equal(config.url, '/genealogy/pc/auth/sms/sms-login/code');
|
||||
assert.deepEqual(config.data, {
|
||||
grantType: 'sms',
|
||||
tenantId: '000000',
|
||||
clientId: 'ced7e5f0498645c6ec642dcf450b036f',
|
||||
phone: '13800000000',
|
||||
sceneCode: 'PC_SMS_LOGIN',
|
||||
validToken: 'captcha-ticket'
|
||||
});
|
||||
assert.equal(config.headers.clientid, 'ced7e5f0498645c6ec642dcf450b036f');
|
||||
return Promise.resolve({ data: { code: 200, data: null } });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await client.sendSmsCode({
|
||||
await client.sendSmsCode('sms-login', {
|
||||
phone: '13800000000',
|
||||
sceneCode: 'PC_SMS_LOGIN',
|
||||
validToken: 'captcha-ticket'
|
||||
});
|
||||
});
|
||||
|
||||
test('password reset adds the documented grant, tenant and client fields', async () => {
|
||||
test('PC verification operations keep operationCode in the path and clientid out of query and body', async () => {
|
||||
const calls = [];
|
||||
const client = GenealogyApi.createClient({
|
||||
baseUrl: 'https://api.example.test',
|
||||
tokenStore: { getItem() { return null; }, setItem() {}, removeItem() {} },
|
||||
axiosInstance: {
|
||||
request(config) {
|
||||
calls.push(config);
|
||||
return Promise.resolve({ data: { code: 200, data: { required: false } } });
|
||||
}
|
||||
}
|
||||
});
|
||||
const challenge = { tenantId: '000000', subject: '13800000000' };
|
||||
const verification = {
|
||||
tenantId: '000000',
|
||||
subject: '13800000000',
|
||||
challengeId: 'challenge-1',
|
||||
providerCode: 'tianai',
|
||||
captchaType: 'SLIDER',
|
||||
payload: { track: { left: 120, top: 0, trackList: [] } }
|
||||
};
|
||||
|
||||
await client.captchaRequirement('sms-login', { subject: '13800000000' });
|
||||
await client.captchaChallenge('sms-login', challenge);
|
||||
await client.captchaVerify('sms-login', verification);
|
||||
|
||||
assert.deepEqual(calls.map((config) => [config.method, config.url, config.params, config.data]), [
|
||||
['get', '/genealogy/pc/auth/verification/sms-login/require', {
|
||||
tenantId: '000000',
|
||||
subject: '13800000000'
|
||||
}, undefined],
|
||||
['post', '/genealogy/pc/auth/verification/sms-login/challenge', undefined, challenge],
|
||||
['post', '/genealogy/pc/auth/verification/sms-login/verify', undefined, verification]
|
||||
]);
|
||||
calls.forEach((config) => {
|
||||
assert.equal(config.headers.clientid, 'ced7e5f0498645c6ec642dcf450b036f');
|
||||
assert.equal(config.headers.Authorization, undefined);
|
||||
});
|
||||
assert.equal(
|
||||
client.captchaChallengeUrl('sms-login'),
|
||||
'https://api.example.test/genealogy/pc/auth/verification/sms-login/challenge'
|
||||
);
|
||||
assert.equal(
|
||||
client.captchaVerifyUrl('sms-login'),
|
||||
'https://api.example.test/genealogy/pc/auth/verification/sms-login/verify'
|
||||
);
|
||||
});
|
||||
|
||||
test('password reset adds the documented grant and tenant fields without body clientId', async () => {
|
||||
const client = GenealogyApi.createClient({
|
||||
baseUrl: 'https://api.example.test',
|
||||
tokenStore: { getItem() { return null; }, setItem() {}, removeItem() {} },
|
||||
@@ -105,7 +158,6 @@ test('password reset adds the documented grant, tenant and client fields', async
|
||||
assert.deepEqual(config.data, {
|
||||
grantType: 'password',
|
||||
tenantId: '000000',
|
||||
clientId: 'ced7e5f0498645c6ec642dcf450b036f',
|
||||
phone: '13800000000',
|
||||
smsCode: '123456',
|
||||
newPassword: 'password-md5'
|
||||
@@ -151,7 +203,6 @@ test('registration and SMS login use the PC auth endpoints with SMS fields', asy
|
||||
grantType: 'password',
|
||||
registerSource: 'PC',
|
||||
tenantId: '000000',
|
||||
clientId: 'ced7e5f0498645c6ec642dcf450b036f',
|
||||
phone: '13800000000',
|
||||
smsCode: '123456',
|
||||
nickName: '小李',
|
||||
@@ -160,14 +211,17 @@ test('registration and SMS login use the PC auth endpoints with SMS fields', asy
|
||||
['post', '/genealogy/pc/auth/login/sms', {
|
||||
grantType: 'sms',
|
||||
tenantId: '000000',
|
||||
clientId: 'ced7e5f0498645c6ec642dcf450b036f',
|
||||
phone: '13800000000',
|
||||
smsCode: '123456'
|
||||
}]
|
||||
]);
|
||||
calls.forEach((config) => {
|
||||
assert.equal(config.headers.clientid, 'ced7e5f0498645c6ec642dcf450b036f');
|
||||
assert.equal('clientId' in config.data, false);
|
||||
});
|
||||
});
|
||||
|
||||
test('phone change and account deactivation retain their PC request shapes', async () => {
|
||||
test('phone change and account deactivation retain PC bodies without clientId', async () => {
|
||||
let stored = 'access-token';
|
||||
const calls = [];
|
||||
const client = GenealogyApi.createClient({
|
||||
@@ -191,14 +245,16 @@ test('phone change and account deactivation retain their PC request shapes', asy
|
||||
assert.deepEqual(calls.map((config) => [config.method, config.url, config.data]), [
|
||||
['put', '/genealogy/pc/auth/phone', {
|
||||
phone: '13900000000',
|
||||
smsCode: '123456',
|
||||
clientId: 'ced7e5f0498645c6ec642dcf450b036f'
|
||||
smsCode: '123456'
|
||||
}],
|
||||
['post', '/genealogy/pc/auth/account/deactivate', {
|
||||
smsCode: '654321',
|
||||
clientId: 'ced7e5f0498645c6ec642dcf450b036f'
|
||||
smsCode: '654321'
|
||||
}]
|
||||
]);
|
||||
calls.forEach((config) => {
|
||||
assert.equal(config.headers.clientid, 'ced7e5f0498645c6ec642dcf450b036f');
|
||||
assert.equal('clientId' in config.data, false);
|
||||
});
|
||||
assert.equal(stored, '');
|
||||
});
|
||||
|
||||
@@ -233,7 +289,7 @@ test('region methods use PC paths and retain the logged-in authorization header'
|
||||
]);
|
||||
});
|
||||
|
||||
test('remaining PC file methods use the documented paths, request forms and login header', async () => {
|
||||
test('current PC file methods use the three documented resumable paths and request forms', async () => {
|
||||
const calls = [];
|
||||
const client = GenealogyApi.createClient({
|
||||
baseUrl: 'https://api.example.test',
|
||||
@@ -268,19 +324,6 @@ test('remaining PC file methods use the documented paths, request forms and logi
|
||||
fileMd5: 'file-md5',
|
||||
fileSize: 8388608
|
||||
});
|
||||
await client.createFileReference({
|
||||
bizType: 'family_feed',
|
||||
bizTable: 'gen_family_feed',
|
||||
bizId: '900013001',
|
||||
bizField: 'media_oss_ids',
|
||||
ossId: '2060000000000000000'
|
||||
});
|
||||
await client.deleteFileReference({
|
||||
bizTable: 'gen_family_feed',
|
||||
bizId: '900013001',
|
||||
bizField: 'media_oss_ids'
|
||||
});
|
||||
|
||||
assert.deepEqual(calls.map((config) => [
|
||||
config.method,
|
||||
config.url,
|
||||
@@ -289,13 +332,7 @@ test('remaining PC file methods use the documented paths, request forms and logi
|
||||
]), [
|
||||
['post', '/genealogy/pc/files/resumable/init', undefined, 'Bearer access-token'],
|
||||
['post', '/genealogy/pc/files/resumable/chunk', undefined, 'Bearer access-token'],
|
||||
['post', '/genealogy/pc/files/resumable/complete', undefined, 'Bearer access-token'],
|
||||
['post', '/genealogy/pc/files/reference', undefined, 'Bearer access-token'],
|
||||
['delete', '/genealogy/pc/files/reference', {
|
||||
bizTable: 'gen_family_feed',
|
||||
bizId: '900013001',
|
||||
bizField: 'media_oss_ids'
|
||||
}, 'Bearer access-token']
|
||||
['post', '/genealogy/pc/files/resumable/complete', undefined, 'Bearer access-token']
|
||||
]);
|
||||
assert.deepEqual(calls[0].data, {
|
||||
fileName: 'video.mp4',
|
||||
@@ -316,12 +353,55 @@ test('remaining PC file methods use the documented paths, request forms and logi
|
||||
fileMd5: 'file-md5',
|
||||
fileSize: 8388608
|
||||
});
|
||||
assert.deepEqual(calls[3].data, {
|
||||
bizType: 'family_feed',
|
||||
bizTable: 'gen_family_feed',
|
||||
bizId: '900013001',
|
||||
bizField: 'media_oss_ids',
|
||||
ossId: '2060000000000000000'
|
||||
});
|
||||
|
||||
test('genealogy quota uses the current PC path without manufacturing a genealogy ID', async () => {
|
||||
const client = GenealogyApi.createClient({
|
||||
baseUrl: 'https://api.example.test',
|
||||
tokenStore: { getItem() { return 'access-token'; }, setItem() {}, removeItem() {} },
|
||||
axiosInstance: {
|
||||
request(config) {
|
||||
assert.equal(config.method, 'get');
|
||||
assert.equal(config.url, '/genealogy/pc/genealogies/quota');
|
||||
assert.equal(config.params, undefined);
|
||||
assert.equal(config.data, undefined);
|
||||
assert.equal(config.headers.Authorization, 'Bearer access-token');
|
||||
assert.equal(config.headers.clientid, 'ced7e5f0498645c6ec642dcf450b036f');
|
||||
return Promise.resolve({ data: { code: 200, data: { canCreate: true, canJoin: true } } });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await client.genealogyQuota();
|
||||
});
|
||||
|
||||
test('notification methods use the current PC list, unread and read paths', async () => {
|
||||
const calls = [];
|
||||
const client = GenealogyApi.createClient({
|
||||
baseUrl: 'https://api.example.test',
|
||||
tokenStore: { getItem() { return 'access-token'; }, setItem() {}, removeItem() {} },
|
||||
axiosInstance: {
|
||||
request(config) {
|
||||
calls.push(config);
|
||||
return Promise.resolve({ data: { code: 200, data: [] } });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await client.notifications({ readStatus: '0' });
|
||||
await client.unreadNotificationCount();
|
||||
await client.markNotificationRead('2060000000000000001');
|
||||
await client.markAllNotificationsRead();
|
||||
|
||||
assert.deepEqual(calls.map((config) => [config.method, config.url, config.params, config.data]), [
|
||||
['get', '/genealogy/pc/notifications', { readStatus: '0' }, undefined],
|
||||
['get', '/genealogy/pc/notifications/unread-count', undefined, undefined],
|
||||
['post', '/genealogy/pc/notifications/2060000000000000001/read', undefined, undefined],
|
||||
['post', '/genealogy/pc/notifications/read-all', undefined, undefined]
|
||||
]);
|
||||
calls.forEach((config) => {
|
||||
assert.equal(config.headers.Authorization, 'Bearer access-token');
|
||||
assert.equal(config.headers.clientid, 'ced7e5f0498645c6ec642dcf450b036f');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -474,3 +554,186 @@ test('lineage person methods use the Apifox PC paths, query and LineagePersonBod
|
||||
['post', '/genealogy/pc/genealogies/2060000000000000001/lineage/persons/2060000000000000002/spouses', undefined, person, 'Bearer access-token', 'ced7e5f0498645c6ec642dcf450b036f']
|
||||
]);
|
||||
});
|
||||
|
||||
test('growth record methods use the Apifox PC paths and GrowthRecordBody', async () => {
|
||||
const calls = [];
|
||||
const client = GenealogyApi.createClient({
|
||||
baseUrl: 'https://api.example.test',
|
||||
tokenStore: { getItem() { return 'access-token'; }, setItem() {}, removeItem() {} },
|
||||
axiosInstance: {
|
||||
request(config) {
|
||||
calls.push(config);
|
||||
return Promise.resolve({ data: { code: 200, data: {} } });
|
||||
}
|
||||
}
|
||||
});
|
||||
const genealogyId = '2060000000000000001';
|
||||
const recordId = '2060000000000000002';
|
||||
const record = {
|
||||
lineagePersonId: '2060000000000000003',
|
||||
recordType: 'birth',
|
||||
recordTitle: '出生记录',
|
||||
recordContent: '平安出生',
|
||||
recordDate: '2026-07-24',
|
||||
remindTime: '2026-07-24T09:00:00+08:00',
|
||||
mediaOssIds: '101,102',
|
||||
sortOrder: 1,
|
||||
status: 'enabled'
|
||||
};
|
||||
|
||||
await client.growthRecords(genealogyId);
|
||||
await client.createGrowthRecord(genealogyId, record);
|
||||
await client.growthRecordDetail(genealogyId, recordId);
|
||||
await client.updateGrowthRecord(genealogyId, recordId, record);
|
||||
await client.deleteGrowthRecord(genealogyId, recordId);
|
||||
|
||||
assert.deepEqual(calls.map((config) => [
|
||||
config.method,
|
||||
config.url,
|
||||
config.params,
|
||||
config.data,
|
||||
config.headers.Authorization,
|
||||
config.headers.clientid
|
||||
]), [
|
||||
['get', '/genealogy/pc/genealogies/2060000000000000001/growth-records', undefined, undefined, 'Bearer access-token', 'ced7e5f0498645c6ec642dcf450b036f'],
|
||||
['post', '/genealogy/pc/genealogies/2060000000000000001/growth-records', undefined, record, 'Bearer access-token', 'ced7e5f0498645c6ec642dcf450b036f'],
|
||||
['get', '/genealogy/pc/genealogies/2060000000000000001/growth-records/2060000000000000002', undefined, undefined, 'Bearer access-token', 'ced7e5f0498645c6ec642dcf450b036f'],
|
||||
['put', '/genealogy/pc/genealogies/2060000000000000001/growth-records/2060000000000000002', undefined, record, 'Bearer access-token', 'ced7e5f0498645c6ec642dcf450b036f'],
|
||||
['delete', '/genealogy/pc/genealogies/2060000000000000001/growth-records/2060000000000000002', undefined, undefined, 'Bearer access-token', 'ced7e5f0498645c6ec642dcf450b036f']
|
||||
]);
|
||||
});
|
||||
|
||||
test('relative record methods use the Apifox PC paths and RelativeRecordBody', async () => {
|
||||
const calls = [];
|
||||
const client = GenealogyApi.createClient({
|
||||
baseUrl: 'https://api.example.test',
|
||||
tokenStore: { getItem() { return 'access-token'; }, setItem() {}, removeItem() {} },
|
||||
axiosInstance: { request(config) { calls.push(config); return Promise.resolve({ data: { code: 200, data: {} } }); } }
|
||||
});
|
||||
const genealogyId = '2060000000000000001';
|
||||
const relativeId = '2060000000000000002';
|
||||
const record = { relativeName: '王叔', relationName: '长辈', eventName: '寿宴', eventTime: '2026-07-24T09:00:00+08:00', giftAmount: 500, recordContent: '出席', mediaOssIds: '101', sortOrder: 1, status: 'enabled' };
|
||||
|
||||
await client.relativeRecords(genealogyId);
|
||||
await client.createRelativeRecord(genealogyId, record);
|
||||
await client.relativeRecordDetail(genealogyId, relativeId);
|
||||
await client.updateRelativeRecord(genealogyId, relativeId, record);
|
||||
await client.deleteRelativeRecord(genealogyId, relativeId);
|
||||
|
||||
assert.deepEqual(calls.map((config) => [config.method, config.url, config.data]), [
|
||||
['get', '/genealogy/pc/genealogies/2060000000000000001/relative-records', undefined],
|
||||
['post', '/genealogy/pc/genealogies/2060000000000000001/relative-records', record],
|
||||
['get', '/genealogy/pc/genealogies/2060000000000000001/relative-records/2060000000000000002', undefined],
|
||||
['put', '/genealogy/pc/genealogies/2060000000000000001/relative-records/2060000000000000002', record],
|
||||
['delete', '/genealogy/pc/genealogies/2060000000000000001/relative-records/2060000000000000002', undefined]
|
||||
]);
|
||||
});
|
||||
|
||||
test('memo methods use the Apifox PC paths and documented request fields', async () => {
|
||||
const calls = [];
|
||||
const client = GenealogyApi.createClient({
|
||||
baseUrl: 'https://api.example.test',
|
||||
tokenStore: { getItem() { return 'access-token'; }, setItem() {}, removeItem() {} },
|
||||
axiosInstance: { request(config) { calls.push(config); return Promise.resolve({ data: { code: 200, data: {} } }); } }
|
||||
});
|
||||
const genealogyId = '2060000000000000001';
|
||||
const memoId = '2060000000000000002';
|
||||
const memo = {
|
||||
memoTitle: '祭祖提醒',
|
||||
memoContent: '准备供品',
|
||||
remindTime: '2026-07-24T09:00:00+08:00',
|
||||
completed: '0',
|
||||
mediaOssIds: '101,102',
|
||||
sortOrder: 1,
|
||||
status: 'enabled'
|
||||
};
|
||||
|
||||
await client.memos(genealogyId);
|
||||
await client.createMemo(genealogyId, memo);
|
||||
await client.memoDetail(genealogyId, memoId);
|
||||
await client.updateMemo(genealogyId, memoId, memo);
|
||||
await client.deleteMemo(genealogyId, memoId);
|
||||
|
||||
assert.deepEqual(calls.map((config) => [config.method, config.url, config.data]), [
|
||||
['get', '/genealogy/pc/genealogies/2060000000000000001/memos', undefined],
|
||||
['post', '/genealogy/pc/genealogies/2060000000000000001/memos', memo],
|
||||
['get', '/genealogy/pc/genealogies/2060000000000000001/memos/2060000000000000002', undefined],
|
||||
['put', '/genealogy/pc/genealogies/2060000000000000001/memos/2060000000000000002', memo],
|
||||
['delete', '/genealogy/pc/genealogies/2060000000000000001/memos/2060000000000000002', undefined]
|
||||
]);
|
||||
});
|
||||
|
||||
test('merit record deletion uses the only documented PC merit operation', async () => {
|
||||
const client = GenealogyApi.createClient({
|
||||
baseUrl: 'https://api.example.test',
|
||||
tokenStore: { getItem() { return 'access-token'; }, setItem() {}, removeItem() {} },
|
||||
axiosInstance: {
|
||||
request(config) {
|
||||
assert.equal(config.method, 'delete');
|
||||
assert.equal(config.url, '/genealogy/pc/genealogies/2060000000000000001/merit-records/2060000000000000002');
|
||||
assert.equal(config.headers.Authorization, 'Bearer access-token');
|
||||
assert.equal(config.headers.clientid, 'ced7e5f0498645c6ec642dcf450b036f');
|
||||
return Promise.resolve({ data: { code: 200, data: null } });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await client.deleteMeritRecord('2060000000000000001', '2060000000000000002');
|
||||
});
|
||||
|
||||
test('remaining document-defined deletion operations use PC paths only', async () => {
|
||||
const calls = [];
|
||||
const client = GenealogyApi.createClient({
|
||||
baseUrl: 'https://api.example.test',
|
||||
tokenStore: { getItem() { return 'access-token'; }, setItem() {}, removeItem() {} },
|
||||
axiosInstance: { request(config) { calls.push(config); return Promise.resolve({ data: { code: 200, data: null } }); } }
|
||||
});
|
||||
const genealogyId = '2060000000000000001';
|
||||
|
||||
await client.deleteArticle(genealogyId, '2060000000000000002');
|
||||
await client.deleteAlbum(genealogyId, '2060000000000000003');
|
||||
await client.deleteAlbumPhoto(genealogyId, '2060000000000000003', '2060000000000000004');
|
||||
await client.deleteVideo(genealogyId, '2060000000000000005');
|
||||
await client.deleteCeremony(genealogyId, '2060000000000000006');
|
||||
await client.deleteCeremonyGift(genealogyId, '2060000000000000006', '2060000000000000007');
|
||||
|
||||
assert.deepEqual(calls.map((config) => [config.method, config.url, config.data, config.headers.clientid]), [
|
||||
['delete', '/genealogy/pc/genealogies/2060000000000000001/articles/2060000000000000002', undefined, 'ced7e5f0498645c6ec642dcf450b036f'],
|
||||
['delete', '/genealogy/pc/genealogies/2060000000000000001/albums/2060000000000000003', undefined, 'ced7e5f0498645c6ec642dcf450b036f'],
|
||||
['delete', '/genealogy/pc/genealogies/2060000000000000001/albums/2060000000000000003/photos/2060000000000000004', undefined, 'ced7e5f0498645c6ec642dcf450b036f'],
|
||||
['delete', '/genealogy/pc/genealogies/2060000000000000001/videos/2060000000000000005', undefined, 'ced7e5f0498645c6ec642dcf450b036f'],
|
||||
['delete', '/genealogy/pc/genealogies/2060000000000000001/ceremonies/2060000000000000006', undefined, 'ced7e5f0498645c6ec642dcf450b036f'],
|
||||
['delete', '/genealogy/pc/genealogies/2060000000000000001/ceremonies/2060000000000000006/gifts/2060000000000000007', undefined, 'ced7e5f0498645c6ec642dcf450b036f']
|
||||
]);
|
||||
});
|
||||
|
||||
test('ceremony invitation methods use the current PC invitation contracts', async () => {
|
||||
const calls = [];
|
||||
const client = GenealogyApi.createClient({
|
||||
baseUrl: 'https://api.example.test',
|
||||
tokenStore: { getItem() { return 'access-token'; }, setItem() {}, removeItem() {} },
|
||||
axiosInstance: { request(config) { calls.push(config); return Promise.resolve({ data: { code: 200, data: [] } }); } }
|
||||
});
|
||||
const genealogyId = '2060000000000000001';
|
||||
const ceremonyId = '2060000000000000002';
|
||||
|
||||
await client.replaceCeremonyInvitees(genealogyId, ceremonyId, { inviteeUserIds: ['900000001', '900000002'] });
|
||||
await client.ceremonyInvitations(genealogyId, ceremonyId);
|
||||
await client.respondCeremonyInvitation(genealogyId, ceremonyId, { inviteStatus: 'ACCEPTED' });
|
||||
await client.myCeremonyInvitations();
|
||||
|
||||
assert.deepEqual(calls.map((config) => [config.method, config.url, config.data]), [
|
||||
['put', '/genealogy/pc/genealogies/2060000000000000001/ceremonies/2060000000000000002/invitees', {
|
||||
inviteeUserIds: ['900000001', '900000002']
|
||||
}],
|
||||
['get', '/genealogy/pc/genealogies/2060000000000000001/ceremonies/2060000000000000002/invitations', undefined],
|
||||
['put', '/genealogy/pc/genealogies/2060000000000000001/ceremonies/2060000000000000002/invitations/me', {
|
||||
inviteStatus: 'ACCEPTED'
|
||||
}],
|
||||
['get', '/genealogy/pc/genealogies/ceremony-invitations/mine', undefined]
|
||||
]);
|
||||
calls.forEach((config) => {
|
||||
assert.equal(config.headers.Authorization, 'Bearer access-token');
|
||||
assert.equal(config.headers.clientid, 'ced7e5f0498645c6ec642dcf450b036f');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,6 +5,25 @@ const test = require('node:test');
|
||||
|
||||
const AuthPages = require('../public/js/auth-pages.js');
|
||||
|
||||
test('password login includes a verification token only when the PC strategy returns one', () => {
|
||||
assert.deepEqual(
|
||||
AuthPages.buildLoginBody({
|
||||
phone: '13800000000',
|
||||
password: 'plain-password',
|
||||
validToken: 'captcha-ticket'
|
||||
}, (value) => 'md5-' + value),
|
||||
{
|
||||
phone: '13800000000',
|
||||
password: 'md5-plain-password',
|
||||
validToken: 'captcha-ticket'
|
||||
}
|
||||
);
|
||||
assert.deepEqual(
|
||||
AuthPages.buildLoginBody({ phone: '13800000000', password: 'plain-password' }, (value) => 'md5-' + value),
|
||||
{ phone: '13800000000', password: 'md5-plain-password' }
|
||||
);
|
||||
});
|
||||
|
||||
test('registration body carries the SMS code but never sends confirmation or captcha fields', () => {
|
||||
assert.deepEqual(
|
||||
AuthPages.buildRegisterBody({
|
||||
@@ -40,11 +59,11 @@ test('password reset body carries only the reset DTO fields', () => {
|
||||
);
|
||||
});
|
||||
|
||||
test('authentication forms use the PC SMS scenes defined by the PC endpoint', () => {
|
||||
assert.equal(AuthPages.getCaptchaScene('login'), 'PC_SMS_LOGIN');
|
||||
assert.equal(AuthPages.getCaptchaScene('sms-login'), 'PC_SMS_LOGIN');
|
||||
assert.equal(AuthPages.getCaptchaScene('register'), 'PC_REGISTER');
|
||||
assert.equal(AuthPages.getCaptchaScene('password-reset'), 'PC_FORGOT_PASSWORD');
|
||||
test('authentication forms use the PC verification operation codes defined by Apifox', () => {
|
||||
assert.equal(AuthPages.getCaptchaOperation('login'), 'password-login');
|
||||
assert.equal(AuthPages.getCaptchaOperation('sms-login'), 'sms-login');
|
||||
assert.equal(AuthPages.getCaptchaOperation('register'), 'register');
|
||||
assert.equal(AuthPages.getCaptchaOperation('password-reset'), 'forgot-password');
|
||||
});
|
||||
|
||||
test('registration validates SMS code and password confirmation before submission', () => {
|
||||
@@ -67,3 +86,9 @@ test('registration page exposes the documented SMS and confirmation inputs', ()
|
||||
assert.match(source, /data-api-send-code/);
|
||||
assert.match(source, /name="confirmPassword"/);
|
||||
});
|
||||
|
||||
test('password login page retains a verification token field for the PC captcha strategy', () => {
|
||||
const source = fs.readFileSync(path.join(__dirname, '..', 'login.html'), 'utf8');
|
||||
|
||||
assert.match(source, /id="login-password-form"[\s\S]*name="validToken"/);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
const assert = require('node:assert/strict');
|
||||
const test = require('node:test');
|
||||
|
||||
const CaptchaPages = require('../public/js/captcha-pages.js');
|
||||
|
||||
test('captcha request bodies keep operationCode in the URL and never submit legacy client or scene fields', () => {
|
||||
const api = { tenantId: '000000', clientId: 'web-client' };
|
||||
const challenge = CaptchaPages.buildChallengeBody({
|
||||
operationCode: 'sms-login',
|
||||
subject: '13800000000'
|
||||
}, api);
|
||||
const verification = CaptchaPages.buildVerifyBody({
|
||||
data: {
|
||||
trackList: [{ x: 0, y: 0 }, { x: 120, y: 4 }]
|
||||
}
|
||||
}, {
|
||||
operationCode: 'sms-login',
|
||||
subject: '13800000000',
|
||||
challengeId: 'challenge-1'
|
||||
}, api);
|
||||
|
||||
assert.deepEqual(challenge, {
|
||||
tenantId: '000000',
|
||||
subject: '13800000000'
|
||||
});
|
||||
assert.deepEqual(verification, {
|
||||
tenantId: '000000',
|
||||
subject: '13800000000',
|
||||
challengeId: 'challenge-1',
|
||||
providerCode: 'tianai',
|
||||
captchaType: 'SLIDER',
|
||||
payload: {
|
||||
track: {
|
||||
trackList: [{ x: 0, y: 0 }, { x: 120, y: 4 }],
|
||||
left: 120,
|
||||
top: 4
|
||||
}
|
||||
}
|
||||
});
|
||||
assert.equal('clientId' in challenge, false);
|
||||
assert.equal('sceneCode' in challenge, false);
|
||||
assert.equal('clientId' in verification, false);
|
||||
assert.equal('sceneCode' in verification, false);
|
||||
});
|
||||
@@ -0,0 +1,47 @@
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const test = require('node:test');
|
||||
|
||||
const GenealogyEntryPages = require('../public/js/genealogy-entry-pages.js');
|
||||
|
||||
function read(relativePath) {
|
||||
return fs.readFileSync(path.resolve(__dirname, '..', relativePath), 'utf8');
|
||||
}
|
||||
|
||||
test('家谱入口只接受本地家谱业务页作为返回目标', () => {
|
||||
assert.equal(GenealogyEntryPages.getTargetPage('?next=profile-feed.html'), 'profile-feed.html');
|
||||
assert.equal(GenealogyEntryPages.getTargetPage('?next=https%3A%2F%2Fevil.example'), 'profile-family-home.html');
|
||||
});
|
||||
|
||||
test('家谱入口只展示当前 PC 已定义的额度,不伪造家谱编号', () => {
|
||||
const message = GenealogyEntryPages.buildStatus({ createRemaining: 1, joinRemaining: 2 }, 'profile-feed.html');
|
||||
|
||||
assert.match(message, /还可创建 1 部/);
|
||||
assert.match(message, /还可加入 2 部/);
|
||||
assert.match(message, /不能伪造家谱编号/);
|
||||
});
|
||||
|
||||
test('负数额度不向用户展示为可用数量', () => {
|
||||
assert.doesNotMatch(
|
||||
GenealogyEntryPages.buildStatus({ createRemaining: -1, joinRemaining: -1 }, 'profile-feed.html'),
|
||||
/-1/
|
||||
);
|
||||
});
|
||||
|
||||
test('个人中心和内容发布在缺少家谱上下文时统一进入家谱入口', () => {
|
||||
const profile = read('profile.html');
|
||||
const content = read('profile-content.html');
|
||||
const familyHome = read('profile-family-home.html');
|
||||
const familyEntry = read('profile-families.html');
|
||||
const profileCommon = read('public/js/profile-common.js');
|
||||
|
||||
assert.match(profile, /进入我的家谱/);
|
||||
assert.doesNotMatch(profile, /四川武胜汤氏族|四川达州刘氏族/);
|
||||
assert.match(profile, /href="profile-family-admin\.html" data-genealogy-context-link/);
|
||||
assert.match(content, /href="profile-feed\.html" data-feature-link="available" data-genealogy-context-link/);
|
||||
assert.match(familyHome, /href="profile-family-admin\.html" data-genealogy-context-link/);
|
||||
assert.match(familyEntry, /href="profile-family-admin\.html" data-genealogy-context-link/);
|
||||
assert.match(profileCommon, /function getGenealogyEntryUrl\(href\)/);
|
||||
assert.match(profileCommon, /genealogyId \? withGenealogyId\([^\n]+\) : getGenealogyEntryUrl\(/);
|
||||
});
|
||||
@@ -0,0 +1,51 @@
|
||||
const assert = require('node:assert/strict');
|
||||
const test = require('node:test');
|
||||
|
||||
const GrowthPages = require('../public/js/growth-pages.js');
|
||||
|
||||
test('成长记录页只从 URL 读取真实家谱编号', () => {
|
||||
assert.equal(GrowthPages.getCurrentGenealogyId('?genealogyId=900001001'), '900001001');
|
||||
assert.equal(GrowthPages.getCurrentGenealogyId(''), '');
|
||||
});
|
||||
|
||||
test('成长记录写入只构造 Apifox GrowthRecordBody 字段', () => {
|
||||
assert.deepEqual(
|
||||
GrowthPages.buildGrowthRecordBody({
|
||||
lineagePersonId: '2060000000000000001',
|
||||
recordType: 'birth',
|
||||
recordTitle: ' 出生记录 ',
|
||||
recordContent: ' 平安出生 ',
|
||||
recordDate: '2026-07-24',
|
||||
remindTime: '2026-07-24T09:00:00+08:00',
|
||||
mediaOssIds: '101,102',
|
||||
sortOrder: '3',
|
||||
status: 'enabled',
|
||||
content: '旧字段',
|
||||
personId: '旧字段'
|
||||
}),
|
||||
{
|
||||
lineagePersonId: '2060000000000000001',
|
||||
recordType: 'birth',
|
||||
recordTitle: '出生记录',
|
||||
recordContent: '平安出生',
|
||||
recordDate: '2026-07-24',
|
||||
remindTime: '2026-07-24T09:00:00+08:00',
|
||||
mediaOssIds: '101,102',
|
||||
sortOrder: 3,
|
||||
status: 'enabled'
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test('成长记录校验标题、整数 ID、附件 OSS ID 和排序值', () => {
|
||||
assert.equal(GrowthPages.validateGrowthRecordBody({ recordTitle: '' }), '请填写记录标题');
|
||||
assert.equal(GrowthPages.validateGrowthRecordBody({ recordTitle: '记录', lineagePersonId: 'A-1' }), '世系人物 ID 必须是整数');
|
||||
assert.equal(GrowthPages.validateGrowthRecordBody({ recordTitle: '记录', mediaOssIds: '101, 102' }), '附件 OSS ID 请使用英文逗号分隔的正整数');
|
||||
assert.equal(GrowthPages.validateGrowthRecordBody(GrowthPages.buildGrowthRecordBody({ recordTitle: '记录', sortOrder: '1.5' })), '排序值必须是安全整数');
|
||||
assert.equal(GrowthPages.validateGrowthRecordBody({ recordTitle: '记录', mediaOssIds: '101,102', sortOrder: 1 }), '');
|
||||
});
|
||||
|
||||
test('成长记录列表不猜测未展开的响应 DTO', () => {
|
||||
assert.deepEqual(GrowthPages.normalizeGrowthList([{ property1: 'value' }]), [{ property1: 'value' }]);
|
||||
assert.deepEqual(GrowthPages.normalizeGrowthList({ rows: [] }), []);
|
||||
});
|
||||
@@ -0,0 +1,45 @@
|
||||
const assert = require('node:assert/strict');
|
||||
const test = require('node:test');
|
||||
|
||||
const MemoPages = require('../public/js/memo-pages.js');
|
||||
|
||||
test('备忘录页只从 URL 读取真实家谱编号', () => {
|
||||
assert.equal(MemoPages.getCurrentGenealogyId('?genealogyId=900001001'), '900001001');
|
||||
assert.equal(MemoPages.getCurrentGenealogyId(''), '');
|
||||
});
|
||||
|
||||
test('备忘录写入只构造 Apifox 已核验请求体字段', () => {
|
||||
assert.deepEqual(
|
||||
MemoPages.buildMemoBody({
|
||||
memoTitle: ' 祭祖提醒 ',
|
||||
memoContent: ' 准备供品 ',
|
||||
remindTime: '2026-07-24T09:00:00+08:00',
|
||||
completed: ' 0 ',
|
||||
mediaOssIds: '101,102',
|
||||
sortOrder: '3',
|
||||
status: ' enabled ',
|
||||
relativeName: '旧字段'
|
||||
}),
|
||||
{
|
||||
memoTitle: '祭祖提醒',
|
||||
memoContent: '准备供品',
|
||||
remindTime: '2026-07-24T09:00:00+08:00',
|
||||
completed: '0',
|
||||
mediaOssIds: '101,102',
|
||||
sortOrder: 3,
|
||||
status: 'enabled'
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test('备忘录校验标题、附件 OSS ID 和排序值', () => {
|
||||
assert.equal(MemoPages.validateMemoBody({ memoTitle: '' }), '请填写备忘标题');
|
||||
assert.equal(MemoPages.validateMemoBody({ memoTitle: '提醒', mediaOssIds: '101, 102' }), '附件 OSS ID 请使用英文逗号分隔的正整数');
|
||||
assert.equal(MemoPages.validateMemoBody(MemoPages.buildMemoBody({ memoTitle: '提醒', sortOrder: '1.5' })), '排序值必须是安全整数');
|
||||
assert.equal(MemoPages.validateMemoBody({ memoTitle: '提醒', mediaOssIds: '101,102', sortOrder: 1 }), '');
|
||||
});
|
||||
|
||||
test('备忘录列表不猜测未展开的响应 DTO', () => {
|
||||
assert.deepEqual(MemoPages.normalizeMemoList([{ property1: 'value' }]), [{ property1: 'value' }]);
|
||||
assert.deepEqual(MemoPages.normalizeMemoList({ rows: [] }), []);
|
||||
});
|
||||
@@ -0,0 +1,26 @@
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const test = require('node:test');
|
||||
|
||||
const NotificationPages = require('../public/js/notification-pages.js');
|
||||
|
||||
test('notification page preserves only the unexpanded PC list records and unread count', () => {
|
||||
const rows = [{ notificationId: '2060000000000000001', title: '系统通知' }];
|
||||
|
||||
assert.deepEqual(NotificationPages.normalizeNotifications(rows), rows);
|
||||
assert.deepEqual(NotificationPages.normalizeNotifications({ rows }), []);
|
||||
assert.equal(NotificationPages.getUnreadCount(3), 3);
|
||||
assert.equal(NotificationPages.getUnreadCount(-1), 0);
|
||||
assert.equal(NotificationPages.formatNotification(rows[0]), '{\n "notificationId": "2060000000000000001",\n "title": "系统通知"\n}');
|
||||
});
|
||||
|
||||
test('message page loads the PC notification module instead of pending-page controls', () => {
|
||||
const source = fs.readFileSync(path.join(__dirname, '..', 'profile-messages.html'), 'utf8');
|
||||
|
||||
assert.doesNotMatch(source, /data-feature-status="pending"/);
|
||||
assert.match(source, /src="public\/js\/notification-pages\.js"/);
|
||||
assert.doesNotMatch(source, /src="public\/js\/pending-pages\.js"/);
|
||||
assert.match(source, /data-notification-unread-count/);
|
||||
assert.match(source, /data-notification-read-all/);
|
||||
});
|
||||
@@ -30,9 +30,9 @@ test('PC 对接规划明确以 Apifox 目录而非导出快照为契约源', ()
|
||||
test('pages do not load scripts for unavailable business APIs', () => {
|
||||
const removedScripts = [
|
||||
'album-pages.js', 'article-pages.js', 'ceremony-pages.js',
|
||||
'feedback-pages.js', 'genealogy-pages.js', 'growth-pages.js',
|
||||
'feedback-pages.js', 'genealogy-pages.js',
|
||||
'help-pages.js', 'join-apply-pages.js', 'member-admin-pages.js',
|
||||
'memo-pages.js', 'notification-pages.js', 'promotion-pages.js', 'vip-pages.js'
|
||||
'promotion-pages.js', 'vip-pages.js'
|
||||
];
|
||||
const retainedPages = fs.readdirSync(root).filter((entry) => entry.endsWith('.html'));
|
||||
|
||||
@@ -48,4 +48,6 @@ test('pages do not load scripts for unavailable business APIs', () => {
|
||||
});
|
||||
assert.equal(read('profile-generation.html').includes('src="public/js/generation-pages.js"'), true, 'profile-generation.html must load generation-pages.js');
|
||||
assert.equal(read('profile-tree.html').includes('src="public/js/lineage-pages.js"'), true, 'profile-tree.html must load lineage-pages.js');
|
||||
assert.equal(read('profile-relative.html').includes('src="public/js/relative-pages.js"'), true, 'profile-relative.html must load relative-pages.js');
|
||||
assert.equal(read('profile-memo.html').includes('src="public/js/memo-pages.js"'), true, 'profile-memo.html must load memo-pages.js');
|
||||
});
|
||||
|
||||
@@ -23,13 +23,8 @@ const contentPendingPages = [
|
||||
'profile-video.html',
|
||||
'profile-gift.html',
|
||||
'profile-gift-edit.html',
|
||||
'profile-growth.html',
|
||||
'profile-growth-edit.html',
|
||||
'profile-memo.html',
|
||||
'profile-memo-edit.html',
|
||||
'profile-merit.html',
|
||||
'profile-merit-edit.html',
|
||||
'profile-messages.html',
|
||||
'profile-feedback.html',
|
||||
'profile-admin-permissions.html',
|
||||
'profile-data-reminders.html'
|
||||
@@ -70,11 +65,18 @@ test('待开发页面显式加载状态脚本', () => {
|
||||
});
|
||||
|
||||
test('已接入的家谱业务页面保持真实功能状态', () => {
|
||||
['profile-feed.html', 'profile-feed-edit.html', 'profile-generation.html', 'profile-tree.html'].forEach((page) => {
|
||||
['profile-feed.html', 'profile-feed-edit.html', 'profile-generation.html', 'profile-tree.html', 'profile-growth.html', 'profile-growth-edit.html', 'profile-relative.html', 'profile-relative-edit.html', 'profile-memo.html', 'profile-memo-edit.html', 'profile-messages.html'].forEach((page) => {
|
||||
assert.doesNotMatch(read(page), /data-feature-status="pending"/, `${page} 不应标记为待开发`);
|
||||
});
|
||||
assert.match(read('profile-generation.html'), /src="public\/js\/generation-pages\.js"/);
|
||||
assert.match(read('profile-tree.html'), /src="public\/js\/lineage-pages\.js"/);
|
||||
assert.match(read('profile-growth.html'), /src="public\/js\/growth-pages\.js"/);
|
||||
assert.match(read('profile-growth-edit.html'), /src="public\/js\/growth-pages\.js"/);
|
||||
assert.match(read('profile-relative.html'), /src="public\/js\/relative-pages\.js"/);
|
||||
assert.match(read('profile-relative-edit.html'), /src="public\/js\/relative-pages\.js"/);
|
||||
assert.match(read('profile-memo.html'), /src="public\/js\/memo-pages\.js"/);
|
||||
assert.match(read('profile-messages.html'), /src="public\/js\/notification-pages\.js"/);
|
||||
assert.match(read('profile-memo-edit.html'), /src="public\/js\/memo-pages\.js"/);
|
||||
});
|
||||
|
||||
test('待开发的家谱管理页明确放行已接入的字辈入口并透传上下文', () => {
|
||||
@@ -94,6 +96,9 @@ test('待开发页面仅放行显式标记的可用功能入口', () => {
|
||||
|
||||
test('内容入口明确保留家族圈动态跳转', () => {
|
||||
assert.match(read('profile-content.html'), /href="profile-feed\.html" data-feature-link="available"/);
|
||||
assert.match(read('profile-content.html'), /href="profile-growth\.html" data-feature-link="available" data-genealogy-context-link/);
|
||||
assert.match(read('profile-content.html'), /href="profile-relative\.html" data-feature-link="available" data-genealogy-context-link/);
|
||||
assert.match(read('profile-content.html'), /href="profile-memo\.html" data-feature-link="available" data-genealogy-context-link/);
|
||||
});
|
||||
|
||||
test('家谱主页明确保留家族圈动态跳转', () => {
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
const assert = require('node:assert/strict');
|
||||
const test = require('node:test');
|
||||
|
||||
const RelativePages = require('../public/js/relative-pages.js');
|
||||
|
||||
test('亲友往来页只从 URL 读取真实家谱编号', () => {
|
||||
assert.equal(RelativePages.getCurrentGenealogyId('?genealogyId=900001001'), '900001001');
|
||||
assert.equal(RelativePages.getCurrentGenealogyId(''), '');
|
||||
});
|
||||
|
||||
test('亲友往来写入只构造 Apifox RelativeRecordBody 字段', () => {
|
||||
assert.deepEqual(RelativePages.buildRelativeRecordBody({
|
||||
relativeName: ' 王叔 ', relationName: '长辈', eventName: '寿宴', eventTime: '2026-07-24T09:00:00+08:00',
|
||||
giftAmount: '500.5', recordContent: ' 出席 ', mediaOssIds: '101,102', sortOrder: '3', status: 'enabled', memoTitle: '旧字段'
|
||||
}), { relativeName: '王叔', relationName: '长辈', eventName: '寿宴', eventTime: '2026-07-24T09:00:00+08:00', giftAmount: 500.5, recordContent: '出席', mediaOssIds: '101,102', status: 'enabled', sortOrder: 3 });
|
||||
});
|
||||
|
||||
test('亲友往来校验必填姓名、金额、附件和排序值', () => {
|
||||
assert.equal(RelativePages.validateRelativeRecordBody({ relativeName: '' }), '请填写亲友姓名');
|
||||
assert.equal(RelativePages.validateRelativeRecordBody(RelativePages.buildRelativeRecordBody({ relativeName: '王叔', giftAmount: '一百' })), '礼金金额必须是数字');
|
||||
assert.equal(RelativePages.validateRelativeRecordBody({ relativeName: '王叔', mediaOssIds: '101, 102' }), '附件 OSS ID 请使用英文逗号分隔的正整数');
|
||||
assert.equal(RelativePages.validateRelativeRecordBody(RelativePages.buildRelativeRecordBody({ relativeName: '王叔', sortOrder: '1.5' })), '排序值必须是安全整数');
|
||||
});
|
||||
@@ -6,9 +6,9 @@ const test = require('node:test');
|
||||
const SecurityPages = require('../public/js/security-pages.js');
|
||||
const UploadPages = require('../public/js/upload-pages.js');
|
||||
|
||||
test('security forms use SMS captcha scenes and submit only documented business fields', () => {
|
||||
assert.equal(SecurityPages.getCaptchaScene('phone'), 'PC_PHONE_CHANGE');
|
||||
assert.equal(SecurityPages.getCaptchaScene('deactivate'), 'PC_ACCOUNT_DEACTIVATE');
|
||||
test('security forms use PC verification operation codes and submit only documented business fields', () => {
|
||||
assert.equal(SecurityPages.getCaptchaOperation('phone'), 'phone-change');
|
||||
assert.equal(SecurityPages.getCaptchaOperation('deactivate'), 'account-deactivate');
|
||||
assert.deepEqual(
|
||||
SecurityPages.buildPasswordChangeBody({
|
||||
oldPassword: 'old',
|
||||
@@ -34,10 +34,12 @@ test('security forms use SMS captcha scenes and submit only documented business
|
||||
assert.deepEqual(SecurityPages.buildDeactivateBody({ smsCode: '654321' }), { smsCode: '654321' });
|
||||
});
|
||||
|
||||
test('upload helper exposes only single-file upload support', () => {
|
||||
assert.equal('uploadResumable' in UploadPages, false);
|
||||
assert.equal('buildResumableInitBody' in UploadPages, false);
|
||||
assert.equal(UploadPages.getUploadMode(5 * 1024 * 1024), 'single');
|
||||
test('upload helper keeps the avatar flow blocked until the current PC resumable response is defined', async () => {
|
||||
assert.equal(UploadPages.getUploadMode(5 * 1024 * 1024), 'resumable');
|
||||
await assert.rejects(
|
||||
UploadPages.uploadFileForPage({ initResumableUpload() {} }, { name: 'avatar.png' }),
|
||||
/当前 PC 文件上传响应未定义头像回填字段/
|
||||
);
|
||||
});
|
||||
|
||||
test('security page field names match phone-change and account-deactivation DTOs', () => {
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const test = require('node:test');
|
||||
|
||||
const root = path.resolve(__dirname, '..');
|
||||
const editorPages = [
|
||||
'profile-article-edit.html',
|
||||
'profile-feed-edit.html',
|
||||
'profile-gift-edit.html',
|
||||
'profile-growth-edit.html',
|
||||
'profile-merit-edit.html'
|
||||
];
|
||||
|
||||
function read(relativePath) {
|
||||
return fs.readFileSync(path.join(root, relativePath), 'utf8');
|
||||
}
|
||||
|
||||
test('所有富文本页面统一加载 wangEditor v5', () => {
|
||||
editorPages.forEach((page) => {
|
||||
const source = read(page);
|
||||
|
||||
assert.match(source, /public\/js\/wangeditor5\/css\/style\.css/);
|
||||
assert.match(source, /public\/js\/wangeditor5\/index\.js/);
|
||||
assert.match(source, /class="js-rich-editor"/);
|
||||
assert.doesNotMatch(source, /kindeditor|public\/js\/ke\//i);
|
||||
});
|
||||
});
|
||||
|
||||
test('公共富文本适配层只使用 wangEditor 并在提交前同步 textarea', () => {
|
||||
const source = read('public/js/rich-editor.js');
|
||||
|
||||
assert.match(source, /root\.wangEditor/);
|
||||
assert.match(source, /wangEditor\.createEditor/);
|
||||
assert.match(source, /wangEditor\.createToolbar/);
|
||||
assert.match(source, /documentRef\.addEventListener\('submit'/);
|
||||
assert.doesNotMatch(source, /KindEditor/);
|
||||
});
|
||||
|
||||
test('旧 KindEditor 资源已移除', () => {
|
||||
assert.equal(fs.existsSync(path.join(root, 'public/js/ke/kindeditor.min.js')), false);
|
||||
});
|
||||
Reference in New Issue
Block a user