补齐平台订阅、外部登录、CRM 同步与用户端页面;Nginx 转发用户端接口,登录页使用 Google 与 Apple 标志。 Co-authored-by: Cursor <cursoragent@cursor.com>
572 行
20 KiB
TypeScript
572 行
20 KiB
TypeScript
/**
|
||
* OmniCard AI (CRMmp) - 名片主 NFC 贴卡激活测试套件 (TDD Red -> Green)
|
||
*
|
||
* 严格按照 AGENTS.md 3.3、4.3、5.1 规范与 PRD pages/owner/nfc/scan 设计编写
|
||
*/
|
||
|
||
import * as fs from 'fs';
|
||
import * as path from 'path';
|
||
|
||
const SCAN_PAGE = path.resolve(__dirname, '../../../mpuniapp/pages/owner/nfc/scan.uvue');
|
||
const SCAN_UTS = path.resolve(__dirname, '../../../mpuniapp/pages/owner/nfc/useScan.uts');
|
||
|
||
type NFCRecordFixture = {
|
||
id: string | number;
|
||
uid: string;
|
||
tenantId: string | number;
|
||
cardId?: string | number | null;
|
||
batchNo: string;
|
||
material: 'wood' | 'metal' | 'pvc';
|
||
status: 'active' | 'suspended' | 'unbound';
|
||
sceneMode: 'business' | 'social';
|
||
};
|
||
|
||
type ActivateByTapInput = {
|
||
uid: string;
|
||
cardId?: string | number | null;
|
||
deviceCardId?: string | number | null;
|
||
};
|
||
|
||
type ScanState = {
|
||
uid: string;
|
||
cardIdRaw: string;
|
||
deviceCardId: number | null;
|
||
submitting: boolean;
|
||
scanning: boolean;
|
||
manualInput: boolean;
|
||
errorMessage: string;
|
||
empty: boolean;
|
||
record: NFCRecordFixture | null;
|
||
};
|
||
|
||
type UseScanModule = {
|
||
parseUid: (raw: string | number | null | undefined) => string;
|
||
parseCardId: (raw: string | number | null | undefined) => number | null;
|
||
activateByTap: (
|
||
input: ActivateByTapInput,
|
||
existing?: { cardId?: string | number | null } | null
|
||
) => Promise<NFCRecordFixture>;
|
||
startScan: (state: ScanState) => boolean;
|
||
createScanState: () => ScanState;
|
||
buildNdefUri: (cardId: string | number | null | undefined) => string;
|
||
writeNdefToTag: (uri: string | null | undefined) => Promise<boolean>;
|
||
resolveScanText: (key: string) => string;
|
||
};
|
||
|
||
type NdefWriteOption = {
|
||
uris?: string[];
|
||
records?: unknown[];
|
||
success?: () => void;
|
||
fail?: (err: { errMsg?: string }) => void;
|
||
};
|
||
|
||
const WRITE_SUCCESS_TOAST_RE =
|
||
/已写入(?!失败)|写入成功|Written to tag|Write successful/i;
|
||
|
||
function assertNoWriteSuccessToast(toasts: UniToastOption[]): void {
|
||
expect(toasts.some(t => WRITE_SUCCESS_TOAST_RE.test(t.title))).toBe(false);
|
||
}
|
||
|
||
type UniToastOption = {
|
||
title: string;
|
||
icon?: string;
|
||
};
|
||
|
||
type UniRequestSuccessRes = {
|
||
statusCode: number;
|
||
data: unknown;
|
||
header: Record<string, string>;
|
||
};
|
||
|
||
type UniRequestOption = {
|
||
url: string;
|
||
method?: string;
|
||
data?: Record<string, unknown>;
|
||
success?: (res: UniRequestSuccessRes) => void;
|
||
fail?: (err: { errMsg: string }) => void;
|
||
complete?: () => void;
|
||
};
|
||
|
||
const EMOJI_RE = /[\u2600-\u27BF\uFE0F]|[\uD83C-\uDBFF][\uDC00-\uDFFF]/;
|
||
|
||
function buildNfcRecord(overrides?: Partial<NFCRecordFixture>): NFCRecordFixture {
|
||
return {
|
||
id: 41,
|
||
uid: '04AABBCCDD',
|
||
tenantId: 8,
|
||
cardId: 12,
|
||
batchNo: 'B202609',
|
||
material: 'pvc',
|
||
status: 'active',
|
||
sceneMode: 'business',
|
||
...overrides,
|
||
};
|
||
}
|
||
|
||
describe('OmniCard AI 名片主 NFC 贴卡激活业务逻辑测试 (useScan.uts)', () => {
|
||
let storageMap: Map<string, string>;
|
||
let toastCalls: UniToastOption[];
|
||
let lastRequestOptions: UniRequestOption | null;
|
||
let requestCallCount: number;
|
||
let writeNdefCalls: NdefWriteOption[];
|
||
let writeMode: 'success' | 'fail' | 'cancel' | 'noAdapter' | 'noWriteApi';
|
||
let mockRequestResponse: {
|
||
statusCode?: number;
|
||
data?: {
|
||
code?: number;
|
||
message?: string;
|
||
data?: unknown;
|
||
};
|
||
failError?: { errMsg: string };
|
||
};
|
||
|
||
beforeEach(() => {
|
||
storageMap = new Map();
|
||
toastCalls = [];
|
||
lastRequestOptions = null;
|
||
requestCallCount = 0;
|
||
writeNdefCalls = [];
|
||
writeMode = 'success';
|
||
mockRequestResponse = {
|
||
statusCode: 200,
|
||
data: {
|
||
code: 1000,
|
||
message: 'success',
|
||
data: buildNfcRecord(),
|
||
},
|
||
};
|
||
jest.resetModules();
|
||
|
||
(global as { uni?: Record<string, unknown> }).uni = {
|
||
getStorageSync: (key: string) => storageMap.get(key) || '',
|
||
setStorageSync: (key: string, val: string) => storageMap.set(key, val),
|
||
removeStorageSync: (key: string) => storageMap.delete(key),
|
||
showToast: (options: UniToastOption) => {
|
||
toastCalls.push(options);
|
||
},
|
||
reLaunch: jest.fn(),
|
||
getLocale: () => 'zh-CN',
|
||
getNFCAdapter: () => {
|
||
if (writeMode === 'noAdapter') {
|
||
return null;
|
||
}
|
||
return {
|
||
startDiscovery: (opts: {
|
||
success?: () => void;
|
||
fail?: (err: { errMsg?: string }) => void;
|
||
}) => {
|
||
opts.fail?.({ errMsg: 'startDiscovery:fail' });
|
||
},
|
||
onDiscovered: () => {},
|
||
getNdef: () => {
|
||
if (writeMode === 'noWriteApi') {
|
||
return null;
|
||
}
|
||
return {
|
||
writeNdefMessage: (opts: NdefWriteOption) => {
|
||
writeNdefCalls.push(opts);
|
||
if (writeMode === 'success') {
|
||
opts.success?.();
|
||
return;
|
||
}
|
||
if (writeMode === 'cancel') {
|
||
opts.fail?.({ errMsg: 'writeNdefMessage:fail cancel' });
|
||
return;
|
||
}
|
||
opts.fail?.({ errMsg: 'writeNdefMessage:fail' });
|
||
},
|
||
};
|
||
},
|
||
};
|
||
},
|
||
request: (options: UniRequestOption) => {
|
||
requestCallCount += 1;
|
||
lastRequestOptions = options;
|
||
setTimeout(() => {
|
||
if (mockRequestResponse.failError) {
|
||
options.fail?.(mockRequestResponse.failError);
|
||
} else {
|
||
options.success?.({
|
||
statusCode: mockRequestOptionsStatus(),
|
||
data: mockRequestResponse.data,
|
||
header: {},
|
||
});
|
||
}
|
||
options.complete?.();
|
||
}, 0);
|
||
return {
|
||
abort: jest.fn(),
|
||
};
|
||
},
|
||
};
|
||
});
|
||
|
||
afterEach(() => {
|
||
delete (global as { uni?: Record<string, unknown> }).uni;
|
||
});
|
||
|
||
function mockRequestOptionsStatus(): number {
|
||
return mockRequestResponse.statusCode != null ? mockRequestResponse.statusCode : 200;
|
||
}
|
||
|
||
function loadUseScan(): UseScanModule {
|
||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||
return require('../../../mpuniapp/pages/owner/nfc/useScan.uts') as UseScanModule;
|
||
}
|
||
|
||
describe('1. parseUid 去空且非空才合法', () => {
|
||
it('1.1 空白与空串解析为空,视为非法', () => {
|
||
const { parseUid } = loadUseScan();
|
||
|
||
expect(parseUid('')).toBe('');
|
||
expect(parseUid(' ')).toBe('');
|
||
expect(parseUid(null)).toBe('');
|
||
expect(parseUid(undefined)).toBe('');
|
||
});
|
||
|
||
it('1.2 两端空白被去掉,非空结果合法', () => {
|
||
const { parseUid } = loadUseScan();
|
||
|
||
expect(parseUid(' 04:AA:BB:CC ')).toBe('04:AA:BB:CC');
|
||
expect(parseUid('04AABBCCDD')).toBe('04AABBCCDD');
|
||
expect(parseUid(4012)).toBe('4012');
|
||
});
|
||
});
|
||
|
||
describe('2. parseCardId 仅正整数合法', () => {
|
||
it('2.1 空值、零、负数、小数、非数字均为非法', () => {
|
||
const { parseCardId } = loadUseScan();
|
||
|
||
expect(parseCardId('')).toBeNull();
|
||
expect(parseCardId(' ')).toBeNull();
|
||
expect(parseCardId(null)).toBeNull();
|
||
expect(parseCardId(undefined)).toBeNull();
|
||
expect(parseCardId(0)).toBeNull();
|
||
expect(parseCardId('0')).toBeNull();
|
||
expect(parseCardId(-1)).toBeNull();
|
||
expect(parseCardId('-8')).toBeNull();
|
||
expect(parseCardId(1.5)).toBeNull();
|
||
expect(parseCardId('12.3')).toBeNull();
|
||
expect(parseCardId('abc')).toBeNull();
|
||
});
|
||
|
||
it('2.2 正整数字符串与数字解析为 number', () => {
|
||
const { parseCardId } = loadUseScan();
|
||
|
||
expect(parseCardId(12)).toBe(12);
|
||
expect(parseCardId('12')).toBe(12);
|
||
expect(parseCardId(' 8 ')).toBe(8);
|
||
});
|
||
});
|
||
|
||
describe('3. activateByTap 调用 bind 或 activate', () => {
|
||
it('3.1 入参 cardId 合法时调用 bindNfcDevice', async () => {
|
||
const { activateByTap } = loadUseScan();
|
||
|
||
const record = await activateByTap({ uid: ' 04AABBCCDD ', cardId: '12' });
|
||
|
||
expect(requestCallCount).toBe(1);
|
||
expect(lastRequestOptions).not.toBeNull();
|
||
expect(lastRequestOptions?.method).toBe('POST');
|
||
expect(lastRequestOptions?.url).toContain('/admin/nfc/bind/bind');
|
||
expect(lastRequestOptions?.data?.uid).toBe('04AABBCCDD');
|
||
expect(lastRequestOptions?.data?.cardId).toBe(12);
|
||
expect(record.uid).toBe('04AABBCCDD');
|
||
expect(record.cardId).toBe(12);
|
||
expect(record.status).toBe('active');
|
||
});
|
||
|
||
it('3.2 设备已有 cardId 且入参 cardId 空时调用 activateNfcDevice', async () => {
|
||
const { activateByTap } = loadUseScan();
|
||
mockRequestResponse.data = {
|
||
code: 1000,
|
||
message: 'success',
|
||
data: buildNfcRecord({ cardId: 88 }),
|
||
};
|
||
|
||
const record = await activateByTap(
|
||
{ uid: '04AABBCCDD', cardId: '' },
|
||
{ cardId: 88 }
|
||
);
|
||
|
||
expect(requestCallCount).toBe(1);
|
||
expect(lastRequestOptions).not.toBeNull();
|
||
expect(lastRequestOptions?.method).toBe('POST');
|
||
expect(lastRequestOptions?.url).toContain('/admin/nfc/bind/activate');
|
||
expect(lastRequestOptions?.data?.uid).toBe('04AABBCCDD');
|
||
expect(lastRequestOptions?.data?.cardId).toBeUndefined();
|
||
expect(record.cardId).toBe(88);
|
||
});
|
||
|
||
it('3.3 入参带 deviceCardId 且 cardId 空时走 activate', async () => {
|
||
const { activateByTap } = loadUseScan();
|
||
|
||
await activateByTap({ uid: '04AABBCCDD', cardId: null, deviceCardId: 5 });
|
||
|
||
expect(requestCallCount).toBe(1);
|
||
expect(lastRequestOptions?.url).toContain('/admin/nfc/bind/activate');
|
||
});
|
||
|
||
it('3.4 空 UID 不发请求并抛出错误信息', async () => {
|
||
const { activateByTap } = loadUseScan();
|
||
|
||
await expect(activateByTap({ uid: ' ', cardId: 12 })).rejects.toThrow();
|
||
expect(requestCallCount).toBe(0);
|
||
});
|
||
|
||
it('3.5 无入参 cardId 且设备亦无 cardId 时不发请求并抛错', async () => {
|
||
const { activateByTap } = loadUseScan();
|
||
|
||
await expect(activateByTap({ uid: '04AABBCCDD', cardId: '' })).rejects.toThrow();
|
||
expect(requestCallCount).toBe(0);
|
||
});
|
||
|
||
it('3.6 接口失败时抛出服务端错误信息', async () => {
|
||
const { activateByTap } = loadUseScan();
|
||
mockRequestResponse.data = {
|
||
code: 1001,
|
||
message: '设备未在台账中',
|
||
data: null,
|
||
};
|
||
|
||
await expect(activateByTap({ uid: '04AABBCCDD', cardId: 12 })).rejects.toThrow(
|
||
'设备未在台账中'
|
||
);
|
||
});
|
||
});
|
||
|
||
describe('4. startScan 不伪造成功', () => {
|
||
it('4.1 天线 API 不可用时失败回退手动输入,不伪造成功、不发激活请求', () => {
|
||
const { createScanState, startScan } = loadUseScan();
|
||
const state = createScanState();
|
||
const uidBefore = state.uid;
|
||
|
||
const ok = startScan(state);
|
||
|
||
expect(ok).toBe(false);
|
||
expect(state.manualInput).toBe(true);
|
||
expect(state.scanning).toBe(false);
|
||
expect(state.record).toBeNull();
|
||
expect(state.uid).toBe(uidBefore);
|
||
expect(state.errorMessage.length).toBeGreaterThan(0);
|
||
expect(requestCallCount).toBe(0);
|
||
});
|
||
|
||
it('4.2 源码契约:APP 块尝试官方 getNFCAdapter 读 UID,成功仅在拿到合法 UID', () => {
|
||
const logicSource = fs.readFileSync(SCAN_UTS, 'utf8');
|
||
const startScanBlock = logicSource.slice(
|
||
logicSource.indexOf('export function startScan'),
|
||
logicSource.indexOf('export function resolveStatusLine')
|
||
);
|
||
|
||
expect(logicSource.includes('#ifdef APP')).toBe(true);
|
||
expect(logicSource.includes('getNFCAdapter')).toBe(true);
|
||
expect(logicSource.includes('startDiscovery') || logicSource.includes('onDiscovered')).toBe(
|
||
true
|
||
);
|
||
expect(startScanBlock.includes('parseUid')).toBe(true);
|
||
expect(startScanBlock.includes('state.uid')).toBe(true);
|
||
expect(startScanBlock.includes('return true')).toBe(true);
|
||
expect(startScanBlock.includes('manualInput = true')).toBe(true);
|
||
expect(startScanBlock.includes('return false')).toBe(true);
|
||
// 禁止在 startScan 内硬编码伪造芯片 UID
|
||
expect(/state\.uid\s*=\s*['"][0-9A-Fa-f:]+['"]/.test(startScanBlock)).toBe(false);
|
||
expect(startScanBlock.includes('Native NFC antenna is not wired')).toBe(false);
|
||
});
|
||
});
|
||
|
||
describe('5. writeNdefToTag 仅系统写入 API 成功才 toast 已写入', () => {
|
||
it('5.1 buildNdefUri 无合法 cardId 时返回空串', () => {
|
||
const { buildNdefUri } = loadUseScan();
|
||
|
||
expect(buildNdefUri(null)).toBe('');
|
||
expect(buildNdefUri(undefined)).toBe('');
|
||
expect(buildNdefUri('')).toBe('');
|
||
expect(buildNdefUri(0)).toBe('');
|
||
expect(buildNdefUri(-1)).toBe('');
|
||
expect(buildNdefUri(12)).toBe('https://omnicard.ai/c/12');
|
||
expect(buildNdefUri(' 8 ')).toBe('https://omnicard.ai/c/8');
|
||
});
|
||
|
||
it('5.2 写入 API 成功才 toast 已写入', async () => {
|
||
const { writeNdefToTag, resolveScanText } = loadUseScan();
|
||
toastCalls = [];
|
||
writeMode = 'success';
|
||
|
||
const ok = await writeNdefToTag('https://omnicard.ai/c/12');
|
||
|
||
expect(ok).toBe(true);
|
||
expect(writeNdefCalls.length).toBe(1);
|
||
expect(writeNdefCalls[0].uris).toEqual(['https://omnicard.ai/c/12']);
|
||
expect(
|
||
toastCalls.some(t => t.title === resolveScanText('writeSuccess'))
|
||
).toBe(true);
|
||
expect(WRITE_SUCCESS_TOAST_RE.test(resolveScanText('writeSuccess'))).toBe(
|
||
true
|
||
);
|
||
});
|
||
|
||
it('5.3 无可写内容不调写入 API,不 toast 已写入', async () => {
|
||
const { writeNdefToTag, resolveScanText } = loadUseScan();
|
||
toastCalls = [];
|
||
|
||
const ok = await writeNdefToTag(' ');
|
||
|
||
expect(ok).toBe(false);
|
||
expect(writeNdefCalls.length).toBe(0);
|
||
assertNoWriteSuccessToast(toastCalls);
|
||
expect(
|
||
toastCalls.some(t => t.title === resolveScanText('writeUnavailable'))
|
||
).toBe(true);
|
||
});
|
||
|
||
it('5.4 天线/Adapter 不可用时不 toast 已写入', async () => {
|
||
const { writeNdefToTag, resolveScanText } = loadUseScan();
|
||
writeMode = 'noAdapter';
|
||
toastCalls = [];
|
||
|
||
const ok = await writeNdefToTag('https://omnicard.ai/c/12');
|
||
|
||
expect(ok).toBe(false);
|
||
expect(writeNdefCalls.length).toBe(0);
|
||
assertNoWriteSuccessToast(toastCalls);
|
||
expect(
|
||
toastCalls.some(t => t.title === resolveScanText('writeFailed'))
|
||
).toBe(true);
|
||
});
|
||
|
||
it('5.5 无 writeNdefMessage 能力时不 toast 已写入', async () => {
|
||
const { writeNdefToTag, resolveScanText } = loadUseScan();
|
||
writeMode = 'noWriteApi';
|
||
toastCalls = [];
|
||
|
||
const ok = await writeNdefToTag('https://omnicard.ai/c/12');
|
||
|
||
expect(ok).toBe(false);
|
||
expect(writeNdefCalls.length).toBe(0);
|
||
assertNoWriteSuccessToast(toastCalls);
|
||
expect(
|
||
toastCalls.some(t => t.title === resolveScanText('writeFailed'))
|
||
).toBe(true);
|
||
});
|
||
|
||
it('5.6 用户取消写入不 toast 已写入', async () => {
|
||
const { writeNdefToTag, resolveScanText } = loadUseScan();
|
||
writeMode = 'cancel';
|
||
toastCalls = [];
|
||
|
||
const ok = await writeNdefToTag('https://omnicard.ai/c/12');
|
||
|
||
expect(ok).toBe(false);
|
||
expect(writeNdefCalls.length).toBe(1);
|
||
assertNoWriteSuccessToast(toastCalls);
|
||
expect(
|
||
toastCalls.some(t => t.title === resolveScanText('writeCancelled'))
|
||
).toBe(true);
|
||
});
|
||
|
||
it('5.7 写入失败不 toast 已写入', async () => {
|
||
const { writeNdefToTag, resolveScanText } = loadUseScan();
|
||
writeMode = 'fail';
|
||
toastCalls = [];
|
||
|
||
const ok = await writeNdefToTag('https://omnicard.ai/c/12');
|
||
|
||
expect(ok).toBe(false);
|
||
expect(writeNdefCalls.length).toBe(1);
|
||
assertNoWriteSuccessToast(toastCalls);
|
||
expect(
|
||
toastCalls.some(t => t.title === resolveScanText('writeFailed'))
|
||
).toBe(true);
|
||
});
|
||
|
||
it('5.8 源码契约:writeSuccess 仅出现在 writeNdefMessage 的 success 回调内', () => {
|
||
const logicSource = fs.readFileSync(SCAN_UTS, 'utf8');
|
||
const writeIdx = logicSource.indexOf('ndef.writeNdefMessage(');
|
||
expect(writeIdx).toBeGreaterThan(-1);
|
||
|
||
const successIdx = logicSource.indexOf('success:', writeIdx);
|
||
expect(successIdx).toBeGreaterThan(writeIdx);
|
||
const successBody = logicSource.slice(successIdx, successIdx + 220);
|
||
expect(successBody.includes('writeSuccess')).toBe(true);
|
||
|
||
const failIdx = logicSource.indexOf('fail:', successIdx);
|
||
expect(failIdx).toBeGreaterThan(successIdx);
|
||
const failBody = logicSource.slice(failIdx, failIdx + 360);
|
||
expect(failBody.includes('writeSuccess')).toBe(false);
|
||
expect(WRITE_SUCCESS_TOAST_RE.test(failBody)).toBe(false);
|
||
expect(
|
||
failBody.includes('writeFailed') ||
|
||
failBody.includes('writeCancelled') ||
|
||
failBody.includes('toastWriteFailed') ||
|
||
failBody.includes('toastWriteCancelled')
|
||
).toBe(true);
|
||
});
|
||
});
|
||
});
|
||
|
||
describe('OmniCard AI 名片主 NFC 贴卡激活页契约 (scan.uvue)', () => {
|
||
it('页面存在且引用 bindNfcDevice 或 activateByTap', () => {
|
||
expect(fs.existsSync(SCAN_PAGE)).toBe(true);
|
||
expect(fs.existsSync(SCAN_UTS)).toBe(true);
|
||
|
||
const pageSource = fs.readFileSync(SCAN_PAGE, 'utf8');
|
||
const logicSource = fs.readFileSync(SCAN_UTS, 'utf8');
|
||
const combined = `${pageSource}\n${logicSource}`;
|
||
|
||
expect(combined.includes('bindNfcDevice') || combined.includes('activateByTap')).toBe(
|
||
true
|
||
);
|
||
expect(pageSource.includes('script setup lang="uts"')).toBe(true);
|
||
expect(pageSource.includes('<view')).toBe(true);
|
||
expect(pageSource.includes('<text')).toBe(true);
|
||
expect(pageSource.includes('<input')).toBe(true);
|
||
expect(pageSource.includes('<button')).toBe(true);
|
||
expect(pageSource.includes('getOmniText')).toBe(true);
|
||
expect(pageSource.includes('#0B5CFF')).toBe(true);
|
||
expect(pageSource.includes('#F7F8FA')).toBe(true);
|
||
expect(pageSource.includes('#101828')).toBe(true);
|
||
expect(pageSource.includes('#667085')).toBe(true);
|
||
expect(pageSource.includes('#E3E7ED')).toBe(true);
|
||
});
|
||
|
||
it('无 emoji、无 any,模板无硬编码中文主文案', () => {
|
||
const pageSource = fs.readFileSync(SCAN_PAGE, 'utf8');
|
||
const logicSource = fs.readFileSync(SCAN_UTS, 'utf8');
|
||
const testSource = fs.readFileSync(__filename, 'utf8');
|
||
const template = pageSource.split('<script')[0];
|
||
|
||
expect(EMOJI_RE.test(pageSource)).toBe(false);
|
||
expect(EMOJI_RE.test(logicSource)).toBe(false);
|
||
expect(EMOJI_RE.test(testSource)).toBe(false);
|
||
expect(pageSource.includes(': any') || pageSource.includes(' as any')).toBe(false);
|
||
expect(logicSource.includes(': any') || logicSource.includes(' as any')).toBe(false);
|
||
expect(/[\u4e00-\u9fff]/.test(template)).toBe(false);
|
||
expect(pageSource.includes('innerAudioContext') || pageSource.includes('autoplay')).toBe(
|
||
false
|
||
);
|
||
});
|
||
|
||
it('主流程走 writeNdefToTag,失败路径无已写入文案,不把云端绑定冒充芯片写入', () => {
|
||
const pageSource = fs.readFileSync(SCAN_PAGE, 'utf8');
|
||
const logicSource = fs.readFileSync(SCAN_UTS, 'utf8');
|
||
|
||
expect(logicSource.includes('writeNdefToTag')).toBe(true);
|
||
expect(logicSource.includes('writeNdefMessage')).toBe(true);
|
||
expect(pageSource.includes('writeNdefToTag')).toBe(true);
|
||
expect(pageSource.includes('buildNdefUri')).toBe(true);
|
||
|
||
expect(pageSource.includes('已写入')).toBe(false);
|
||
expect(pageSource.includes('烧录至')).toBe(false);
|
||
expect(WRITE_SUCCESS_TOAST_RE.test(pageSource)).toBe(false);
|
||
|
||
const unavailableIdx = logicSource.indexOf("resolveScanText('writeUnavailable')");
|
||
expect(unavailableIdx).toBeGreaterThan(-1);
|
||
const unavailableSlice = logicSource.slice(
|
||
Math.max(0, unavailableIdx - 80),
|
||
unavailableIdx + 80
|
||
);
|
||
expect(WRITE_SUCCESS_TOAST_RE.test(unavailableSlice)).toBe(false);
|
||
});
|
||
});
|