Scert/frontend/src/views/Dashboard/DomainModal.test.js
xuans 73511d6fcb 统一多级域名泛域名生成与预览
集中处理泛域名识别、生成、编辑回显和多级子域名预览,补充前后端测试覆盖。
2026-10-16 02:41:16 +08:00

165 lines
5.9 KiB
JavaScript

import assert from "node:assert/strict";
import test from "node:test";
import { readFile } from "node:fs/promises";
import * as Vue from "vue";
import { parse, compileTemplate } from "@vue/compiler-sfc";
import { renderToString } from "@vue/server-renderer";
async function renderModal(overrides = {}) {
const source = await readFile(new URL("./DomainModal.vue", import.meta.url), "utf8");
const { descriptor, errors } = parse(source);
assert.deepEqual(errors, []);
const compiled = compileTemplate({
source: descriptor.template.content,
filename: "DomainModal.vue",
id: "domain-many-to-many",
compilerOptions: { mode: "function" },
});
assert.deepEqual(compiled.errors, []);
const render = new Function("Vue", compiled.code)(Vue);
const warnings = [];
const app = Vue.createSSRApp({
render,
setup: () => ({
showForm: true,
formTitle: "编辑域名",
closeForm() {},
saveDomain() {},
form: {
name: "example.test",
certificate_type: "single",
sans: "",
auto_renew: false,
acme_environment: "staging",
dns_provider: "",
server_ids: ["server-one", "server-two"],
},
dnsProviders: [],
dnsAccountID: "",
dnsBindingLoading: false,
dnsBindingReady: true,
dnsBindingError: "",
dnsBound: false,
domainDNSAccountOptions: [],
dnsUnavailableSelection: false,
reloadDomainDNS() {},
domainServerOptions: [
{ id: "server-one", name: "共享服务器一", address: "192.0.2.1", web_server: "nginx" },
{ id: "server-two", name: "共享服务器二", address: "192.0.2.2", web_server: "nginx" },
],
displayLabel: (_, value) => value,
webServerLabels: {},
formError: "",
saving: false,
wildcardName: "",
domainBindingsLoading: false,
domainBindingsReady: true,
domainSaveUncertain: false,
reloadDomainBindings() {},
...overrides,
}),
});
app.component("HelpTip", {
setup: (_, { slots }) => () => Vue.h("span", { class: "help-tip" }, slots.default?.()),
});
app.config.warnHandler = (message) => warnings.push(message);
const html = await renderToString(app);
assert.deepEqual(warnings, []);
return html;
}
async function readModalSource() {
return readFile(new URL("./DomainModal.vue", import.meta.url), "utf8");
}
test("域名表单使用复选框同时显示两台已关联服务器并提供复用提示", async () => {
const html = await renderModal();
const inputs = html.match(/<input\b[^>]*>/g) || [];
for (const id of ["server-one", "server-two"]) {
const input = inputs.find((value) => value.includes(`value="${id}"`));
assert.ok(input);
assert.match(input, /type="checkbox"/);
assert.match(input, /\bchecked(?:[\s=>])/);
}
assert.match(html, /同一服务器可关联多个域名/);
assert.match(html, /不影响其他域名/);
assert.match(html, /当前域名所属项目/);
});
test("域名表单不会通过背景点击误关闭,并提示泛域名格式", async () => {
const source = await readModalSource();
const html = await renderModal({ form: {
name: "pcux.cn",
certificate_type: "wildcard",
sans: "",
auto_renew: false,
acme_environment: "production",
dns_provider: "aliyun",
server_ids: [],
} });
assert.doesNotMatch(source, /class="modal-backdrop domain-backdrop"[^>]*@click/);
assert.match(html, /placeholder="\*\.example\.com"/);
assert.match(html, /泛域名必须以 \*\. 开头/);
});
test("多级主域名可预览对应的一级泛域名", async () => {
const html = await renderModal({
form: {
name: "x.x.x.com",
certificate_type: "single",
include_wildcard: true,
sans: "",
auto_renew: true,
acme_environment: "staging",
dns_provider: "",
server_ids: [],
},
});
assert.match(html, /同时申请泛域名/);
assert.match(html, /x\.x\.x\.com、\*\.x\.x\.x\.com/);
assert.match(html, /泛域名只能使用 DNS-01/);
});
test("域名账户显示归属与服务商,原绑定不可用不被静默清空", async () => {
const html = await renderModal({
dnsAccountID: "old-account", dnsBound: true, dnsUnavailableSelection: true,
domainDNSAccountOptions: [{ id: "new-account", name: "生产账户", provider_name: "阿里云 DNS", owner_username: "甲用户" }],
});
assert.match(html, /当前绑定账户不可用/);
assert.match(html, /生产账户 · 阿里云 DNS · 甲用户/);
assert.match(html, /解除独立绑定/);
assert.doesNotMatch(html, /id="dns-provider"/);
const challenge = html.match(/<select\b[^>]*id="challenge-type"[^>]*>/)?.[0];
assert.match(challenge, /disabled/);
});
test("DNS 绑定读取中或失败时禁止提交并提供重新读取", async () => {
for (const overrides of [
{ dnsBindingLoading: true, dnsBindingReady: false },
{ dnsBindingReady: false, dnsBindingError: "绑定读取失败" },
]) {
const html = await renderModal(overrides);
const submit = html.match(/<button\b[^>]*type="submit"[^>]*>/)?.[0];
assert.match(submit, /disabled/);
assert.match(html, /重新读取 DNS 绑定/);
}
});
test("关联读取失败或结果未确认时禁用提交,读取失败提供重试入口", async () => {
for (const overrides of [
{ domainBindingsReady: false },
{ domainBindingsLoading: true },
{ domainSaveUncertain: true },
{ saving: true },
]) {
const html = await renderModal(overrides);
const submit = (html.match(/<button\b[^>]*>/g) || []).find((tag) => tag.includes('type="submit"'));
assert.ok(submit);
assert.match(submit, /\bdisabled(?:[\s=>])/);
const picker = (html.match(/<fieldset\b[^>]*>/g) || [])[0];
assert.match(picker, /\bdisabled(?:[\s=>])/);
if (overrides.domainBindingsReady === false) assert.match(html, /重新读取关联/);
if (overrides.domainBindingsLoading) assert.match(html, /正在读取服务器关联/);
}
});