S8, finished where the plan said it would be: every .help-tip badge is a <button> - upgraded by the component itself at load (help.js), with helpTipUpgrade() for late renders, so a badge added tomorrow is born reachable. The count the task warned about came true: 15 at wave 0, 18 at the wave 6 exit, 20 at the start of this task - all 20 buttons now, and the fix being in the component is what stops the number growing again. One viewport-clamped role=tooltip bubble serves every badge: focus shows it, Escape hides it, tap toggles it, tap-elsewhere closes it - the touch path Field View's tablets never had. The injected styles now use theme tokens (four raw hexes of the S5 kind, gone). BL-001, CLOSED after three causes and nine waves: the old CSS ::after escaped its badge to the right and was the creator's last 390px overflow. The clamped bubble ends it - scrollWidth 390 vs clientWidth 390 - and frame_check's pin FLIPPED, exactly as designed: it asserted the failure until the fix landed, and now asserts the fix so a regression reopens the entry loudly. The audit (docs/reference/accessibility-audit.md), every number probe-backed: - div/span click handlers: 12/2 at wave 0 -> 0 (the wizard's constraint library entries and the dashboard chips became buttons here; the comments backdrop stopped pretending to be a control) - outline:none without replacement: 0 (wp-chrome's one is the documented S12 exception - its ring is on :focus-within, one ring not two) - aria-live: every toast system and banner announces - native dialogs: 79 -> 21, all on surfaces no S1 task named (admin, users, launcher) - documented as BL-024 with the T7.9 kit ready for them - keyboard-only primary flow: covered leg by leg by the probes that dispatch real CDP key events, cited in the document Three stale count-pins re-pointed to the numbers this task reached (stepper's baseline-minus-10, form_structure's one-span-left, frame_check's BL-001 pin) - each now pins the TARGET so slack cannot hide a regression. Verification (each probe run alone): NEW tests/helptip_check.py 13/13. Regressions: a11y_check 22/22, stepper_check 71/71, form_structure_check 50/51 (BL-022's product question), pipeline_check 44/44, frame_check 38/38. Items: C1, S8 (BL-001 closed, BL-024 opened) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
464 lines
22 KiB
Python
464 lines
22 KiB
Python
#!/usr/bin/env python3
|
|
"""Is the creator's iframe actually gone? — B7 / T7.1, plus D1.
|
|
|
|
`docs/reference/creator-frame.md` measured the boundary before it was dissolved:
|
|
21 colliding stylesheet selectors, 9 colliding script globals, 0 colliding markup
|
|
ids, 28 cross-frame call sites across 8 scripts and 1 page. This checks the other
|
|
end of that work.
|
|
|
|
1. no iframe, and no cross-frame code, anywhere in html/
|
|
2. both documents still parse and boot - the failure mode a structural edit has
|
|
3. the creator is a page: app bar, tab strip, drawer, sample controls (D1)
|
|
4. every address that used to reach the creator through the frame still reaches it
|
|
5. the SOP gate still gates, and a gated tab explains itself instead of vanishing
|
|
6. every creator feature the frame used to hide is reachable
|
|
7. the four backlog entries logged against this file, re-measured
|
|
|
|
Check 2 is here because of a real failure during T7.1: a `const` shadowing a
|
|
function parameter is a SyntaxError, so `work-package-suite-app.js` did not parse
|
|
at all and every one of its globals was undefined. Four unrelated checks in
|
|
another probe went red and none of them said "the script did not load". A probe
|
|
that asserts a page's own entry points exist is cheap and says exactly that.
|
|
|
|
Self-contained: throwaway SQLite, its own uvicorn, headless Edge or Chrome.
|
|
Exit 0 all passed, 1 a failure, 2 could not run.
|
|
"""
|
|
import io
|
|
import json
|
|
import os
|
|
import re
|
|
import sys
|
|
import tempfile
|
|
import time
|
|
|
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
|
|
import cdp # noqa: E402
|
|
from browser_check import seed, start_server, chk, _PASS, _FAIL, _c # noqa: E402
|
|
# BL-018, for the fourth time: browser_check.seed() writes a SOP whose data is
|
|
# {"governance": ...}, and nothing in the app writes that shape. restoreSavedSOP()
|
|
# bails on it, sopComplete stays false, and every creator route below would land on
|
|
# the SOP gate instead. Imported rather than copied, so the duplication stays
|
|
# visible in one place until T9.9 fixes the fixture itself.
|
|
from sections_check import set_sop # noqa: E402
|
|
|
|
HTML = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "html")
|
|
|
|
# Which boxes are wider than the screen, worst first. BL-001's own entry blamed
|
|
# --nav-w; naming the actual elements means the next person opens the right file
|
|
# instead of re-deriving it.
|
|
WIDEST_JS = r"""(() => {
|
|
const vw = document.documentElement.clientWidth;
|
|
const sw = document.documentElement.scrollWidth;
|
|
const out = [];
|
|
document.querySelectorAll('body *').forEach(el => {
|
|
const cs = getComputedStyle(el);
|
|
// A `position: fixed` drawer parked off-screen with translateX(100%) sits far
|
|
// to the right of the viewport by design and contributes nothing to
|
|
// scrollWidth. Its CHILDREN are `position: static` and ride out there with it,
|
|
// so testing the element alone is not enough - the whole subtree has to go, or
|
|
// the list fills with the five boxes inside the drawer and BL-001 gets
|
|
// attributed to the wrong file for a second time.
|
|
for (let p = el; p; p = p.parentElement) {
|
|
if (getComputedStyle(p).position === 'fixed') return;
|
|
}
|
|
const r = el.getBoundingClientRect();
|
|
if (r.right > vw + 2 || r.width > vw + 2) {
|
|
out.push({
|
|
sel: el.tagName.toLowerCase()
|
|
+ (el.id ? '#' + el.id : '')
|
|
+ (el.className && typeof el.className === 'string'
|
|
? '.' + el.className.trim().split(/\s+/).slice(0, 2).join('.') : ''),
|
|
w: Math.round(r.width), right: Math.round(r.right),
|
|
pos: cs.position,
|
|
});
|
|
}
|
|
});
|
|
out.sort((a, b) => b.right - a.right);
|
|
return JSON.stringify({scrollWidth: sw, viewport: vw, boxes: out.slice(0, 5)});
|
|
})()"""
|
|
|
|
|
|
def settle(seconds=1.0):
|
|
time.sleep(seconds)
|
|
|
|
|
|
def wait_creator(page, tries=40):
|
|
for _ in range(tries):
|
|
if page.eval("!!window.wpCreatorReady"):
|
|
return True
|
|
time.sleep(0.3)
|
|
return False
|
|
|
|
|
|
def read(name):
|
|
return io.open(os.path.join(HTML, name), encoding="utf-8").read()
|
|
|
|
|
|
def strip_comments(src, kind):
|
|
"""Comments are prose about the change, and prose about removing an iframe
|
|
contains the word iframe. BL-017 recorded exactly this: a grep that counts
|
|
its own explanation moves the number it is meant to be proving. Strip them.
|
|
"""
|
|
if kind == "html":
|
|
return re.sub(r"<!--.*?-->", "", src, flags=re.S)
|
|
src = re.sub(r"/\*.*?\*/", "", src, flags=re.S)
|
|
return re.sub(r"(?m)^\s*//.*$", "", src)
|
|
|
|
|
|
# ── 1. the source ─────────────────────────────────────────────────────────────
|
|
def source_checks():
|
|
print("\n1. no iframe, and no cross-frame code, in html/")
|
|
pages = [f for f in os.listdir(HTML) if f.endswith(".html")]
|
|
scripts = [f for f in os.listdir(HTML) if f.endswith(".js")]
|
|
|
|
framed = []
|
|
for f in pages:
|
|
body = strip_comments(read(f), "html")
|
|
if "<iframe" in body.lower():
|
|
framed.append(f)
|
|
chk("no page declares an iframe", not framed, framed)
|
|
|
|
# The four APIs that only mean something across a document boundary.
|
|
banned = {
|
|
"contentWindow": "reaching into a child document",
|
|
"contentDocument": "reading a child document",
|
|
"window.top": "asking whether we are the top document",
|
|
"window.parent": "reaching out to a parent document",
|
|
}
|
|
hits = {}
|
|
for f in scripts + pages:
|
|
body = strip_comments(read(f), "html" if f.endswith(".html") else "js")
|
|
for api, why in banned.items():
|
|
if api in body:
|
|
hits.setdefault(api + " (" + why + ")", []).append(f)
|
|
chk("no cross-frame API is called anywhere", not hits, hits)
|
|
|
|
# The embedding flag and the class set it drove.
|
|
flags = {}
|
|
for f in scripts + pages:
|
|
body = strip_comments(read(f), "html" if f.endswith(".html") else "js")
|
|
for token in ("embedded=1", "embed-hide", "embed-first", "body.embedded", "wp-frame"):
|
|
if token in body:
|
|
flags.setdefault(token, []).append(f)
|
|
chk("the embedding flag and its classes are gone", not flags, flags)
|
|
|
|
# And the sizing machinery that existed only to make a frame fill a window.
|
|
suite = strip_comments(read("work-package-suite-app.js"), "js")
|
|
gone = [n for n in ("applyEmbedLayout", "sizeWPFrame", "viewportMinusChrome",
|
|
"renderWPTab", "pushSectionsToCreator")
|
|
if n in suite]
|
|
chk("...and so is the frame-sizing machinery", not gone, gone)
|
|
|
|
# The creator was the only page loading neither of wp-chrome's two files.
|
|
creator = read("wp-creation-index.html")
|
|
chk("the creator loads the shared app bar, like every other page",
|
|
"wp-chrome.css" in creator and "wp-chrome.js" in creator)
|
|
chk("...and the tab strip lives in the sheet both tool pages load",
|
|
".nav-tab" in read("wp-chrome.css")
|
|
and ".nav-tab {" not in read("work-package-suite-styles.css"))
|
|
|
|
|
|
# ── 2. both documents boot ────────────────────────────────────────────────────
|
|
def boot_checks(page, base, tok):
|
|
print("\n2. both documents parse and boot")
|
|
for label, path, probe in (
|
|
("the SOP wizard", "/work-package-suite.html?project=projA&tab=sop",
|
|
"typeof switchTool === 'function' && typeof goToStep === 'function'"
|
|
" && typeof openCreator === 'function'"),
|
|
("the creator", "/wp-creation-index.html?project=projA",
|
|
"typeof showDashboard === 'function' && typeof newPackage === 'function'"
|
|
" && typeof syncToolTabs === 'function'"),
|
|
):
|
|
page.clear_cookies()
|
|
page.set_cookie("wp_session", tok["root"])
|
|
page.viewport(1440)
|
|
page.goto(base + path)
|
|
settle(1.6)
|
|
chk("%s's own entry points are defined" % label, page.eval(probe) is True,
|
|
"script did not parse; see js_errors below")
|
|
# `beforeunload` is not a fault. Leaving a page with unsaved SOP edits is
|
|
# supposed to warn, and headless blocks the panel because there was no user
|
|
# gesture. It IS a behaviour change T7.1 introduced - a tab switch used to
|
|
# stay inside one document and never prompt - and it is recorded as BL-020
|
|
# rather than quietly suppressed here.
|
|
errs = [e for e in page.js_errors()
|
|
if "favicon" not in e and "404" not in e and "beforeunload" not in e]
|
|
chk("...and it logged no script error", not errs, errs[:2])
|
|
|
|
|
|
# ── 3. the creator is a page ──────────────────────────────────────────────────
|
|
def page_checks(page, base, tok):
|
|
print("\n3. the creator is a page, not a panel")
|
|
page.clear_cookies()
|
|
page.set_cookie("wp_session", tok["root"])
|
|
page.viewport(1440)
|
|
page.goto(base + "/wp-creation-index.html?project=projA")
|
|
chk("the creator boots", wait_creator(page))
|
|
settle(1.4)
|
|
|
|
chk("it hosts no iframe", page.eval("document.querySelectorAll('iframe').length") == 0)
|
|
chk("the app bar mounted", page.eval("!!document.querySelector('.wp-chrome')"))
|
|
chk("...with the project switcher in it",
|
|
page.eval("!!document.querySelector('.wp-chrome .wpc-proj-name, .wp-chrome button')"))
|
|
chk("the navigation drawer mounted", page.eval("!!document.querySelector('#wp-sidenav')"))
|
|
chk("the tab strip is present", page.eval(
|
|
"document.querySelectorAll('.main-nav .nav-tab').length") == 3,
|
|
page.eval("document.querySelectorAll('.main-nav .nav-tab').length"))
|
|
chk("...marking Work Package Creation as the current one", page.eval("""(() => {
|
|
const cur = document.querySelector('.nav-tab[aria-current="page"]');
|
|
return !!cur && cur.getAttribute('data-tab') === 'wp';
|
|
})()"""), page.eval("""(() => { const c = document.querySelector('.nav-tab[aria-current="page"]');
|
|
return c ? c.getAttribute('data-tab') : '(none)'; })()"""))
|
|
chk("...and the SOP tab is a link back to the wizard, carrying the project",
|
|
page.eval("""(() => {
|
|
const a = document.querySelector('a.nav-tab');
|
|
const h = a ? (a.getAttribute('href') || '') : '';
|
|
return h.indexOf('work-package-suite.html') === 0 && h.indexOf('project=projA') > 0;
|
|
})()"""),
|
|
page.eval("(document.querySelector('a.nav-tab')||{}).getAttribute"
|
|
" ? document.querySelector('a.nav-tab').getAttribute('href') : '(none)'"))
|
|
|
|
# Switching to the board must re-mark the strip; a tab row that lies about
|
|
# where you are is worse than none.
|
|
page.eval("showDashboard()")
|
|
settle(1.2)
|
|
chk("opening the board moves the current marker", page.eval("""(() => {
|
|
const cur = document.querySelector('.nav-tab[aria-current="page"]');
|
|
return !!cur && cur.getAttribute('data-tab') === 'dashboard';
|
|
})()"""))
|
|
page.eval("showForm()")
|
|
settle(1.0)
|
|
chk("...and going back to the form moves it back", page.eval("""(() => {
|
|
const cur = document.querySelector('.nav-tab[aria-current="page"]');
|
|
return !!cur && cur.getAttribute('data-tab') === 'wp';
|
|
})()"""))
|
|
|
|
print("\n D1: the controls body.embedded used to hide")
|
|
vis = json.loads(page.eval("""(() => {
|
|
const out = {};
|
|
[...document.querySelectorAll('#wp-toolbar button')].forEach(b => {
|
|
const r = b.getBoundingClientRect();
|
|
out[(b.textContent || '').trim()] = r.width > 0 && r.height > 0;
|
|
});
|
|
return JSON.stringify(out);
|
|
})()"""))
|
|
# "Usage data" left this list at T7.10: D5 moved the report to the admin
|
|
# console. Its absence HERE is asserted, so the button cannot quietly return
|
|
# and recreate the duplicate D5 existed to remove.
|
|
# "Sample SOP" + "Load example" became the ONE "Load sample data" at T9.4
|
|
# (S7): D1's point was that the sample control is reachable, and it still is.
|
|
for label in ("Load sample data", "View SOP", "Import SOP"):
|
|
chk("%-13s is visible on the unframed page" % label, vis.get(label) is True, vis)
|
|
chk("Usage data is GONE from the creator toolbar (D5)",
|
|
"Usage data" not in vis, vis)
|
|
chk("...and every one of them has a handler that exists", page.eval("""(() => {
|
|
return [...document.querySelectorAll('#wp-toolbar button')].every(b => {
|
|
const m = (b.getAttribute('onclick') || '').match(/^([A-Za-z_$][\\w$]*)\\(/);
|
|
return !m || typeof window[m[1]] === 'function';
|
|
});
|
|
})()"""), page.eval("""(() => {
|
|
return JSON.stringify([...document.querySelectorAll('#wp-toolbar button')]
|
|
.map(b => (b.getAttribute('onclick') || ''))
|
|
.filter(o => { const m = o.match(/^([A-Za-z_$][\\w$]*)\\(/);
|
|
return m && typeof window[m[1]] !== 'function'; }));
|
|
})()"""))
|
|
|
|
|
|
# ── 4. old addresses ──────────────────────────────────────────────────────────
|
|
def address_checks(page, base, tok):
|
|
print("\n4. every address that reached the creator through the frame still reaches it")
|
|
# These are in bookmarks, in wp-sidenav's link map, and they are the shape the
|
|
# CR-011 and CR-014 emails were specified against (X1). Breaking them silently
|
|
# is the one regression this task could ship that nobody would notice for weeks.
|
|
for label, path, want in (
|
|
("?tab=wp opens the creator", "/work-package-suite.html?project=projA&tab=wp", None),
|
|
("?view=dashboard opens the board",
|
|
"/work-package-suite.html?project=projA&view=dashboard", "Dashboard"),
|
|
("?wp=<id> opens that package",
|
|
"/work-package-suite.html?project=projA&wp=wpA1", "wpA1"),
|
|
):
|
|
page.clear_cookies()
|
|
page.set_cookie("wp_session", tok["root"])
|
|
page.goto(base + path)
|
|
settle(0.8)
|
|
landed = wait_creator(page)
|
|
chk(label, landed and "wp-creation-index.html" in page.eval("location.pathname"),
|
|
page.eval("location.pathname + location.search"))
|
|
settle(1.2)
|
|
if want == "Dashboard":
|
|
chk("...on the board, not the form", page.eval("""(() => {
|
|
const dv = document.getElementById('dashboard-view');
|
|
return !!dv && getComputedStyle(dv).display !== 'none';
|
|
})()"""))
|
|
elif want == "wpA1":
|
|
chk("...with that package in the form", "horn/strobe" in (page.eval(
|
|
"(document.getElementById('wp_subject')||{}).value||''") or ""),
|
|
page.eval("(document.getElementById('wp_subject')||{}).value||''"))
|
|
|
|
print("\n ...and the forward is a replace, so Back is not a bounce")
|
|
page.clear_cookies()
|
|
page.set_cookie("wp_session", tok["root"])
|
|
page.goto(base + "/index.html?project=projA")
|
|
settle(1.4)
|
|
page.eval("location.assign('work-package-suite.html?project=projA&tab=wp')")
|
|
settle(0.8)
|
|
wait_creator(page)
|
|
settle(1.2)
|
|
page.eval("history.back()")
|
|
settle(1.8)
|
|
where = page.eval("location.pathname")
|
|
chk("Back from a forwarded link returns to where you came from",
|
|
"index.html" in where, where)
|
|
|
|
|
|
# ── 5. the gate ───────────────────────────────────────────────────────────────
|
|
def gate_checks(page, base, tok):
|
|
print("\n5. the SOP gate still gates, and says so")
|
|
# projB has members but no SOP.
|
|
page.clear_cookies()
|
|
page.set_cookie("wp_session", tok["root"])
|
|
page.goto(base + "/work-package-suite.html?project=projB&tab=sop")
|
|
settle(1.8)
|
|
chk("with no SOP, the creator tab is marked unavailable", page.eval("""(() => {
|
|
const a = document.querySelector('.nav-tab[data-tab="wp"]');
|
|
return !!a && a.getAttribute('aria-disabled') === 'true';
|
|
})()"""), page.eval("""(() => { const a = document.querySelector('.nav-tab[data-tab="wp"]');
|
|
return a ? (a.getAttribute('aria-disabled') || '(not set)') : 'missing'; })()"""))
|
|
chk("...but it is still a real control, in the tab order", page.eval("""(() => {
|
|
const a = document.querySelector('.nav-tab[data-tab="wp"]');
|
|
if (!a) return false;
|
|
a.focus();
|
|
return document.activeElement === a;
|
|
})()"""))
|
|
page.eval("""(() => { document.querySelector('.nav-tab[data-tab="wp"]').click(); })()""")
|
|
settle(1.2)
|
|
chk("...clicking it does not navigate", "work-package-suite.html" in page.eval("location.pathname"),
|
|
page.eval("location.pathname"))
|
|
chk("...it opens the gate panel instead", page.eval("""(() => {
|
|
const g = document.getElementById('wp-gate');
|
|
return !!g && getComputedStyle(g).display !== 'none';
|
|
})()"""))
|
|
chk("...which says what to do about it", "SOP" in (page.eval(
|
|
"(document.getElementById('wp-gate')||{}).textContent||''") or ""))
|
|
|
|
|
|
# ── 6. the backlog entries logged against this file ───────────────────────────
|
|
def measurement(page, base, tok):
|
|
print("\n6. the four backlog entries logged against the creator, re-measured")
|
|
page.clear_cookies()
|
|
page.set_cookie("wp_session", tok["root"])
|
|
|
|
# BL-001: the creator laid out 485px of content in a 390px viewport, because a
|
|
# runtime <style> injected --nav-w:288px with no media query and beat the
|
|
# stylesheet's own breakpoint.
|
|
page.viewport(390, 900, mobile=True)
|
|
page.goto(base + "/wp-creation-index.html?project=projA")
|
|
wait_creator(page)
|
|
settle(1.4)
|
|
over = json.loads(page.eval("""JSON.stringify({
|
|
scroll: document.documentElement.scrollWidth,
|
|
client: document.documentElement.clientWidth,
|
|
navw: getComputedStyle(document.body).getPropertyValue('--nav-w').trim(),
|
|
})"""))
|
|
print(" BL-001 390px: scrollWidth %s vs clientWidth %s, --nav-w %s"
|
|
% (over["scroll"], over["client"], over["navw"] or "(unset)"))
|
|
if over["scroll"] > over["client"] + 2:
|
|
print(" still reproduces. Widest boxes: %s" % page.eval(WIDEST_JS))
|
|
# CLOSED at T9.5. The pin below held this failure in view from T7.1 until the
|
|
# cause was actually removed: the help-tip's CSS ::after escaped its badge to
|
|
# the right, and rebuilding the component (S8) with a viewport-clamped bubble
|
|
# ended the overflow. The check now asserts the FIX, so a regression reopens
|
|
# BL-001 loudly instead of quietly re-widening the page.
|
|
chk("BL-001 is closed: the creator does not overflow at 390px",
|
|
over["scroll"] <= over["client"] + 2, over)
|
|
|
|
# BL-013: outline:none on every input, replaced by a 3px #edf5ff glow on white -
|
|
# a 1.05:1 edge. T7.2 owns the fix; this records whether the rebuild changed it,
|
|
# so T7.2 inherits a measurement instead of an assumption.
|
|
page.viewport(1440)
|
|
page.goto(base + "/wp-creation-index.html?project=projA")
|
|
wait_creator(page)
|
|
settle(1.2)
|
|
# Without this the headless document is not focused, :focus-visible never
|
|
# matches, and EVERY control reports no ring - a reading that looks like a
|
|
# finding and is an artefact. It is `page.ws.call`, not `page.call`: an earlier
|
|
# draft of this probe used the latter inside a try/except and silently measured
|
|
# nothing at all, which is worse than not measuring.
|
|
page.ws.call("Emulation.setFocusEmulationEnabled", {"enabled": True})
|
|
chk("focus emulation is on, so a focus reading means something",
|
|
page.eval("document.hasFocus()") is True)
|
|
ring = page.eval("""(() => {
|
|
const el = document.querySelector('#wp_subject, .main input[type=text]');
|
|
if (!el) return 'no input found';
|
|
el.focus();
|
|
const cs = getComputedStyle(el);
|
|
return 'outline ' + cs.outlineStyle + ' ' + cs.outlineWidth
|
|
+ ' | box-shadow ' + cs.boxShadow
|
|
+ ' | focused=' + (document.activeElement === el);
|
|
})()""")
|
|
print(" BL-013 focused creator input: %s" % ring)
|
|
|
|
sheet = read("wp-creation-styles.css")
|
|
halves = len(re.findall(r"\b\d+\.5px", sheet))
|
|
radii = len(re.findall(r"border-radius:\s*(?!var\()", sheet))
|
|
print(" BL-006 half-pixel font sizes in wp-creation-styles.css: %d" % halves)
|
|
print(" BL-007 raw border-radius values in the same sheet: %d" % radii)
|
|
return {"bl001": over, "bl013": ring, "bl006": halves, "bl007": radii}
|
|
|
|
|
|
def main():
|
|
exe = cdp.find_browser()
|
|
if not exe:
|
|
print("no headless-capable browser found; set WP_BROWSER.")
|
|
return 2
|
|
|
|
tmpdir = tempfile.mkdtemp(prefix="wpsuite-frame-")
|
|
db_path = os.path.join(tmpdir, "check.db")
|
|
server = None
|
|
try:
|
|
tok = seed(db_path)
|
|
set_sop(db_path, {}) # a SOP the app can actually read (BL-018)
|
|
port = cdp.free_port()
|
|
base = "http://127.0.0.1:%d" % port
|
|
server = start_server(port, db_path)
|
|
if server is None:
|
|
print("the test server would not start.")
|
|
return 2
|
|
print("\nThe creator's iframe - B7 / T7.1\nTarget: %s" % base)
|
|
|
|
source_checks()
|
|
|
|
browser = cdp.Browser(exe)
|
|
page = browser.page()
|
|
try:
|
|
boot_checks(page, base, tok)
|
|
page_checks(page, base, tok)
|
|
address_checks(page, base, tok)
|
|
gate_checks(page, base, tok)
|
|
measurement(page, base, tok)
|
|
finally:
|
|
page.close()
|
|
browser.close()
|
|
|
|
print("\n" + "-" * 54)
|
|
total = len(_PASS) + len(_FAIL)
|
|
print("%d/%d checks passed." % (len(_PASS), total))
|
|
for f in _FAIL:
|
|
print(" - " + f)
|
|
if _FAIL:
|
|
return 1
|
|
print("\nResult: " + _c("ALL PASS", "32") + " - the creator is a page.")
|
|
return 0
|
|
finally:
|
|
if server is not None:
|
|
try:
|
|
server.terminate()
|
|
except Exception:
|
|
pass
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|