|
|
@@ -0,0 +1,118 @@
|
|
|
+using Xunit;
|
|
|
+
|
|
|
+namespace Admin.NET.Plugin.AiDOP.Tests.ApprovalFlow;
|
|
|
+
|
|
|
+/// <summary>
|
|
|
+/// 审批中心 D-07 源码契约测试:「业务类型」列展示中文 Name,但 <b>Code 仍是唯一机器契约</b>。
|
|
|
+///
|
|
|
+/// 核心边界:
|
|
|
+/// bizType(Code)= 路由跳转 / 按钮显隐 / 筛选 value / 后端查询 —— 不得被中文化;
|
|
|
+/// bizTypeName = 仅展示,取自 ApprovalBizType.Name(全局配置表,无 TenantId)。
|
|
|
+/// </summary>
|
|
|
+public class ApprovalCenterBizTypeDisplayContractTests
|
|
|
+{
|
|
|
+ private static string Helper() => File.ReadAllText(FindFile(
|
|
|
+ "Web", "src", "views", "approvalFlow", "center", "components", "bizTypeDisplay.ts"));
|
|
|
+
|
|
|
+ private static string Center() => File.ReadAllText(FindFile(
|
|
|
+ "Web", "src", "views", "approvalFlow", "center", "index.vue"));
|
|
|
+
|
|
|
+ private static string Tab(string name) => File.ReadAllText(FindFile(
|
|
|
+ "Web", "src", "views", "approvalFlow", "center", "components", name));
|
|
|
+
|
|
|
+ /// <summary>三个 Tab 必须走同一个展示解析,不得各写一套。</summary>
|
|
|
+ [Theory]
|
|
|
+ [InlineData("PendingList.vue")]
|
|
|
+ [InlineData("DoneList.vue")]
|
|
|
+ [InlineData("InitiatedList.vue")]
|
|
|
+ public void AllThreeTabs_UseSharedBizTypeText(string file)
|
|
|
+ {
|
|
|
+ var s = Tab(file);
|
|
|
+ Assert.Contains("from './bizTypeDisplay'", s);
|
|
|
+ Assert.Contains("bizTypeText(row.bizType, props.bizTypeNames)", s);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>三个 Tab 都不得再把 bizType 作为唯一展示(裸 prop 列)。</summary>
|
|
|
+ [Theory]
|
|
|
+ [InlineData("PendingList.vue")]
|
|
|
+ [InlineData("DoneList.vue")]
|
|
|
+ [InlineData("InitiatedList.vue")]
|
|
|
+ public void AllThreeTabs_NoBareBizTypeColumn(string file)
|
|
|
+ {
|
|
|
+ Assert.DoesNotContain(
|
|
|
+ "<el-table-column prop=\"bizType\" label=\"业务类型\" width=\"140\" class-name=\"mobile-hide\" label-class-name=\"mobile-hide\" />",
|
|
|
+ Tab(file));
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>Name 缺失(新增/未配置/已停用的类型)必须回落 Code,不能显示 '-' 把配置缺失藏起来。</summary>
|
|
|
+ [Fact]
|
|
|
+ public void Helper_FallsBackToCode_NotDash()
|
|
|
+ {
|
|
|
+ var s = Helper();
|
|
|
+ Assert.Contains("return name && name.trim() !== '' ? name : code;", s);
|
|
|
+ Assert.Contains("if (!code) return '-';", s); // 仅 Code 本身为空时才 '-'
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>名称映射复用筛选下拉已加载的同一份数据,不得引入 N+1 / 额外请求。</summary>
|
|
|
+ [Fact]
|
|
|
+ public void Center_ReusesExistingBizTypeList_NoExtraFetch()
|
|
|
+ {
|
|
|
+ var s = Center();
|
|
|
+ Assert.Contains("const bizTypeNameMap = computed<Record<string, string>>", s);
|
|
|
+ Assert.Contains("bizTypeOptions.value", s);
|
|
|
+ // 全页只有一次业务类型字典请求
|
|
|
+ Assert.Equal(1, s.Split("getBizTypeList()").Length - 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>三个 Tab 都拿到映射(否则会出现"待办中文、已办英文")。</summary>
|
|
|
+ [Fact]
|
|
|
+ public void Center_PassesNameMapToAllThreeTabs()
|
|
|
+ {
|
|
|
+ Assert.Equal(3, Center().Split(":biz-type-names=\"bizTypeNameMap\"").Length - 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>筛选控件的 value 仍是 Code,label 才是中文——不得反过来。</summary>
|
|
|
+ [Fact]
|
|
|
+ public void Filter_StillSubmitsCodeNotName()
|
|
|
+ {
|
|
|
+ var s = Center();
|
|
|
+ Assert.Contains(":value=\"bt.code\"", s);
|
|
|
+ Assert.Contains("params.bizType = filterBizType.value;", s);
|
|
|
+ Assert.DoesNotContain(":value=\"bt.name\"", s);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>路由 / 按钮显隐等业务判断继续读 Code,未被中文化波及。</summary>
|
|
|
+ [Fact]
|
|
|
+ public void RoutingAndButtonLogic_StillKeyedOnCode()
|
|
|
+ {
|
|
|
+ Assert.Contains("row.bizType === 'S7_FQC_INSPBILL'", Tab("PendingList.vue"));
|
|
|
+ Assert.Contains("row.bizType === 'S6_PROCESS_INSPECTION'", Tab("PendingList.vue"));
|
|
|
+ Assert.Contains("row.bizType === 'ORDER_CHANGE_REVIEW'", Tab("DoneList.vue"));
|
|
|
+ Assert.Contains("row.bizType === 'MATERIAL_SHORTAGE'", Tab("InitiatedList.vue"));
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>不得出现把 Code 硬编码成中文的前端字典(中文必须来自后端 Name)。</summary>
|
|
|
+ [Fact]
|
|
|
+ public void NoHardcodedCodeToChineseDictionary()
|
|
|
+ {
|
|
|
+ foreach (var f in new[] { "PendingList.vue", "DoneList.vue", "InitiatedList.vue" })
|
|
|
+ {
|
|
|
+ var s = Tab(f);
|
|
|
+ Assert.DoesNotContain("IPQC_INSPECTION:", s);
|
|
|
+ Assert.DoesNotContain("S7_FQC_INSPBILL:", s);
|
|
|
+ }
|
|
|
+ Assert.DoesNotContain("IPQC_INSPECTION:", Helper());
|
|
|
+ }
|
|
|
+
|
|
|
+ private static string FindFile(params string[] parts)
|
|
|
+ {
|
|
|
+ var dir = new DirectoryInfo(AppContext.BaseDirectory);
|
|
|
+ while (dir != null)
|
|
|
+ {
|
|
|
+ var candidate = Path.Combine(new[] { dir.FullName }.Concat(parts).ToArray());
|
|
|
+ if (File.Exists(candidate)) return candidate;
|
|
|
+ dir = dir.Parent;
|
|
|
+ }
|
|
|
+ throw new FileNotFoundException(string.Join("/", parts));
|
|
|
+ }
|
|
|
+}
|