diff --git a/src/w3c/headers.js b/src/w3c/headers.js
index 53a675f1df..da76687c68 100644
--- a/src/w3c/headers.js
+++ b/src/w3c/headers.js
@@ -393,11 +393,22 @@ export async function run(conf) {
}
if (conf.isEd) conf.thisVersion = conf.edDraftURI;
- if (conf.isCGBG) validateCGBG(conf);
+ if (conf.isCGBG) {
+ validateCGBG(conf);
+ }
+ if (conf.isTagEditorFinding && !conf.latestVersion) {
+ conf.latestVersion = null;
+ }
if (conf.latestVersion !== null) {
conf.latestVersion = conf.latestVersion
? w3Url(conf.latestVersion)
: w3Url(`${pubSpace}/${conf.shortName}/`);
+ const exists = await resourceExists(conf.latestVersion);
+ if (!exists && conf.specStatus !== "FPWD") {
+ const msg = `The "Latest published version:" header link points to a URL that does not exist.`;
+ const hint = docLink`Check that the ${"[shortname]"} is correct and you are using the right ${"[specStatus]"} for this kind of document.`;
+ showWarning(msg, name, { hint });
+ }
}
if (conf.latestVersion) validateIfAllowedOnTR(conf);
@@ -469,7 +480,9 @@ export async function run(conf) {
conf.publishISODate = conf.publishDate.toISOString();
conf.shortISODate = ISODate.format(conf.publishDate);
validatePatentPolicies(conf);
- await deriveHistoryURI(conf);
+
+ conf.historyURI = await deriveHistoryURI(conf);
+
if (conf.isTagEditorFinding) {
delete conf.thisVersion;
delete conf.latestVersion;
@@ -644,7 +657,6 @@ function validateIfAllowedOnTR(conf) {
const msg = docLink`Documents with a status of \`"${conf.specStatus}"\` can't be published on the W3C's /TR/ (Technical Report) space.`;
const hint = docLink`Ask a W3C Team Member for a W3C URL where the report can be published and change ${"[latestVersion]"} to something else.`;
showError(msg, name, { hint });
- return;
}
}
@@ -670,9 +682,6 @@ function derivePubSpace(conf) {
function validateCGBG(conf) {
const reportType = status2text[conf.specStatus];
- const latestVersionURL = conf.latestVersion
- ? new URL(w3Url(conf.latestVersion))
- : null;
if (!conf.wg) {
const msg = docLink`The ${"[group]"} configuration option is required for this kind of document (${reportType}).`;
@@ -680,8 +689,16 @@ function validateCGBG(conf) {
return;
}
+ if (conf.specStatus.endsWith("-DRAFT") && !conf.latestVersion) {
+ conf.latestVersion = null;
+ return;
+ }
+
// Deal with final reports
if (conf.isCGFinal) {
+ const latestVersionURL = conf.latestVersion
+ ? new URL(w3Url(conf.latestVersion))
+ : null;
// Final report require a w3.org URL.
const isW3C =
latestVersionURL?.origin === "https://www.w3.org" ||
@@ -696,18 +713,17 @@ function validateCGBG(conf) {
}
async function deriveHistoryURI(conf) {
- if (!conf.shortName || conf.historyURI === null || !conf.latestVersion) {
- return; // Nothing to do
+ if (!conf.shortName || !conf.latestVersion) {
+ return null;
}
const canShowHistory = conf.isEd || trStatus.includes(conf.specStatus);
- if (conf.historyURI && !canShowHistory) {
- const msg = docLink`The ${"[historyURI]"} can't be used with non /TR/ documents.`;
+ if (!canShowHistory && conf.historyURI) {
+ const msg = docLink`The ${"[historyURI]"} can't be used with non-standards track documents.`;
const hint = docLink`Please remove ${"[historyURI]"}.`;
showError(msg, name, { hint });
- conf.historyURI = null;
- return;
+ return conf.historyURI;
}
const historyURL = new URL(
@@ -729,13 +745,16 @@ async function deriveHistoryURI(conf) {
// Do a fetch HEAD request to see if the history exists...
// We don't discriminate... if it's on the W3C website with a history,
// we show it.
+ const exists = await resourceExists(historyURL);
+ return exists ? historyURL.href : null;
+}
+
+async function resourceExists(url) {
try {
- const response = await fetch(historyURL, { method: "HEAD" });
- if (response.ok) {
- conf.historyURI = response.url;
- }
+ const response = await fetch(url, { method: "HEAD" });
+ return response.ok;
} catch {
- // Ignore fetch errors
+ return false;
}
}
diff --git a/src/w3c/templates/cgbg-headers.js b/src/w3c/templates/cgbg-headers.js
index 03a2585587..0db26c6514 100644
--- a/src/w3c/templates/cgbg-headers.js
+++ b/src/w3c/templates/cgbg-headers.js
@@ -37,10 +37,10 @@ export default (conf, options) => {
>
`
: ""}
- ${"latestVersion" in conf // latestVersion can be falsy
+ ${conf.latestVersion !== null
? html`
${l10n.latest_published_version}
- ${conf.latestVersion
+ ${conf.latestVersion !== ""
? html`${conf.latestVersion}`
diff --git a/tests/spec/w3c/headers-spec.js b/tests/spec/w3c/headers-spec.js
index ff951b19b3..4e474e217d 100644
--- a/tests/spec/w3c/headers-spec.js
+++ b/tests/spec/w3c/headers-spec.js
@@ -16,9 +16,11 @@ import {
makeDefaultBody,
makeRSDoc,
makeStandardOps,
+ warningFilters,
} from "../SpecHelper.js";
const headerErrors = errorFilters.filter("w3c/headers");
+const headerWarnings = warningFilters.filter("w3c/headers");
const defaultErrors = errorFilters.filter("w3c/defaults");
const findContent = string => {
@@ -62,26 +64,27 @@ describe("W3C — Headers", () => {
expect(exportedDoc.querySelector(".head details[open]")).toBeTruthy();
});
- it("links to the 'kinds of documents' only for W3C documents", async () => {
- const statuses = ["FPWD", "WD", "CR", "CRD", "PR", "REC", "NOTE"];
- for (const specStatus of statuses) {
+ for (const specStatus of recTrackStatus) {
+ it(`links to the 'kinds of documents' only for W3C documents with status ${specStatus}`, async () => {
const doc = await makeRSDoc(
makeStandardOps({ specStatus, group: "webapps" })
);
const w3cLink = doc.querySelector(
`.head a[href='https://www.w3.org/standards/types#${specStatus}']`
);
- expect(w3cLink).withContext(`specStatus: ${specStatus}`).toBeTruthy();
- }
+ expect(w3cLink).toBeTruthy();
+ });
+ }
- for (const specStatus of ["unofficial", "base"]) {
+ for (const specStatus of noTrackStatus) {
+ it(`doesn't link to the 'kinds of documents' for non-rec track ${specStatus}`, async () => {
const doc = await makeRSDoc(makeStandardOps({ specStatus }));
const w3cLink = doc.querySelector(
".head a[href='https://www.w3.org/standards/types#UD']"
);
- expect(w3cLink).withContext(`specStatus: ${specStatus}`).toBeNull();
- }
- });
+ expect(w3cLink).toBeNull();
+ });
+ }
describe("prevRecShortname & prevRecURI", () => {
it("takes prevRecShortname and prevRecURI into account", async () => {
@@ -1437,6 +1440,21 @@ describe("W3C — Headers", () => {
expect(latestVersionLink.textContent).toBe("https://www.w3.org/TR/foo/");
});
+ it("warns if latestVersion URL doesn't exist", async () => {
+ const ops = makeStandardOps({
+ shortName: "foo",
+ specStatus: "WD",
+ group: "webapps",
+ github: "w3c/respec",
+ });
+ const doc = await makeRSDoc(ops);
+ const warnings = headerWarnings(doc);
+ expect(warnings).toHaveSize(1);
+ expect(warnings[0].message).toContain(
+ `The "Latest published version:" header link points to a URL that does not exist`
+ );
+ });
+
it("allows skipping latest published version link in initial ED", async () => {
const ops = makeStandardOps({
specStatus: "ED",
@@ -1543,7 +1561,7 @@ describe("W3C — Headers", () => {
);
});
- for (const specStatus of cgbgStatus.filter(s => s.endsWith("-DRAFT"))) {
+ for (const specStatus of cgStatus.filter(s => s.endsWith("-DRAFT"))) {
it(`doesn't set latestVersion URL for ${specStatus} status`, async () => {
const ops = makeStandardOps({
shortName: "some-report",
@@ -1551,11 +1569,7 @@ describe("W3C — Headers", () => {
group: "wicg",
});
const doc = await makeRSDoc(ops);
- const terms = [...doc.querySelectorAll(".head dt")];
- const latestVersion = terms.find(
- el => el.textContent.trim() === "Latest published version:"
- );
- expect(latestVersion).toHaveSize(0);
+ expect(contains(doc, "dt", "Latest published version:")).toHaveSize(0);
});
}
for (const specStatus of noTrackStatus) {
@@ -1989,7 +2003,7 @@ describe("W3C — Headers", () => {
{ specStatus: "BG-FINAL", group: "publishingbg" },
];
for (const { specStatus, group } of finalReportStatus) {
- it("requires that the ${specStatus} latestVersion be a w3c URL", async () => {
+ it(`requires that the ${specStatus} latestVersion be a w3c URL`, async () => {
const ops = makeStandardOps({
specStatus,
group,
@@ -2550,6 +2564,7 @@ describe("W3C — Headers", () => {
const ops = makeStandardOps({
shortName: "payment-request",
specStatus: "ED",
+ group: "payments",
});
const doc = await makeRSDoc(ops);
const [history] = contains(doc, ".head dt", "History:");
@@ -2562,8 +2577,8 @@ describe("W3C — Headers", () => {
);
});
- for (const specStatus of trStatus) {
- it(`includes the history for "${specStatus}" rec-track status`, async () => {
+ for (const specStatus of recTrackStatus) {
+ it(`includes the history for rec-track "${specStatus}" docs`, async () => {
const shortName = `push-api`;
const ops = makeStandardOps({
shortName,
@@ -2575,9 +2590,9 @@ describe("W3C — Headers", () => {
expect(history).withContext(specStatus).toBeTruthy();
expect(history.nextElementSibling).withContext(specStatus).toBeTruthy();
const historyLink = history.nextElementSibling.querySelector("a");
- expect(historyLink).toBeTruthy();
+ expect(historyLink).withContext(specStatus).toBeTruthy();
expect(historyLink.href).toBe(
- `https://www.w3.org/standards/history/${shortName}/`
+ `https://www.w3.org/standards/history/${shortName}`
);
});
}