From 6368ee376f2504b958454b73f6cef12360eb76d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E8=87=AA=E8=BE=BE?= <980324341@qq.com> Date: Sat, 5 Sep 2026 16:51:13 +0800 Subject: [PATCH] =?UTF-8?q?task-96(=E5=BA=97=E9=93=BA=E4=B8=AD=E5=BF=83):?= =?UTF-8?q?=20=E5=AE=9E=E7=8E=B0=E5=BA=97=E9=93=BA=E8=B4=A6=E5=8F=B7?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增 shop-manage-field-validators.ts:分组/店铺名/商城/账号/密码逐字段校验 与汇总,错误文案与后端 NotBlank/Size 规则一致。 TDD: task-96.test.ts 8 用例先 RED 后 GREEN。 --- .../shop/shop-manage-field-validators.ts | 54 +++++++++++++++ admin-frontend-vue/tests/task-96.test.ts | 69 +++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 admin-frontend-vue/src/pages/shop/shop-manage-field-validators.ts create mode 100644 admin-frontend-vue/tests/task-96.test.ts diff --git a/admin-frontend-vue/src/pages/shop/shop-manage-field-validators.ts b/admin-frontend-vue/src/pages/shop/shop-manage-field-validators.ts new file mode 100644 index 00000000..bbf4d799 --- /dev/null +++ b/admin-frontend-vue/src/pages/shop/shop-manage-field-validators.ts @@ -0,0 +1,54 @@ +/** 店铺中心逐字段校验(任务 96):账号/密码等店铺字段可独立校验,错误文案与后端 NotBlank/Size 一致;纯逻辑。 */ +import type { ShopManageFormErrors, ShopManageFormValues } from './shop-manage-form-model.ts' + +export const SHOP_MANAGE_ZN_USERNAME_MAX_LEN = 128 + +function requiredText(value: string): string { + return (value || '').trim() +} + +export function validateGroupId(value: number | null): string | undefined { + return typeof value === 'number' && Number.isFinite(value) && value >= 1 ? undefined : '分组不能为空' +} + +export function validateShopNameValue(value: string): string | undefined { + return requiredText(value) ? undefined : '店铺名称不能为空' +} + +export function validateMallNameValue(value: string): string | undefined { + return requiredText(value) ? undefined : '商城名称不能为空' +} + +export function validateAccount(value: string): string | undefined { + return requiredText(value) ? undefined : '账号不能为空' +} + +export function validatePassword(value: string): string | undefined { + return requiredText(value) ? undefined : '密码不能为空' +} + +export function validateZnUsernameLength(value: string): string | undefined { + const trimmed = (value || '').trim() + if (trimmed.length > SHOP_MANAGE_ZN_USERNAME_MAX_LEN) { + return `自动化账号长度不能超过${SHOP_MANAGE_ZN_USERNAME_MAX_LEN}个字符` + } + return undefined +} + +/** 汇总逐字段错误(供编辑/新增表单即时反馈)。 */ +export function collectShopManageFieldErrors(form: ShopManageFormValues): ShopManageFormErrors { + const errors: ShopManageFormErrors = {} + const groupError = validateGroupId(form.groupId) + if (groupError) errors.groupId = groupError + const nameError = validateShopNameValue(form.shopName) + if (nameError) errors.shopName = nameError + const mallError = validateMallNameValue(form.mallName) + if (mallError) errors.mallName = mallError + const accountError = validateAccount(form.account) + if (accountError) errors.account = accountError + const passwordError = validatePassword(form.password) + if (passwordError) errors.password = passwordError + const znError = validateZnUsernameLength(form.znUsername) + if (znError) errors.znUsername = znError + return errors +} diff --git a/admin-frontend-vue/tests/task-96.test.ts b/admin-frontend-vue/tests/task-96.test.ts new file mode 100644 index 00000000..962bd96d --- /dev/null +++ b/admin-frontend-vue/tests/task-96.test.ts @@ -0,0 +1,69 @@ +import test from 'node:test' +import assert from 'node:assert/strict' +import { readSource } from './helpers.ts' +import { + collectShopManageFieldErrors, + validateAccount, + validateGroupId, + validateMallNameValue, + validatePassword, + validateShopNameValue, + validateZnUsernameLength, +} from '../src/pages/shop/shop-manage-field-validators.ts' + +test('test_task_096_shop_field_validators_normal_primary_path', () => { + // 正常主路径:合法账号/密码字段无错误。 + assert.equal(validateAccount('sale@a.com'), undefined) + assert.equal(validatePassword('p@ss'), undefined) + assert.equal(validateShopNameValue('BlueWave'), undefined) + assert.equal(validateGroupId(3), undefined) +}) + +test('test_task_096_shop_field_validators_normal_variant_input', () => { + // 正常变体:可选自动化账号超长报错,其余字段各自独立。 + assert.equal(validateZnUsernameLength('x'.repeat(128)), undefined) + assert.match(validateZnUsernameLength('y'.repeat(129)) || '', /128/) + assert.equal(validateMallNameValue(' '), '商城名称不能为空') +}) + +test('test_task_096_shop_field_validators_repeated_is_idempotent', () => { + // 正常重复:逐字段校验结果稳定、不改输入。 + assert.equal(validateAccount('a@b.c'), validateAccount('a@b.c')) + const form = { groupId: 2, shopName: 'S', mallName: 'M', znUsername: '', account: 'x', password: 'y' } + assert.deepEqual(collectShopManageFieldErrors(form), collectShopManageFieldErrors(form)) + assert.deepEqual(form, { groupId: 2, shopName: 'S', mallName: 'M', znUsername: '', account: 'x', password: 'y' }) +}) + +test('test_task_096_shop_field_validators_boundary_empty_input', () => { + // 边界空值:空白账号/密码返回必填错误。 + assert.equal(validateAccount(' '), '账号不能为空') + assert.equal(validatePassword(''), '密码不能为空') +}) + +test('test_task_096_shop_field_validators_boundary_single_item', () => { + // 边界单元素:单字段错误可独立定位。 + const errors = collectShopManageFieldErrors({ groupId: null, shopName: 'S', mallName: 'M', znUsername: '', account: 'a', password: 'p' }) + assert.equal(errors.groupId, '分组不能为空') + assert.equal('shopName' in errors, false) +}) + +test('test_task_096_shop_field_validators_boundary_limit_or_missing_field', () => { + // 边界上限/缺字段:超长自动化账号/空白商城被定位;长度上限边界放行。 + assert.equal(validateZnUsernameLength(''), undefined) + assert.equal(validateZnUsernameLength('a'.repeat(128)), undefined) + assert.match(validateZnUsernameLength('b'.repeat(129)) || '', /128/) +}) + +test('test_task_096_shop_field_validators_invalid_input_rejected', () => { + // 异常输入:缺分组/空白店铺名均返回可操作错误。 + assert.equal(validateGroupId(null), '分组不能为空') + assert.equal(validateGroupId(-1), '分组不能为空') + assert.equal(validateShopNameValue(' '), '店铺名称不能为空') +}) + +test('test_task_096_shop_field_validators_dependency_failure_returns_actionable_message', () => { + // 依赖失败/可操作:字段校验纯逻辑,无框架/http;错误文案与后端 NotBlank 一致。 + const mod = readSource('src/pages/shop/shop-manage-field-validators.ts') + assert.equal(/axios|http\.|vue/.test(mod), false, '字段校验保持纯逻辑') + assert.match(mod, /不能为空/) +})