NAME

App::FuguWeb::Check - what a built site must be true of

SYNOPSIS

use App::FuguWeb::Check;

my $check = App::FuguWeb::Check->new(
    config => $config,
    out    => 'web/build',
);

my @problems = $check->run;
warn "$_\n" for @problems;
note("external link: $_") for $check->external;

DESCRIPTION

The checks are generic, so every project that uses the tool gets them. A project keeps only the assertions that are about its own content.

The class never fetches a link. It collects the external ones and reports them, because the build and its checks touch no network.

The class reads each attribute of a page through "edit_attributes" in App::FuguWeb, the reader that App::FuguWeb::Fragment reads too. The text of a value, such as title="see href=x", is therefore no reference, and a name such as data-id is no id.

METHODS

new

App::FuguWeb::Check->new(
    config => $config,      # required
    out    => $dir,         # required: the built site
)

pages, generated_pages

pages names each page of each language tree, as <path>/<page>, where the path is that of the tree of the language: for each language, the pages of the description, then one page for each manual of each group. generated_pages names the human page of each key directory, which the build writes itself and which no block names. The pages of the top level sit outside every tree as well, and "top_pages" in App::FuguWeb::Config names them.

A generated page takes the checks of a page. It takes no reachability check: a site links the key directory when it chooses to. The pages of the top level take none either.

The full expectation, assets and the pages of the top level included, comes from "inventory" in App::FuguWeb::Config, so the build and the checks can never disagree about what the site is.

run

Check the site and return the problems, each one a sentence that names the page. An empty list means the site is good.

The checks are:

  • every expected page and asset exists and is not empty;

  • on every page but the top-level index.html, the check finds a title. The place of the path of the page decides its chrome, as it does in App::FuguWeb::Page: a tree page, a page of the top level, or the human page of a key directory;

  • every page of a language tree carries the tag of its tree as lang on its <html> element, and dir="rtl" when the language of the tree is right to left, per WEB-LANGUAGE-13;

  • the head of a tree page names the page itself, the same page in each language, and the forward page, per WEB-CHROME-6. The head of the forward names the bare url as its canonical link and as x-default, and the entry page of each language, per WEB-TOP-2. The head of any other page names none. Each such href is the url of the description, then the path of a page of the inventory, or the bare url, per WEB-LINKS-5. None of them is an external link;

  • the header links the entry page of the tree, and the nav holds one item for each nav entry, in the order of the description. A page outside every tree takes the words and the links of the default tree. The check resolves each link of the chrome from the path of its page, and reads its target and not its form. A link can carry the marks of its language after its href;

  • on a site of more than one language, the header of a tree page and of the top-level 404.html, and the body of the forward, hold one item for each language, in the order of the declaration. On a tree page, each link goes to the same page in that language. On a page of the top level, each link goes to the entry page of that language, per WEB-TOP-2 and WEB-TOP-4. Its text is the name of the language, and the tag is its lang and its hreflang. The key page, and each page of a site of one language, holds no such item;

  • the link of the language of the page, and the nav link of the page, carry aria-current="page", and no other link of the chrome does;

  • the chrome holds no class, no <hr> and no text separator, per WEB-CHROME-5. Each word of the chrome of the body is the text of a link. The body fragment and the footer fragment are the writer's own;

  • a relative nav href of the description names a page of each tree, as it resolves from the path of that tree, per WEB-LINKS-2;

  • no reference starts with /, because the host may serve the site from a path below the root. The <base> of a page of the top level is the one exception, per WEB-LINKS-4. Its href is "url_path" in App::FuguWeb::Config, and that page must carry it;

  • the top-level index.html is a page of the default language, per WEB-TOP-1. Its head holds the charset, a meta refresh to the entry page of the default tree, the title and the links of WEB-CHROME-6, in that order, and nothing else. Its title is the site name. On a site of one language, its body holds one link to the entry page, with the site name as its text. The body holds no other reference, so the page loads nothing, and it carries no navigation;

  • the top-level 404.html is a page of the default language, with the title of the 404.html of the default tree, per WEB-TOP-4. Its head holds the charset, the viewport, the base, the title and the stylesheet link, in that order, and nothing else, per WEB-TOP-3. Its <main> carries no mark. It holds one <section> for each language, in the order of the declaration, and nothing else, per WEB-TOP-5. Each section carries the marks of the language of its text. It holds the body of the 404.html of the tree of its language, as "not_found" in App::FuguWeb::Fragment fits it: with no id, and with each relative link from the site root, per WEB-TOP-6;

  • no reference uses a file: URL;

  • every relative reference resolves to a file of the inventory, a manual cross-reference included. A directory and a stray file are no file of the inventory. A reference with a query passes when its path names a file of the inventory, per WEB-LINKS-3;

  • a reference of a page resolves against the directory of that page: a language tree, a key directory, or the top level. A page with a <base> resolves each reference against the site root, and a fragment alone names no page there, per WEB-LINKS-3;

  • every fragment resolves to an id on the target page;

  • each heading of the main element of a tree page of another language than the default takes the id of the heading at the same position of its namesake in the default tree, per WEB-OUTLINE-3. It takes no id when that heading has none, and the page holds the same count of headings. The check reads each outline through "outline" in App::FuguWeb::Fragment, as the build does;

  • no relative href reads as a URL scheme, so every local link keeps its ./;

  • every page of a language tree is reachable from the entry page of that tree, except a page that the description marks unlinked. The walk stays inside the tree, per WEB-LANGUAGE-13;

  • the output holds the site and nothing else, at any depth: no staging directory, no editor backup, no stray source, no tree of a language that the description does not declare, and no empty directory.

A description with a keys block adds the checks of "THE CHECKS" in App::FuguWeb::Keys. They read the source key directory and not the output. A stray key file and a stale digest are therefore found whether or not a build has run.

external

The external links that the last run collected, sorted.

SEE ALSO

App::FuguWeb, App::FuguWeb::Config, App::FuguWeb::Fragment, App::FuguWeb::Keys, App::FuguWeb::Site, fuguweb(1)

AUTHOR

Dick Olsson <hi@senzilla.io>