qqq 1.0.0: the command-line client and the full-screen terminal UI (qqq ui) for QQQ applications over the UI API (/qqq/v1), with @qrun-io/qqq-cli 1.0.0, @qrun-io/qqq-core 1.0.0, @qrun-io/qqq-ui-api-client 1.8.0 and @qrun-io/qqq-ui-api-conformance 1.6.0 going final together. Squash of the release/1.0.0 branch at 79ba68e (the branch is kept with the full signed history; the tags v1.8.0 and cli-v1.0.0 point at that commit, which is what the released binaries were built from). See CHANGELOG.md [1.0.0] and docs/release-notes/1.0.0.md.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
473 lines
73 KiB
Python
473 lines
73 KiB
Python
#!/usr/bin/env python3
|
||
"""Generates docs/parity/feature-matrix.md: the Next UI acceptance matrix rows with a CLI and a TUI column.
|
||
|
||
Usage: python3 scripts/parity-matrix.py <next-feature-matrix.md> [out.md]
|
||
|
||
Statuses: pass (verified against the sample server), partial (works with gaps, noted), todo (planned),
|
||
n/a (browser-only, noted). Edit STATUS below when a row changes; the summary counts are computed.
|
||
"""
|
||
import re, sys, collections
|
||
|
||
SRC = sys.argv[1]
|
||
OUT = sys.argv[2] if len(sys.argv) > 2 else 'docs/parity/feature-matrix.md'
|
||
|
||
# id -> (cli, note) ; family default is todo
|
||
P, T, N, X, B = 'pass', 'todo', 'partial', 'n/a', 'blocked' # T is the default; B (blocked) notes cite a server finding
|
||
B = 'blocked' # waits on a server finding or issue, named in the note
|
||
STATUS = {
|
||
# NAV: the CLI has no shell; the TUI covers these in M3
|
||
**{f'NAV-{i:03d}': (X, 'browser shell') for i in (1,2,3,4,5,12,13,14,15,16,18,19,20,21,22,26,29,32,33,34,35,36,37,38,55,56)},
|
||
'NAV-006': (P, 'hidden tables and processes are not listed; direct use is refused by the server'),
|
||
'NAV-007': (P, 'qqq table get on a hidden table the user may read'),
|
||
'NAV-008': (P, 'qqq apps show <app>: sections of processes, reports, tables and child apps, then widgets'),
|
||
'NAV-009': (P, 'qqq apps show counts each table with TABLE_COUNT (--no-counts skips)'),
|
||
'NAV-010': (X, 'app home rendering'), 'NAV-011': (P, 'qqq apps show <app> --widgets: one labelled section per permitted declared widget, loaded once each; verified live on SampleWidgetsDashboard'),
|
||
'NAV-017': (P, 'qqq home: counts of what you may open, quick actions (permitted processes in your apps, with the command) and one card per app; verified live'),
|
||
'NAV-023': (P, 'qqq table get <t> --key field=value: one match shows the record, none exits 5, several exit 2'),
|
||
'NAV-024': (P, "qqq find <text> [--type]: apps, tables, processes and reports by label with type, app and the command that opens each (the terminal's palette); verified live; shell completion covers names"),
|
||
'NAV-025': (P, 'qqq search'), 'NAV-027': (P, 'qqq whoami: apiVersion, features, counts'),
|
||
'NAV-028': (P, "qqq report list (with app) and apps show list reports in the app's Reports group, report run opens them; unit-tested and stub-server tested (the sample has no reports)"), 'NAV-030': (P, 'qqq search over POST /search'), 'NAV-031': (P, 'server-enforced; refusals shown'),
|
||
# PRF
|
||
'PRF-001': (P, '10,000 rows with --all and --page-size'), 'PRF-002': (P, '--limit/--skip paging'), 'PRF-003': (P, 'server sort and filter'),
|
||
'PRF-004': (P, 'apps show --widgets loads every widget once, all at once, in declared order: 24 widgets (statistics, charts, 100-row tables) answering in 150 ms each finish well under 3 s; stub-server tested'), 'PRF-005': (P, 'process run polls every 500 ms and prints progress'),
|
||
# PRC
|
||
'PRC-001': (P, 'process run -r ids'), 'PRC-002': (P, '--filter-json'), 'PRC-003': (P, ''), 'PRC-004': (P, ''), 'PRC-005': (X, 'grid selection; use -r'),
|
||
'PRC-006': (P, 'checked before init (filters counted); the sample declares no limits, verified by tests'), 'PRC-007': (P, 'every component in order as text (kernel screen model); bulk load forms belong to qqq bulk'),
|
||
'PRC-008': (P, 'text with line breaks; previewText printed above it'), 'PRC-009': (P, 'includeFieldNames and values kept across steps'), 'PRC-010': (P, 'required checked locally and by the server'),
|
||
'PRC-011': (P, 'labels or ids, prompts list choices'), 'PRC-012': (P, 'label: value on every step'), 'PRC-013': (P, '{step}.html as text (tags stripped)'), 'PRC-014': (P, 'GET /download/{file} saved to --out-dir, else the download directory; no sample process offers one, verified by tests'),
|
||
'PRC-015': (P, 'step helpContents shown on the screen, process help at start and in describe; none in the sample'), 'PRC-016': (P, 'b at a prompt when the step reports backStep (isStepBack=true); verified live: bulk insert -i back from the mapping to the upload and from the review to the mapping'), 'PRC-017': (P, 'updatedFrontendStepList followed'), 'PRC-018': (P, 'updatedFields applied'),
|
||
'PRC-019': (P, 'message, N/M, elapsed on stderr'), 'PRC-020': (P, 'Ctrl-C during a job or a prompt calls POST …/cancel, exit 130'), 'PRC-021': (P, 'userFacingError shown'), 'PRC-022': (X, 'retry is re-running the command'),
|
||
'PRC-023': (X, 'screen labels'), 'PRC-024': (X, 'stepper; step names printed'), 'PRC-025': (P, 'GET …/records paged into a table, --record-limit'), 'PRC-026': (P, 'count and preview; asks validate or skip on a terminal, --validate / --skip-validation; Submit? after validation'),
|
||
'PRC-027': (P, 'status, count, message lines'), 'PRC-028': (P, ''), 'PRC-029': (P, 'qqq table bulk-edit <t> -r ids|-w ... --set f=v --clear f (-i chooses fields): only the switched-on fields are sent (bulkEditEnabledFields), review, result; verified live on person'), 'PRC-030': (P, 'qqq table bulk-delete <t> -r ids|-w ...: review with count and preview, validation, result; asks unless --yes (exit 2 without a terminal); verified live'), 'PRC-031': (P, 'qqq table bulk-insert <t> --file x.csv: upload, column mapping (server suggestion cleaned per finding 26, --mapping document, --column/--default, -i), value mapping, review preview and validation, result; FLAT and TALL verified live'), 'PRC-032': (P, 'mapping checked before submit (unmapped field, unknown column, layout, WIDE repetition, key fields, repeated header); Back from mapping to upload and from review to mapping verified live with -i'),
|
||
'PRC-033': (P, 'qqq table bulk-edit <t> --file x.csv --key id [--mapping]: rows matched by the key, value mapping; verified live on person'), 'PRC-034': (P, 'upload instructions printed as text; --template [flat|tall|wide] [--out] saves the data: templates; verified live'), 'PRC-035': (P, 'named widget with the process UUID and values, kernel render model; none in the sample, verified by tests'), 'PRC-036': (P, 'seeded from the process value of the widget name'), 'PRC-037': (P, 'ad hoc composite: ${} interpolation, conditional blocks left out, INPUT_FIELD blocks asked as step fields (submitOnEnter submits), BUTTON actionCodes offered as a choice (-v actionCode=…), controlCode buttons change only what shows; unit-tested (the sample has no ad hoc widget)'), 'PRC-038': (X, 'Google Drive is web-only'), 'PRC-039': (X, 'Google Drive is web-only'),
|
||
'PRC-040': (X, 'no reload'), 'PRC-041': (P, 'UNAUTHENTICATED exits 3 with the sign-in hint'), 'PRC-042': (P, 'server-enforced'), 'PRC-043': (P, '-v values seed every step'),
|
||
'PRC-044': (X, 'scanner screens are interactive'), 'PRC-045': (P, 'the runner follows nextStep whatever the flow'), 'PRC-046': (P, 'qqq bulk-profile list|show|save|rename|delete <t> [--edit], bulk-insert --saved <name> and --save-profile; unit-tested and stub-server CLI tests: the sample has no saved-profile processes'), 'PRC-047': (P, 'completes on init'),
|
||
'PRC-048': (P, 'searchProcessPossibleValues'), 'PRC-049': (P, 'v1 routes only'), 'PRC-050': (X, 'Material-specific'), 'PRC-051': (P, 'dependent choices narrow after each answer'),
|
||
'PRC-058': (P, 'bulk-profile save --replace prints the changes and asks (--yes), rename, delete; stub-server CLI tests (the sample has no saved-profile processes)'), 'PRC-059': (P, 'profiles listed as yours, then shared by their owner; save over, rename and delete refused for a profile you do not own; stub-server CLI tests'), 'PRC-060': (P, "-i lists columns with preview values and the step's field help, asks for the header row (--no-header), warns on repeated headers (only the first is read); header toggle and previews verified live, help and repeated headers unit-tested"), 'PRC-061': (P, 'defaults typed per field (--default f=label resolves possible values; yes/no booleans), value mapping by label or id with choices listed, -i add-fields list grouped by table; verified live (Dog/Cat to species ids, yes/no to booleans)'), 'PRC-062': (X, 'composite editors'), 'PRC-063': (X, ''), 'PRC-064': (P, 'process values typed local, sent UTC; screens show the local zone'), 'PRC-065': (X, 'Google Drive'), 'PRC-066': (P, 'field behaviors applied before submit'),
|
||
# QRY
|
||
'QRY-001': (P, 'default order by primary key'), 'QRY-002': (P, '--limit, --skip, --all'), 'QRY-003': (P, '--order, -field for descending'), 'QRY-004': (P, 'qqq table columns <t> [fields…|--reset]: kept per table in the config the TUI column chooser uses; table query defaults to them, --fields wins; verified live'),
|
||
'QRY-005': (X, 'grid presentation'), 'QRY-006': (X, ''), 'QRY-007': (P, 'error with code, status and trace id'), 'QRY-010': (P, 'all operators and shorthand; text matches (~, CONTAINS, STARTS_WITH, …) carry CASE_INSENSITIVE by default, ~= / OPERATOR:CS / --case-sensitive match case (finding 11 is by design; the v1 route drops the option until finding 27, qqq#1358)'), 'QRY-011': (P, 'BETWEEN, IN lists'),
|
||
'QRY-012': (P, 'now(), now(-7d), thisPeriod(), startOf()'), 'QRY-013': (P, '--where/--group DATE_TIME values local to UTC (a date is local midnight); expressions as before'), 'QRY-014': (P, 'IS_BLANK, TRUE, FALSE'), 'QRY-015': (P, 'labels in --where/--group for possible-value fields resolved to ids (speciesId = Cat sends 2; unknown or ambiguous labels exit 2 with the choices); verified live'),
|
||
'QRY-016': (P, '--group with parentheses'), 'QRY-017': (P, '--print-filter gives the JSON the web links accept'), 'QRY-018': (P, 'any operator in --where'), 'QRY-019': (P, '--search on query, count and export: CONTAINS over the shown text fields, ORed, ANDed with the filter; sent with CASE_INSENSITIVE (--case-sensitive to match case; the v1 route drops the option until finding 27, qqq#1358)'),
|
||
'QRY-020': (P, '--join and table.field in --fields/--where/--order, labelled "Join: Field"; verified live through an association join (person to pet); exposed joins unit-tested (the sample exposes none)'), 'QRY-021': (P, 'a many-side join repeats rows; count and --count add the distinct count (person --join pet: 21 rows, 16 distinct)'), 'QRY-022': (P, 'qqq table stats <t> <field> [-w …] through the columnStats process when offered (unit-tested; the sample has neither QUERY_STATS nor the process); --local computes the same figures and was verified live'), 'QRY-023': (P, 'without QUERY_STATS (or the process) table stats exits 2 with the reason; --local is an explicit opt-in'), 'QRY-030': (P, '-r ids, --filter-json, --limit'), 'QRY-031': (P, 'table bulk-edit -r ids; verified live'), 'QRY-032': (P, 'table bulk-delete -w ... (or --filter-json, --all-records) sends the whole filter; verified live'),
|
||
'QRY-033': (P, 'process run --filter-json with limit'), 'QRY-034': (P, 'qqq table actions <t>: bulk load, bulk edit/delete and the table processes with the command for each; verified live'), 'QRY-035': (P, 'server refuses; exit 3'), 'QRY-040': (P, 'table export: filtered (--where, --group, --search), sorted (--order), --fields in column order, --limit, -o/--out - (-o is the output file on every command)'), 'QRY-041': (P, 'csv, xlsx, json, tsv; format from --out extension; --no-header; bad format or field exit 2 before the request'),
|
||
'QRY-042': (X, 'nothing to disable'), 'QRY-043': (P, 'server refuses'), 'QRY-050': (P, 'qqq saved-view list <t>, open <t> <view>, table query --view; unit-tested and stub-server tested (the sample has no saved views)'), 'QRY-051': (P, 'saved-view save --name: filter, sort, columns and page size as the Material RecordQueryView; unit-tested and stub-server tested'), 'QRY-052': (P, 'saved-view save --replace, rename, delete (asks; --yes); unit-tested and stub-server tested'), 'QRY-053': (P, 'a name in use is refused before any change; an unknown view exits 2 with the list; unit-tested and stub-server tested'), 'QRY-054': (P, 'views shared with you are listed as shared by their owner; changes to them are refused by the server, its message shown; unit-tested and stub-server tested'),
|
||
'QRY-060': (P, '--variant on query, count, get, copy, export, writes and process run; a terminal asks; the chosen variant is remembered per server and table (--variant - forgets it); stub-server tested (the sample has no variant table)'), 'QRY-061': (P, 'no --variant, none remembered and no terminal (or no answer): exit 2, nothing queried; stub-server tested'), 'QRY-062': (P, 'describe shows capabilities; server refuses'), 'QRY-063': (P, 'server refuses writes'), 'QRY-064': (P, ''), 'QRY-065': (P, 'POST /table/{t}/export'),
|
||
'QRY-066': (P, 'GET /table/{t}/variants (called live; the sample lists none; the listing is stub-server tested)'), 'QRY-067': (P, 'table get --key; the primary key and Material gotoFieldNames are the declared keys, other fields looked up with a note'), 'QRY-068': (P, 'default columns follow sections by tier'), 'QRY-069': (X, 'locale formatting'), 'QRY-070': (X, 'shortcuts'), 'QRY-071': (P, 'process run --variant (or the remembered one): tableVariant on init, every step and status; sent live, unit-tested (no variant table to observe)'),
|
||
'QRY-080': (X, 'quick filters UI'), 'QRY-081': (X, ''), 'QRY-082': (X, ''), 'QRY-083': (X, ''), 'QRY-084': (X, ''), 'QRY-085': (B, "weekday(field) IN 1,7 sends the WeekdayOfDate fieldFunction with ISO weekdays (--print-filter); the sample's H2 backend answers 500 (server finding 24, qqq#1355)"), 'QRY-086': (P, 'expressions'), 'QRY-087': (P, 'IN lists; quoted values'), 'QRY-088': (X, ''), 'QRY-089': (P, 'behaviors'),
|
||
**{f'QRY-{i:03d}': (X, 'saved views and column UI') for i in (90,91,92,93,94,95,96)}, **{f'QRY-{i:03d}': (X, 'phone layout') for i in (8,9,24,36,55)},
|
||
# REL
|
||
'REL-001': (P, 'display values; table get shows the target (Avery Sample → person 1), --links lists them, --follow opens one'), 'REL-002': (P, 'table get --associations lists exactly the record\'s children; table children pages a list with its count (View All)'), 'REL-003': (P, 'table create <child> --parent table=pk; table get --associations shows it at once'), 'REL-004': (P, 'person > pets > notes: each pet lists only its own notes'), 'REL-005': (P, 'records are never cached: update or delete a child, the next get/children shows the current rows'), 'REL-006': (P, 'unreadable child tables shown as unavailable without data or actions; table children shows the server message; unit-tested (the sample has no denied child table, finding 17)'), 'REL-007': (P, 'aliased and composite associations bound to their widget sections, members by every join value; unit-tested (the sample declares none, finding 17)'), 'REL-008': (P, 'table create --parent sets every join value of a composite association; unit-tested (the sample declares none)'), 'REL-009': (P, 'table delete of a parent: its pets are gone, other parents keep theirs'),
|
||
# REC
|
||
'REC-001': (P, 'table get by section and tier'), 'REC-002': (P, 'empty values show --; hidden sections and fields never shown; unit-tested (the sample has none, finding 17)'), 'REC-003': (P, 'exit 5'), 'REC-004': (X, 'tabs'), 'REC-005': (P, 'validated locally before any request'), 'REC-006': (P, ''), 'REC-007': (P, ''), 'REC-008': (X, 'guard'),
|
||
'REC-009': (P, 'update sends only changed fields'), 'REC-010': (P, 'non-editable fields excluded'), 'REC-011': (P, ''), 'REC-012': (P, 'confirm unless --yes'), 'REC-013': (P, 'qqq table copy <t> <pk> [--set f=v] [-i] [--dry-run]: primary key, non-editable and file fields dropped'), 'REC-014': (P, ''), 'REC-015': (P, ''), 'REC-016': (P, 'defaults applied'),
|
||
'REC-017': (P, ''), 'REC-018': (P, 'typed and shown in TZ, stored UTC, --utc for raw; DST edges tested'), 'REC-019': (X, 'display zones'), 'REC-020': (P, 'HTML fields shown as text (sanitized); table update -e <field> edits the HTML source in $VISUAL/$EDITOR; verified live on fieldLab htmlValue'), 'REC-021': (P, 'server masks'), 'REC-022': (P, 'table create/update --upload and table download (base64 BLOB value) verified on fieldLab; default name into the download directory'), 'REC-023': (P, 'server'),
|
||
'REC-024': (P, 'maxLength checked locally'), 'REC-025': (P, 'behaviors'), 'REC-026': (P, 'ranges checked locally'), 'REC-027': (P, 'server message'), 'REC-028': (P, 'record links name table and key, --follow opens them; URL links show the URL'), 'REC-029': (P, 'CHIP as [value] colored by color.<value> (or colorMap) on a terminal, icon named in the record view; unit-tested (none in the sample)'), 'REC-030': (X, ''), 'REC-031': (P, "CODE_EDITOR values shown verbatim; table update -e <field> opens the code in $EDITOR with the language's extension; unit-tested (none in the sample; -e verified live on an HTML field)"), 'REC-032': (P, 'RENDER_HTML shown as sanitized text; unit-tested (none in the sample)'),
|
||
'REC-033': (P, 'REVEAL masked in the record view, query and child lists until --reveal; table get --copy <field> copies the value (pbcopy/clip/wl-copy/xclip, else OSC 52); masking unit-tested, --copy verified live'), 'REC-034': (P, 'FILE_DOWNLOAD shows the file name and qqq table download saves it through the field route; unit-tested (none in the sample); BLOB values without the adornment are decoded locally (server finding 16)'), 'REC-035': (P, "create/update --upload field=path replaces the file, --set field= removes it (verified live on a BLOB); FILE_UPLOAD's format (button or drag and drop) is presentation only"), 'REC-036': (P, 'tooltip text in parentheses after the value; unit-tested (none in the sample)'), 'REC-037': (P, "the value rendered through the named widget's render model; unit-tested (none in the sample)"), 'REC-038': (P, 'ERROR values marked with !; unit-tested (none in the sample)'), 'REC-039': (P, 'table get --help-content: field help by view-screen role, HTML/MARKDOWN as text; qqq help show lists help alone; unit-tested (the sample has no help, finding 17)'), 'REC-040': (P, 'section help under the section with --help-content; unit-tested (finding 17)'), 'REC-041': (P, 'table helpContent and helpContents slots printed with --help-content; unit-tested (finding 17)'), 'REC-042': (P, 'qqq audit list <t> <pk> [--asc]: newest first with field changes; node tests and a stub server (the sample audits no table)'), 'REC-043': (P, 'without the audit process the audit table is read with its auditTable and auditDetail joins; node tests'),
|
||
'REC-044': (P, 'recordLabel'), 'REC-045': (P, 'labels resolved, defaults looked up'), 'REC-046': (P, 'server'), 'REC-047': (X, ''), 'REC-048': (P, 'fieldErrors by field'), 'REC-049': (X, ''), 'REC-050': (P, ''), 'REC-051': (P, ''), 'REC-052': (P, 'contingent fields'),
|
||
'REC-053': (P, 'qqq table apis <t>, api-doc <t> <api> <version> (saved as JSON), api-call <t> <api> <version> <method> [path] (the playground: sends the request with your session, prints status and answer); none in the sample (verified: none listed), api-call stub-server tested'), 'REC-054': (P, 'qqq script list|show [--revision]|store --file/--editor|test -v input=…|logs, and table developer <t> <pk> (panels verified live on person 1); scripts and test runs stub-server tested (the sample has no associated scripts or testScript process)'), 'REC-055': (X, ''), 'REC-056': (X, ''), 'REC-057': (X, ''), 'REC-058': (X, ''), 'REC-059': (X, ''), 'REC-060': (P, 'form adjusters (onLoad, onChange) and field behaviors run through the kernel FormSession for create, update and copy; unit-tested (the sample declares none)'), 'REC-061': (P, "qqq table widget-form <t> <pk> <widget> --set|-i: the dynamicForm's fields validated (required) and saved in its merged JSON field (or as record fields); stub-server tested (the sample has no dynamicForm)"), 'REC-062': (P, 'inline enums'), 'REC-063': (B, 'get, copy and update -i read with --variant; the v1 insert, update and delete routes ignore it (server finding 19, qqq#1351)'), 'REC-064': (P, 'table get --join adds joined fields (queryJoins); verified live through an association join; exposed-join sections unit-tested'), 'REC-065': (P, 'an unchanged wall-clock time keeps the original instant and its precision'),
|
||
# SEC
|
||
'SEC-001': (P, ''), 'SEC-002': (P, 'a DISABLED table (permissions false in the metadata) is listed by table list with the reason; query, get, create, edit, delete, export and bulk commands refuse with it (exit 3) before any request; stub-server tested (the sample has no DISABLED table)'), 'SEC-003': (P, ''), 'SEC-004': (P, ''), 'SEC-005': (P, ''), 'SEC-006': (P, 'table copy needs insert permission: refused with the reason (exit 3) before reading the record; stub-server tested'), 'SEC-007': (P, ''), 'SEC-008': (P, 'a DISABLED process (hasPermission false) is listed by process list and table actions with the reason; process run refuses (exit 3) before init; stub-server tested'), 'SEC-009': (P, 'widget get shows the refusal'), 'SEC-010': (P, ''),
|
||
'SEC-011': (P, "server-enforced: saved views (and bulk load profiles) are listed as the server returns them, yours first, others as shared by their owner; changing someone else's is refused locally or by the server; stub-server tested"), 'SEC-012': (P, 'server'), 'SEC-013': (P, 'server'), 'SEC-014': (P, ''), 'SEC-015': (P, ''), 'SEC-020': (P, 'qqq login on a MOCK server'), 'SEC-021': (P, 'qqq logout calls POST /logout'), 'SEC-022': (P, 'exit 3 with the sign-in hint'),
|
||
'SEC-023': (P, ''), 'SEC-024': (X, ''), 'SEC-025': (P, 'device flow and --browser (code + PKCE, loopback) verified against a sample on OAUTH2 with qqq-dev-idp'), 'SEC-026': (P, 'denial, forged state and a refused code exchange exit 3 and save nothing; the token is checked against the server first'), 'SEC-027': (P, 'logout --provider-logout: refresh token revoked (RFC 7009), end-session URL opened; bearer sign-ins have no server session'), 'SEC-028': (P, 'a refresh the provider refuses exits 3 with sign in again; refresh before expiry verified'), 'SEC-029': (B, 'qqq login on an AUTH_0 server: tenant from values.baseUrl, values.nativeClientId (else clientId), the audience sent so the token is a JWT; device flow and --browser (PKCE) unit-tested; no AUTH_0 server to run against: the sample configures only OAUTH2 (server finding 28, qqq#1359)'), 'SEC-030': (B, 'same sign-in as SEC-029; a real Auth0 tenant needs tenant credentials from James (qqq-cli#10; server finding 28, qqq#1359)'), 'SEC-031': (P, ''), 'SEC-032': (P, 'unknown type refused'),
|
||
'SEC-033': (X, ''), 'SEC-034': (P, "qqq login --username (password asked, or QQQ_PASSWORD): HTTP Basic once, the server's sessionId cookie kept, never the password; stub-server tested (the sample uses MOCK, so it cannot exercise TABLE_BASED; finding 8 is the missing manageSession recipe)"), 'SEC-035': (P, "a wrong password or unknown user: one message (the server's), exit 3, nothing stored; stub-server tested"), 'SEC-036': (P, "an expired or idle session answers 401: exit 3 with 'Sign in again with qqq login'; stub-server tested"), 'SEC-037': (P, 'qqq logout calls POST /qqq/v1/logout with the sessionId cookie and forgets the sign-in; later commands are unauthenticated; stub-server tested'), **{f'SEC-{i:03d}': (X, 'browser security') for i in (38,39,40,41,42,43,44,48,49,50)},
|
||
# INT
|
||
**{f'INT-{i:03d}': (X, 'browser accessibility and layout') for i in (1,2,3,4,5,6,8,9,11)}, 'INT-007': (P, 'field named in every message'), 'INT-010': (P, 'exit 4 and a message; rerun'), 'INT-012': (P, 'no client cache of records'), 'INT-013': (P, '(no records)'), 'INT-014': (P, 'cannot reach the server'),
|
||
# WID
|
||
**{f'WID-{i:03d}': (P, 'rendered as text') for i in (1,2,3,4,5,7,8,9,10,13,14,16,17,18,19,23,24,46,51,54,59,61)},
|
||
'WID-006': (P, 'generic: its html as text, plus label, sublabel and footer; unit-tested (none in the sample)'), 'WID-011': (P, 'location: image link, title, description, address and footer text; unit-tested (none in the sample)'), 'WID-012': (P, 'multiStatistics: groups with icon, header and subheader, statisticList values with separators (links shown); verified live on SampleMultiStatisticsWidget'), 'WID-015': (P, 'prints the URL'), 'WID-020': (P, "usaMap: each marker with name and coordinates as a table, 'No locations to show' when none; unit-tested"), 'WID-021': (P, "a process widget names its process; widget get --run runs it here with the widget's default values; stub-server tested (no process widget in the sample)"), 'WID-022': (P, 'parentWidget: children loaded and shown in order, each sized n/12 (gridColumns; TABS as tab headings); unit-tested'),
|
||
'WID-025': (X, 'customComponent is web-only'), **{f'WID-{i:03d}': (T, 'developer and editor widgets') for i in (26,27,28,29,30,31,32,70,71,72,73,74,75)}, 'WID-033': (X, 'QuickSight'), 'WID-040': (P, 'apps show --widgets: app widgets in declared order, each marked with its width in twelfths (gridColumns); verified live'), 'WID-041': (P, "chrome: a card widget's title framed (== label ==), plain (isCard=false) without; sublabel; payload or metadata footer HTML as text; unit-tested and verified live"),
|
||
**{f'WID-{i:03d}': (X, 'card chrome') for i in (42,43,44,45,55,58,62,64,65)}, 'WID-047': (P, '--input name=value'), 'WID-048': (P, '--input'), 'WID-049': (P, "a widget waiting for a required dropdown shows the server's dropdownNeedsSelectedText in place of its body; selections given with --input name=id; unit-tested"), 'WID-050': (X, ''), 'WID-052': (P, "every display type shows the web's empty state (No rows found, No chart data available, No statistics available, …); unit-tested"), 'WID-053': (P, 'unknown shapes shown raw'),
|
||
'WID-056': (P, 'parent widgets skip unknown and denied (hasPermission false) children without a request and say which; unit-tested'), 'WID-057': (P, 'composite: TEXT (icons, alert/banner), NUMBER_ICON_BADGE, UP_OR_DOWN_NUMBER, TABLE_SUB_ROW_DETAIL_ROW, PROGRESS_BAR, DIVIDER, BIG_NUMBER, INPUT_FIELD, BUTTON, AUDIO, IMAGE, ICON, nested COMPOSITE, tooltips; unit-tested, BIG_NUMBER and UP_OR_DOWN verified live'), 'WID-060': (P, '--input id=…'), 'WID-063': (P, ''), 'WID-066': (P, ''), 'WID-067': (P, 'a payload that is one leaf block renders that block; unit-tested'), 'WID-068': (P, 'table extras: hidden columns dropped, numbers with separators, html, htmlAndTooltip, image cells, composite cells, sub-rows, sticky last (totals) row, link and noRowsFoundHTML; unit-tested, sticky row verified live on SampleTableWidget'), 'WID-069': (X, 'theme colors'),
|
||
# RPT
|
||
'RPT-001': (P, "report run <r>: through the report's process (reportName, reportFormat), the file from GET /download saved (--out-dir); stub-server tested (the sample has no reports)"), 'RPT-002': (P, 'report run --format xlsx: the same process, the XLSX file as the server writes it; stub-server tested'), 'RPT-003': (P, 'report run --format json: the same process, the JSON file; stub-server tested (format path shared with csv/xlsx)'), 'RPT-004': (P, "SUMMARY reports: the server's grouped rows with counts and totals saved as sent (process or report route); stub-server tested"), 'RPT-005': (P, 'PIVOT reports: the server writes the native XLSX pivot; the CLI saves the file unchanged; stub-server tested'), 'RPT-006': (P, 'report input fields asked on a terminal or given with -v; a missing required one exits 2 naming it before the report runs; the value is sent to the step; stub-server tested'), 'RPT-007': (P, 'server'), 'RPT-008': (P, 'reports listed by report list and under their app in apps show; report run opens them; stub-server tested'),
|
||
**{f'RPT-{i:03d}': (T, 'saved reports, schedules, sharing') for i in (9,10,11,12,13,14,15,16,17,18,19)}, 'RPT-020': (P, 'report files come from GET /download (process reports) or GET /reports/{name} (route); stub-server tested'),
|
||
# HLP
|
||
'HLP-001': (P, 'qqq help show app|widget|instance; unit-tested (the sample carries no help content, finding 17)'), 'HLP-002': (P, 'qqq help show table <t> [field]: table, section and field help; unit-tested (finding 17)'), 'HLP-003': (P, 'field help is the column help; table action help slots printed with their slot name; unit-tested (finding 17)'), 'HLP-004': (P, 'qqq help show process <p>: process, step, HELP_TEXT and step field help; unit-tested (finding 17)'), 'HLP-005': (P, 'qqq help-search'),
|
||
'QRY-093': (X, 'no screen state to keep: views are opened by name'), 'QRY-094': (P, 'saved-view list counts quick views with doCount (--counts counts all); unit-tested (no sample saved views)'), 'QRY-096': (P, "opening a view drops removed fields with Material's warnings on stderr; unit-tested (no sample saved views)"), 'HLP-006': (X, 'author affordance'),
|
||
'WID-026': (P, "cronUI: the record's cron expression, the payload's description and the time zone (widget get --record t=pk); 'No schedule set'; unit-tested"),
|
||
'WID-027': (P, 'dynamicForm: labeled values (display values first, or the merged JSON field); noFieldsMessage; unit-tested'),
|
||
'WID-028': (P, "dataBagViewer: the bag's versions newest first (current marked) and the current version's data as JSON; unit-tested"),
|
||
'WID-029': (P, "qqq saved-report show prints the pivot definition (rows, columns, 'Sum of Total'); saved-report pivot sets it (--rows/--columns/--values FUNCTION:field) or --clear clears it; stub-server tested"),
|
||
'WID-030': (P, 'qqq saved-report show: filter (match all/any), sort and visible columns as the setup widget words them; stub-server tested'),
|
||
'WID-031': (P, 'rowBuilder without association binding: its rows read-only in a table (declared fields, display values); unit-tested'),
|
||
'WID-032': (P, "scriptViewer: the script's revisions (current marked) and the current revision's files; unit-tested"),
|
||
'WID-070': (P, 'qqq saved-report set: filter and sort (--where/--group/--order, ${variable.x} filter variables), visible columns (--columns), name; written to queryFilterJson/columnsJson as the setup does; stub-server tested'),
|
||
'WID-071': (P, 'a process field drawn by a filterAndColumnsSetup widget takes the filter grammar (-v field="a = 1 AND b > 2") and writes its QueryFilter JSON; stub-server tested'),
|
||
'WID-072': (P, 'qqq saved-report preview --api-path/--api-version queries and counts through the application API, as API-versioned setups preview; stub-server tested'),
|
||
'WID-073': (P, 'qqq saved-report preview pages, sorts and counts on the server (-l, --skip, -o); columns fit their content (dragging widths is a browser grid control); stub-server tested'),
|
||
'WID-074': (X, 'standalone composite controls are browser input editors (caret, IME, widths) whose drafts stay on the page; the CLI shows the controls and their values (WID-057), and process-hosted inputs go through the form session (PRC-037)'),
|
||
'WID-075': (P, 'a seeded DATE_TIME input block is shown in the local zone with its seconds (kernel displayDateTime); unit-tested'),
|
||
'RPT-009': (P, 'qqq saved-report show: filter, sort, columns and pivot setup; stub-server tested'),
|
||
'RPT-010': (P, 'qqq saved-report run <r> [--format]: renderSavedReport with the record, the file downloaded; stub-server tested'),
|
||
'RPT-011': (P, 'report run on a report without a process (or --route) streams from GET /reports/{name}; stub-server tested'),
|
||
'RPT-012': (P, "qqq saved-report schedule <r> --cron --time-zone --to [--format]; saved-report schedules lists them with the server's cron description; stub-server tested"),
|
||
'RPT-013': (P, 'qqq share add savedReport <id> user:<id> --scope read-only (insertSharedRecord); stub-server tested'),
|
||
'RPT-014': (P, 'qqq share scope savedReport <id> <share> read-write (editSharedRecord); stub-server tested'),
|
||
'RPT-015': (P, 'qqq share remove savedReport <id> <share> (asks; --yes); stub-server tested'),
|
||
'RPT-016': (P, 'server-enforced record security: saved-report list shows only what the server returns (yours, shared with you); stub-server tested'),
|
||
'RPT-017': (P, "only the owner may share: the server's refusal shown (exit 2); stub-server tested"),
|
||
'RPT-018': (P, 'qqq share … savedView <id>: the same sharing processes for saved views; stub-server tested with saved reports'),
|
||
'RPT-019': (P, "the render step shows only its fields (kernel screen model); a step without fields shows no 'no fields' message; unit-tested"),
|
||
}
|
||
|
||
# TUI column (M3, qqq ui): rows verified in tmux against the sample server or by the in-memory tests in
|
||
# packages/tui/test. Rows not listed fall back to n/a for browser-only rows and todo for the rest.
|
||
TUI = {
|
||
'NAV-001': (P, 'qqq ui table query <t>; :table query <t>'), 'NAV-002': (P, 'Apps tree, backend order'), 'NAV-003': (P, 'nested apps in the tree'),
|
||
'NAV-004': (P, 'Enter opens; current entry marked; arrows expand and collapse'), 'NAV-005': (N, 'markers for processes and reports; icons not drawn'),
|
||
'NAV-006': (P, 'hidden tables and processes are not listed'), 'NAV-007': (P, ':table query and :record open hidden tables the user may read'),
|
||
'NAV-008': (P, 'app home: sections with tables, processes, reports'), 'NAV-009': (P, 'record counts beside tables'), 'NAV-010': (P, ''),
|
||
'NAV-011': (P, 'dashboard apps draw their widgets'), 'NAV-012': (N, 'app name in the title bar'), 'NAV-014': (P, 'title-bar path: apps, table, record or form'),
|
||
'NAV-017': (N, 'home: counts and app list; no quick actions'), 'NAV-018': (P, 'qqq ui <route in the CLI grammar>'), 'NAV-023': (P, 'g: primary key or a configured unique key (kernel gotoKeys)'),
|
||
'NAV-024': (P, 'F2, /, ., Ctrl+K palette with type and app context'), 'NAV-025': (N, 'palette lists recents; no search page'), 'NAV-027': (N, 'F9 Connection: versions, features, extensions, metadata generation'),
|
||
'NAV-028': (N, 'reports listed; run through :report run'), 'NAV-030': (N, 'palette calls POST /search; the sample has no provider'), 'NAV-035': (P, 'Esc returns to the record or query, cursor kept'),
|
||
'PRF-002': (P, 'server-paged grid, page = visible rows'), 'PRF-003': (P, 'server sort and filter'), 'PRF-005': (P, 'progress N/M, elapsed, 250 ms polling'),
|
||
'PRC-001': (P, 'grid selection or record, ids sent'), 'PRC-002': (P, 'without a selection: the grid filter'), 'PRC-004': (P, ''), 'PRC-005': (P, 'Space selects, b runs a table process'),
|
||
'PRC-007': (N, 'EDIT_FORM, BULK_EDIT_FORM, VIEW_FORM, HELP_TEXT, VALIDATION_REVIEW_SCREEN, PROCESS_SUMMARY_RESULTS'), 'PRC-008': (N, 'HELP_TEXT shown above the form'),
|
||
'PRC-009': (P, 'includeFieldNames via the kernel runner'), 'PRC-010': (P, 'required checked by field before submit'), 'PRC-011': (P, 'picker'), 'PRC-012': (P, 'summary shows view fields'),
|
||
'PRC-017': (P, 'kernel runner'), 'PRC-018': (P, 'kernel runner'), 'PRC-019': (P, 'bar, N/M, message, elapsed'), 'PRC-020': (N, 'Esc asks, then POST cancel; the sample keeps the run'),
|
||
'PRC-021': (P, 'failure message shown; F5 re-runs'), 'PRC-022': (P, 'F5 or r runs again with the same records'), 'PRC-024': (P, 'step list with marks'),
|
||
'PRC-026': (P, 'validate or skip, then run'), 'PRC-027': (P, 'summary lines colored by status'), 'PRC-028': (P, ''), 'PRC-029': (P, 'Ctrl+T marks fields; bulkEditEnabledFields'),
|
||
'PRC-031': (X, 'bulk load parked for the TUI'), 'PRC-032': (X, 'bulk load parked for the TUI'), 'PRC-033': (X, 'bulk load parked for the TUI'), 'PRC-047': (P, ''), 'PRC-048': (P, ''), 'PRC-051': (P, 'contingent picker narrows'),
|
||
'QRY-001': (P, ''), 'QRY-002': (P, 'PgUp/PgDn, Ctrl+F/B, Home/End across pages'), 'QRY-003': (P, 's cycles asc, desc, default'), 'QRY-004': (P, 'v: columns on/off and order, kept per table in the config file'),
|
||
'QRY-005': (P, 'v: widths, fit or fill, zebra, density'), 'QRY-006': (P, 'Esc lands on the row last viewed, filter and page kept'), 'QRY-007': (P, 'error shown in the grid; F5 retries'),
|
||
'QRY-010': (P, 'operators by field type in the filter dialog'), 'QRY-011': (P, ''), 'QRY-012': (P, 'date presets: now(-7d), thisPeriod(month) …'), 'QRY-015': (P, 'labels in the picker, ids sent; typed labels resolved'),
|
||
'QRY-016': (P, 'AND/OR and groups, guided and advanced'), 'QRY-017': (N, 'the grammar and the CLI command are shown; no URL'), 'QRY-030': (N, 'Space and Ctrl+A (page)'), 'QRY-031': (P, ''), 'QRY-034': (N, 'F9 lists table processes; bulk load parked'),
|
||
'QRY-040': (P, 'x: visible columns, filter and sort, file dialog'), 'QRY-041': (P, ''), 'QRY-050': (N, 'F9 runs saved-view list in a pane'), 'QRY-062': (P, 'F-keys hidden without permission'),
|
||
'QRY-065': (P, ''), 'QRY-067': (P, 'g'), 'QRY-068': (P, ''), 'QRY-070': (P, 'n r f s and the F-keys; off with keyboard.singleKeyShortcuts'), 'QRY-083': (P, 'guided builder edits and removes conditions'), 'QRY-084': (P, 'sort in the builder; c clears'), 'QRY-086': (P, ''),
|
||
'REC-001': (P, 'sections by tier, two columns when they fit'), 'REC-003': (P, 'error shown in the record pane'), 'REC-005': (P, 'local validation by field'), 'REC-006': (P, 'new record replaces the form'),
|
||
'REC-008': (P, 'Esc asks when there are unsaved changes; F10 warns too'), 'REC-009': (P, 'only changed fields sent'), 'REC-010': (P, 'non-editable fields read-only'), 'REC-012': (P, 'F8 asks; the grid moves on to the next record'),
|
||
'REC-013': (P, 'c: copyRecordValues'), 'REC-014': (N, 'TEXT single line with $EDITOR on Ctrl+E'), 'REC-015': (P, ''), 'REC-016': (P, 'Space toggles'), 'REC-017': (P, ''), 'REC-018': (P, 'local zone in and out (kernel datetime)'),
|
||
'REC-021': (P, 'masked'), 'REC-028': (P, 'links marked, Enter follows (kernel record-view)'), 'REC-033': (N, 'masked; Enter reveals'), 'REC-034': (N, 'Enter saves through the file dialog; no sample field'),
|
||
'REC-042': (N, 'F9 runs audit list in a pane'), 'REC-044': (P, ''), 'REC-045': (P, 'picker by keyboard'), 'REC-048': (P, 'fieldErrors by field'), 'REC-050': (P, ''), 'REC-051': (P, ''), 'REC-052': (P, 'contingent fields wait and narrow'), 'REC-055': (P, 'n e c d; F-keys'),
|
||
'REL-001': (N, 'labels with links in the record view'), 'REL-002': (P, 'child lists; F9 Query all opens the filtered list'), 'REL-003': (P, 'F7 on a child list prefills the parent'), 'REL-004': (N, 'one level shown; open a child for its own'),
|
||
'REL-005': (P, 'the parent reloads on return'), 'REL-006': (P, 'unavailable panel (kernel associationGroups)'),
|
||
'SEC-003': (P, 'F7 hidden without insert permission'), 'SEC-004': (P, ''), 'SEC-005': (P, ''), 'SEC-020': (P, 'MOCK session, shared with the CLI keyring'), 'SEC-021': (P, 'F9 Sign out'), 'SEC-025': (N, 'device flow screen; not tried against a provider'), 'SEC-031': (P, ''), 'SEC-032': (P, 'unknown type refused with a message'),
|
||
'INT-001': (P, 'keyboard only by design'), 'INT-002': (P, ''), 'INT-003': (P, 'modal dialogs, Esc closes'), 'INT-004': (P, ''), 'INT-007': (P, ''), 'INT-009': (P, 'loading lines'), 'INT-010': (P, 'F5 retries'), 'INT-012': (P, 'F5 and on return'), 'INT-013': (N, 'no records message; F7 offered'), 'INT-014': (P, 'sign-in screen shows the failure'),
|
||
'WID-001': (P, ''), 'WID-002': (P, 'bars in block characters'), 'WID-004': (P, ''), 'WID-009': (P, 'sparkline and table'), 'WID-012': (P, ''), 'WID-014': (P, 'percentage bars'), 'WID-016': (P, ''), 'WID-017': (P, 'segments per dataset'),
|
||
'WID-018': (P, ''), 'WID-019': (P, ''), 'WID-023': (P, 'big numbers, up/down'), 'WID-040': (N, 'declared order, v reorders; one column'), 'WID-051': (P, 'error contained to the widget'), 'WID-053': (P, ''), 'WID-060': (N, 'record section widgets get the id'),
|
||
'HLP-002': (N, 'F1 shows table and field help; the sample has none'), 'HLP-005': (P, 'type in F1'),
|
||
}
|
||
|
||
# Re-scored after the Home, F9-stack, uploads, quick search, variants and process-screen work (TUI only).
|
||
TUI.update({
|
||
'NAV-012': (N, 'app name in the title bar and Home; no logo or banners in a terminal'), 'NAV-017': (P, 'Home: greeting, counts, recents, quick actions, app cards, work'),
|
||
'NAV-025': (N, 'palette: recents and navigation targets; no separate search page'), 'NAV-028': (N, 'reports listed in the tree, Home and palette; run through :report run'),
|
||
'NAV-030': (N, 'palette calls POST /search when the server lists recordSearch; the sample answers provider NONE'),
|
||
'NAV-031': (P, 'server-enforced; the palette shows only what the server returns'),
|
||
'PRF-001': (N, 'server paging opens the first page; the column layout scrolls horizontally rather than fitting 40 columns'),
|
||
'PRF-004': (P, 'dashboards load widgets as they come into view, 4 at a time (fake 3000-widget instance)'),
|
||
'PRC-003': (P, 'a filter that matches nothing runs as the server decides (zero records)'), 'PRC-006': (P, 'kernel checkInputRecords before init'),
|
||
'PRC-007': (P, 'kernel screen blocks: forms, view, records, review, summary, help, HTML, widgets, downloads'), 'PRC-008': (P, 'HELP_TEXT and its preview from the kernel screen'),
|
||
'PRC-013': (P, 'HTML component as text from the kernel screen'), 'PRC-014': (N, 'w saves the DOWNLOAD_FORM file; no sample process offers one'),
|
||
'PRC-015': (P, 'step and process help in the screen'), 'PRC-016': (N, 'F4 Back resolves the runner BACK; no sample process declares a back step'),
|
||
'PRC-020': (N, 'Esc asks, then POST cancel; the sample keeps the run'), 'PRC-023': (P, 'Ctrl+S continues; the last screen closes with Enter or Esc'),
|
||
'PRC-025': (N, 'RECORD_LIST shows the first page of records; no paging inside the step'), 'PRC-030': (P, 'Person Bulk Delete from the bulk bar runs through review and result'),
|
||
'PRC-034': (X, 'bulk load parked for the TUI'), 'PRC-035': (N, 'named WIDGET blocks drawn from the kernel screen; no sample step uses one'),
|
||
'PRC-036': (N, 'composite widget blocks drawn as text'), 'PRC-037': (N, 'ad hoc widget blocks drawn; no action buttons'),
|
||
'PRC-040': (X, 'no reload in a terminal; F5 re-runs'), 'PRC-041': (P, 'a 401 shows the error; :signout and F9 sign in again'),
|
||
'PRC-042': (P, 'hidden processes are not offered; the server refuses the rest'), 'PRC-043': (P, ':process run P -v name=value seeds the values'),
|
||
'PRC-044': (T, 'scanner-format screens'), 'PRC-045': (P, 'the runner follows nextStep whatever the flow'), 'PRC-046': (X, 'bulk load parked for the TUI'),
|
||
'PRC-049': (P, 'v1 process routes through the kernel'), 'PRC-050': (P, 'every process the metadata gives the table is in the bulk bar and F9'),
|
||
'PRC-058': (X, 'bulk load parked'), 'PRC-059': (X, 'bulk load parked'), 'PRC-060': (X, 'bulk load parked'), 'PRC-061': (X, 'bulk load parked'),
|
||
'PRC-062': (T, 'composite INPUT_FIELD editors'), 'PRC-063': (P, 'the first field has focus; behaviors trim on set'), 'PRC-064': (P, 'step DATE_TIME inputs edited in the local zone, sent as UTC (kernel datetime)'), 'PRC-066': (P, 'field behaviors via FormSession'),
|
||
'PRC-031': (X, 'bulk load parked'), 'PRC-032': (X, 'bulk load parked'), 'PRC-033': (X, 'bulk load parked'),
|
||
'QRY-013': (N, 'date-time criteria take expressions; typed local times are sent as typed'), 'QRY-014': (P, 'TRUE/FALSE/EQUALS on booleans, blank tests, text operators on long text'),
|
||
'QRY-017': (N, 'the grammar and the CLI command are shown and copied; no URL'), 'QRY-018': (P, 'More operators… offers every operator'),
|
||
'QRY-019': (P, 'q quick search ANDed onto the filter (kernel)'), 'QRY-020': (T, 'exposed joins in the grid'), 'QRY-021': (T, 'exposed joins in the grid'),
|
||
'QRY-022': (N, 'S runs kernel column statistics; the sample has no QUERY_STATS table'), 'QRY-023': (P, 'the refusal says why'),
|
||
'QRY-030': (N, 'Space, Ctrl+A for the page; no full-result or first-N selection'), 'QRY-032': (N, 'bulk delete on the selection; without a selection the process gets the filter'),
|
||
'QRY-033': (T, 'first-N selection'), 'QRY-034': (N, 'F9 and b list table processes; bulk load parked'), 'QRY-035': (P, 'F-keys and entries hidden without permission; the server refuses'),
|
||
'QRY-042': (P, 'an empty result exports an empty file; the server decides'), 'QRY-043': (P, 'server refuses; the message is shown'),
|
||
'QRY-050': (N, 'F9 Saved views lists and opens (kernel); no sample table has saved views'), 'QRY-051': (N, 'save the current query as a view; unverified live'),
|
||
'QRY-052': (N, 'delete from F9; no rename'), 'QRY-053': (P, 'server messages shown'), 'QRY-054': (P, 'server refuses changes'),
|
||
'QRY-060': (P, 'the variant prompt before the first query; tableVariant on query and count (test)'), 'QRY-061': (P, 'dismissing queries nothing'),
|
||
'QRY-063': (P, 'no write F-keys without permission'), 'QRY-064': (P, 'server refuses; shown'), 'QRY-066': (P, 'v1 variants route (kernel)'),
|
||
'QRY-069': (P, 'counts with en-US separators'), 'QRY-071': (N, 'processes from a variant grid get tableVariant through the runner; no sample variant table'),
|
||
'QRY-080': (T, 'basic-mode quick filters'), 'QRY-081': (T, 'quick filter fields'), 'QRY-082': (X, 'the guided builder shows every condition; no basic mode'),
|
||
'QRY-085': (T, 'weekday criteria'), 'QRY-087': (P, 'IN takes a pasted comma list'), 'QRY-088': (T, 'bulk paste help'), 'QRY-089': (P, 'behaviors applied to typed values'),
|
||
'QRY-090': (N, 's sorts, v hides and orders columns, F6 filters; no pinning'), 'QRY-091': (X, 'clipboard copy of column values'),
|
||
'QRY-092': (N, 'v lists every column with on/off; no grouping or counts'), 'QRY-093': (P, 'view settings (columns, widths, sort) persist per table'),
|
||
'QRY-094': (T, 'counted quick views'), 'QRY-095': (X, 'page size follows the terminal height'), 'QRY-096': (N, 'viewToQuery warnings flashed'),
|
||
'REL-001': (N, 'labels with links in the record view; grid cells show labels without links'), 'REL-004': (N, 'one level shown; open a child for its own children'),
|
||
'REL-007': (P, 'kernel associationGroups binds aliased associations to their widgets'), 'REL-008': (N, 'create child prefills every join value; composite untested live'),
|
||
'REL-009': (P, 'server cascade; the list reloads'),
|
||
'REC-002': (P, 'hidden fields and sections are left out; empty values blank'), 'REC-004': (X, 'one scrolling view, no tabs'), 'REC-007': (P, 'server message by field'),
|
||
'REC-011': (P, 'the load failure shows instead of the form'), 'REC-014': (N, 'TEXT single line, $EDITOR on Ctrl+E'), 'REC-019': (N, 'kernel displayDateTime; fixed and per-record zones as the server formats them'),
|
||
'REC-020': (N, 'HTML edited as text or in $EDITOR'), 'REC-022': (P, 'BLOB upload through the file dialog and save from the record (Field Lab, live)'),
|
||
'REC-023': (P, 'defaults applied by FormSession and the server'), 'REC-024': (P, 'local check and server policy'), 'REC-025': (P, 'behaviors on save'), 'REC-026': (P, 'local range check and server policy'),
|
||
'REC-027': (P, 'server message shown'), 'REC-029': (N, 'kernel valueText renders CHIP as text'), 'REC-030': (P, 'saved widths per column; SIZE as text width'),
|
||
'REC-031': (N, 'code shown as text; $EDITOR to edit'), 'REC-032': (N, 'HTML shown as text'), 'REC-033': (P, 'masked; Enter reveals'), 'REC-034': (P, 'Enter saves through the save dialog'),
|
||
'REC-035': (P, 'FILE_UPLOAD fields choose a file; Backspace clears'), 'REC-036': (P, 'tooltip text appended by valueText'), 'REC-037': (N, 'WIDGET adornment as text'), 'REC-038': (P, 'marked by valueText'),
|
||
'REC-039': (N, 'F1 shows field help; no sample content'), 'REC-040': (N, 'section help via F1; no sample content'), 'REC-041': (P, 'table help in F1'),
|
||
'REC-042': (N, 'A opens the audit history (kernel); the sample has no audit source'), 'REC-043': (N, 'kernel reads the audit table when there is no process; unverified live'),
|
||
'REC-046': (P, 'write keys hidden without permission; server refuses'), 'REC-047': (P, 'keyboard only, labels on every field'), 'REC-049': (P, 'breadcrumb uses the table label'),
|
||
'REC-053': (N, 'D on a grid lists the table APIs and their OpenAPI documents; the sample has none'), 'REC-054': (N, 'D on a record: panels live; scripts, versions, logs (kernel-tested)'),
|
||
'REC-056': (X, 'hash links'), 'REC-057': (P, ':table create T --set parent=… and F7 on a child list'), 'REC-058': (X, 'phone layout'), 'REC-059': (X, 'phone layout'),
|
||
'REC-060': (N, 'adjusters wired through FormSession; no sample adjuster'), 'REC-061': (T, 'dynamicForm widget editing'), 'REC-062': (P, 'inline enums filter locally'),
|
||
'REC-063': (T, 'record view and forms with the grid variant'), 'REC-064': (T, 'exposed-join fields in sections'), 'REC-065': (P, 'DATE_TIME edited in local zone, unchanged instants kept'),
|
||
})
|
||
|
||
# Batch: the 13 priority todos.
|
||
TUI.update({
|
||
'PRC-044': (P, 'scanner-format steps: no heading or step list, Enter in the input submits (unit-tested only; no sample scanner process)'),
|
||
'QRY-094': (P, 'doCount views show their record counts in F9 Saved views (unit-tested only; no sample saved views)'),
|
||
'QRY-020': (P, 'joined columns and filters, labelled "Join: Field", joins planned by the kernel (unit-tested only; the sample exposes no joins)'),
|
||
'QRY-021': (P, 'many-side join: rows repeat and the status shows the distinct count (unit-tested only)'),
|
||
'REC-064': (P, 'section fields of readable exposed joins read through a joined query (unit-tested only)'),
|
||
'QRY-033': (P, 'run a table process on the first N records of the result (filter with a limit; unit-tested only)'),
|
||
'QRY-080': (P, 'the filter dialog starts with the quick filter fields (defaultQuickFilterFieldNames, else T1); live on person'),
|
||
'QRY-081': (P, 'add a quick filter field; kept per table in the local view settings'),
|
||
'QRY-085': (B, 'weekday(field) IN days sent as the WeekdayOf fieldFunction; the sample H2 backend fails it (server finding 24, qqq#1355)'),
|
||
'QRY-088': (P, 'paste values split on commas, tabs, semicolons, new lines; the instance bulkAddFilterValues help shown (unit-tested only; the sample has no such help)'),
|
||
'REC-063': (P, 'records and forms opened from a variant grid read with tableVariant (unit-tested only; writes ignore it, finding 19)'),
|
||
})
|
||
|
||
# Batch: the partials.
|
||
TUI.update({
|
||
'NAV-005': (X, 'icon glyphs are a browser icon font; the TUI marks processes and reports and shows the icon name on app cards'),
|
||
'NAV-012': (X, 'logo, favicon and accent images are browser-only; the app name is in the title bar and on Home'),
|
||
'NAV-025': (P, 'the palette (F2, /, ., Ctrl+K) jumps to navigation targets and recently viewed records; live'),
|
||
'NAV-027': (P, 'F9 › Connection: UI API version, features, extensions, metadata generation and age, client build; live'),
|
||
'NAV-030': (B, 'the palette calls POST /search; the sample lists recordSearch with no provider (server finding 21)'),
|
||
'PRC-014': (P, 'w saves the DOWNLOAD_FORM file through the save dialog (unit-tested only)'),
|
||
'PRC-016': (P, 'F4 goes back to the backend back step with the entered values (isStepBack; unit-tested only)'),
|
||
'PRC-020': (B, 'Esc asks, then POST cancel; the sample accepts the cancel and keeps the run (server finding 13)'),
|
||
'PRC-025': (P, 'RECORD_LIST pages with n / p through getProcessRecords (unit-tested only)'),
|
||
'PRC-035': (P, 'named WIDGET fetched with the process UUID and values, drawn from the kernel screen (unit-tested only)'),
|
||
'PRC-036': (P, 'composite widget blocks seeded from process values drawn as text (kernel screen; unit-tested only)'),
|
||
'QRY-013': (P, 'DATE_TIME criteria typed in the local zone go out as UTC instants; expressions pass through (unit-tested)'),
|
||
'QRY-017': (X, 'URL state is browser-only; the TUI shows the grammar and the CLI command, and qqq ui accepts the command as a route'),
|
||
'QRY-022': (P, 'S: kernel column statistics of the column under the cursor with the filter (unit-tested only; no sample QUERY_STATS table)'),
|
||
'QRY-030': (P, 'Ctrl+A: this page, the full result, the first N, clear (unit-tested)'),
|
||
'QRY-032': (P, 'with the full result selected, bulk delete gets the whole filter (unit-tested)'),
|
||
'QRY-050': (P, 'F9 › Saved views lists and opens views (kernel; unit-tested only)'),
|
||
'QRY-051': (P, 'save the current filter, sort, columns and page size as a view (unit-tested only)'),
|
||
'QRY-052': (P, 'rename, update with the current query, delete (unit-tested only)'),
|
||
'QRY-071': (P, 'processes from a variant grid carry tableVariant through the runner (unit-tested only)'),
|
||
'QRY-090': (P, 's sorts, v hides, orders, sizes and pins columns, F6 filters; live on person'),
|
||
'QRY-092': (P, 'v: / searches, columns grouped by table and join with shown counts, * marks changes; live'),
|
||
'QRY-096': (P, 'opening a view drops removed fields and flashes the kernel warnings (unit-tested only)'),
|
||
'REL-001': (P, 'labels with ↗ links in the record view; l opens a grid cell\'s linked record; live'),
|
||
'REL-004': (P, 'child lists three levels deep (a pet\'s notes under the pet); live on person 1'),
|
||
'REL-008': (P, 'a new child through a composite association gets every join value (unit-tested only)'),
|
||
'REC-014': (P, 'single-line text inline; multi-line through $EDITOR (Ctrl+E) round-trips'),
|
||
'REC-019': (P, 'DATE_TIME through the kernel displayDateTime: server-zoned display values as sent, else the viewer\'s zone; live on Field Lab'),
|
||
'REC-020': (X, 'the rich-text editor is a browser component; HTML is edited as source in $EDITOR and shown as text'),
|
||
'REC-029': (P, 'CHIP values bracketed and colored by the adornment color (unit-tested only)'),
|
||
'REC-031': (X, 'the code editor is a browser component; code is shown as text and edited in $EDITOR'),
|
||
'REC-032': (P, 'RENDER_HTML shown as text with the markup stripped (kernel htmlToText)'),
|
||
'REC-037': (P, 'a WIDGET-adorned field\'s value drawn as its widget (unit-tested only)'),
|
||
'REC-039': (P, 'field help by screen role in F1 and in the form\'s field hint (unit-tested only)'),
|
||
'REC-040': (P, 'section help under the section heading and in F1 (unit-tested only)'),
|
||
'REC-042': (P, 'A: audits newest first, who, when, what, changes (unit-tested only; the sample has no audit source)'),
|
||
'REC-043': (P, 'without the audit process the kernel reads the audit table (kernel-tested; the TUI uses it)'),
|
||
'REC-053': (P, 'D on a grid: the table\'s APIs and their OpenAPI documents, view or save (unit-tested only)'),
|
||
'REC-054': (P, 'D on a record: panels (live), scripts, revisions, code, logs, store a new version (unit-tested)'),
|
||
'REC-060': (P, 'form adjusters run on change through FormSession (unit-tested only; the sample has none)'),
|
||
'INT-013': (P, 'no records explains it and offers F7 when the user may create; live'),
|
||
'WID-040': (P, 'widgets in declared order, laid out side by side by gridColumns on wide terminals, v reorders; live'),
|
||
'WID-060': (P, 'record section widgets get the record id and table (unit-tested)'),
|
||
'HLP-002': (P, 'table, field and section help by screen role in F1 (unit-tested only; the sample has none)'),
|
||
})
|
||
|
||
# Batch: the bulk wizard (kernel runBulkProcess) and reports.
|
||
TUI.update({
|
||
'QRY-034': (P, 'a lists bulk insert, bulk edit with a file, bulk edit, bulk delete, the template and the table processes, with each CLI command; live'),
|
||
'QRY-031': (P, 'bulk edit on the checked records sends their ids; live: 2 person records edited and verified'),
|
||
'QRY-032': (P, 'bulk delete of the selected/full result sends the filter; live: 2 person records deleted'),
|
||
'PRC-016': (P, 'F4 goes back to the backend back step (process screen and bulk wizard); live'),
|
||
'PRC-029': (P, 'bulk edit: Space switches fields on, Enter sets typed values (picker, yes/no), review, result; live on person'),
|
||
'PRC-030': (P, 'bulk delete: review with count and preview, validation choice, confirm, result; live on person'),
|
||
'PRC-031': (P, 'bulk insert: file navigator upload, column mapping grid, value mapping, review preview, result; live: 2 person rows from CSV (then deleted)'),
|
||
'PRC-032': (P, 'mapping checked before Ctrl+S (kernel checkMapping), F4 back through mapping and upload (unit-tested only)'),
|
||
'PRC-033': (P, 'bulk edit with a file: key fields (k) from the server, mapping, review, result (unit-tested only)'),
|
||
'PRC-034': (P, 'a: save the bulk insert template file(s) from the upload instructions; live on person (3 templates)'),
|
||
'PRC-046': (P, 'P on the mapping screen: use, save, update, delete saved profiles (unit-tested only; the sample has none)'),
|
||
'PRC-058': (P, 'update a saved profile after a confirm listing the changes (kernel diffMappings), new profile, delete (unit-tested only)'),
|
||
'PRC-059': (P, 'profiles listed as yours or shared by their owner; update and delete only for the owner (unit-tested only)'),
|
||
'PRC-060': (P, 'mapping grid: field help of the row, column previews, required marks, repeated-header warnings, header row toggle h; live'),
|
||
'PRC-061': (P, 'typed defaults (picker for possible values, yes/no), searchable value mapping picker, searchable field source menu (unit-tested)'),
|
||
'NAV-028': (P, 'reports listed under their app and in the tree, palette and F9; Enter runs one (format chosen, process screens, file saved) (unit-tested only; the sample has no reports)'),
|
||
'RPT-008': (P, 'reports appear in app navigation and run from there (unit-tested only)'),
|
||
'RPT-011': (P, 'a report without a process streams from GET /reports/{name}?format= into the save dialog (unit-tested only)'),
|
||
'RPT-020': (P, 'report files download from the report route and the process DOWNLOAD_FORM (unit-tested only)'),
|
||
})
|
||
|
||
# Batch: widgets, saved reports, sharing, sign-in, permissions, help, the large table.
|
||
TUI.update({
|
||
'PRF-001': (P, 'fake big server 10,000 rows x 40 columns at 160x45 (scripts/perf-large-table.sh): first page 95-100 ms with col 1/40, End to record 10,000 in 39-45 ms, to column 40 in 343-364 ms; budget 2,000 ms per step'),
|
||
'PRC-037': (P, 'ad hoc composite widgets: interpolation, conditional blocks hidden, INPUT_FIELD inputs (Enter submits when asked), BUTTON actionCode sent with the step (unit-tested only)'),
|
||
'PRC-062': (P, 'composite INPUT_FIELD editors are step fields: required checks before sending, a BLOB input goes as a multipart file (unit-tested only)'),
|
||
'REC-061': (P, 'record view: a dynamicForm widget section has "Edit these fields": FormEditor editors, required checked, saved into the named JSON field or record fields (unit-tested only)'),
|
||
'SEC-001': (P, 'a hidden table is not in the metadata: absent from the tree, palette and index; a direct :table query names no such table and loads nothing (unit-tested only)'),
|
||
'SEC-002': (P, 'a disabled table (hasPermission false) is listed with "(no access)"; opening it explains the denial and requests nothing (unit-tested only)'),
|
||
'SEC-006': (P, 'copy disabled with the reason without insert permission; c and :table copy are refused (unit-tested only)'),
|
||
'SEC-007': (P, 'a hidden process is not offered (tree, palette, actions) and :process run names no such process (unit-tested only)'),
|
||
'SEC-008': (P, 'a disabled process is listed "(no access)" and refused with the reason; table actions show it disabled (unit-tested only)'),
|
||
'SEC-009': (P, 'hidden widgets are absent; a disabled widget shows the permission state without a request; children of a parent skip denied ones (unit-tested only)'),
|
||
'SEC-010': (P, 'hidden apps are absent and unknown by name; a disabled app explains the denial (unit-tested only)'),
|
||
'SEC-011': (P, 'saved views and reports: the TUI lists what the server returns; sharing through the share dialog; a refused direct link shows the server message (unit-tested only)'),
|
||
'SEC-012': (P, 'record security is server-side: grids show the server\'s rows; a record the server refuses shows its message (403) (unit-tested only)'),
|
||
'SEC-013': (P, 'hidden fields never shown (grid, record, form); PASSWORD values masked; edits send only changed fields, never the masked value (unit-tested only)'),
|
||
'SEC-014': (P, 'without TABLE_INSERT/UPDATE/DELETE capabilities the F-keys, menu entries and copy are off and n/e/d are refused (unit-tested only)'),
|
||
'SEC-015': (P, 'a save refused mid-session keeps the form with what was typed and shows the server message (unit-tested only)'),
|
||
'SEC-022': (P, 'a 401 mid-use opens sign-in on top of the screens; after signing in the same screen is back and refreshed (unit-tested only)'),
|
||
'SEC-023': (P, 'a refused sign-in (TABLE_BASED 401) shows the reason, stores nothing, no anonymous fallback (unit-tested only)'),
|
||
'SEC-025': (P, 'OAUTH2 sign-in in the browser: discovery, S256 PKCE authorize address shown, loopback callback, code exchanged with the verifier; device flow as the alternative (unit-tested only)'),
|
||
'SEC-026': (P, 'tampered state, provider denial and a refused code exchange are reported and store nothing (unit-tested only)'),
|
||
'SEC-027': (P, 'sign out: here, or also revoke the refresh token and open the provider end-session address (unit-tested only)'),
|
||
'SEC-028': (P, 'a 401 after the refresh opens sign-in (browser or device) on top and returns to the refreshed screen (unit-tested only)'),
|
||
'SEC-029': (P, 'AUTH_0: authorize at baseUrl with the audience (PKCE), code exchanged at /oauth/token, the token handed to manageSession, the session kept; sign-out offers v2/logout (unit-tested only)'),
|
||
'SEC-034': (P, 'TABLE_BASED: username then password (masked) under the app name, POST manageSession with Basic credentials, the session cookie kept, the password never stored (unit-tested only)'),
|
||
'SEC-035': (P, 'a wrong password or unknown user: one message, no session stored, Enter tries again (unit-tested only)'),
|
||
'SEC-036': (P, 'an expired session gets a 401: sign-in on top, then back to the refreshed screen (unit-tested only)'),
|
||
'SEC-037': (P, 'sign out sends POST /logout with the session cookie, forgets the sign-in and shows sign-in (unit-tested only)'),
|
||
'WID-003': (P, 'a payload with chartData and no type is drawn as a bar chart (unit-tested only)'),
|
||
'WID-005': (P, 'fieldValueList: labels, display values, prefix icon marks, indentation; empty state (unit-tested only)'),
|
||
'WID-006': (P, 'generic widget: label, sublabel, text from HTML, footer (unit-tested only)'),
|
||
'WID-007': (P, 'horizontal bars around a zero axis: negative values left, positive right; live structure in the fake dashboard (unit-tested only)'),
|
||
'WID-008': (P, 'html widget as text: tags and scripts dropped, links kept as text (unit-tested only)'),
|
||
'WID-010': (P, 'smallLineChart: title, HTML description as the subtitle, sparkline (unit-tested only)'),
|
||
'WID-011': (P, 'location: image address, title, description, address, footer (unit-tested only)'),
|
||
'WID-013': (P, 'multiTable: each table with its label and rows (unit-tested only)'),
|
||
'WID-015': (X, 'embedding the QuickSight dashboard needs a browser frame; the TUI shows its address to open'),
|
||
'WID-020': (P, 'usaMap: each marker with name and coordinates in a table, the count; the map itself is browser-only (unit-tested only)'),
|
||
'WID-021': (P, 'process widget: Enter runs its process in the process screen with the payload default values (unit-tested only)'),
|
||
'WID-022': (P, 'parent widget: children loaded with the parent\'s selections, stacked (tabs layout: one tab at a time, switch from the menu) (unit-tested only)'),
|
||
'WID-024': (P, 'childRecordList without an association: its rows and count from the payload (unit-tested only)'),
|
||
'WID-026': (P, 'cronUI: the record\'s cron expression, the server description, the time zone; "No schedule set" (unit-tested only)'),
|
||
'WID-027': (P, 'dynamicForm: labeled values, required marks, the no-fields message (unit-tested only)'),
|
||
'WID-028': (P, 'dataBagViewer: versions newest first (sequence, message, when, author) and the chosen version\'s contents pretty-printed (unit-tested only)'),
|
||
'WID-029': (P, 'pivotTableSetup: rows, columns, values shown; add rows/columns/values from the report columns; clear (unit-tested only)'),
|
||
'WID-030': (P, 'filterAndColumnsSetup: table, filter, sort and visible columns of the saved report (unit-tested only)'),
|
||
'WID-070': (P, 'edit the report filter and sort in the TUI filter dialog and the columns in a chooser; saved to the record (unit-tested only)'),
|
||
'WID-071': (P, 'a WIDGET-adorned process field with a filter setup: Enter opens the filter dialog for the form\'s table and writes the filter JSON (unit-tested only)'),
|
||
'WID-072': (P, 'API-versioned setup: fields from the application API metadata, preview rows and count through its query (unit-tested only)'),
|
||
'WID-073': (P, 'Preview opens the report filter in the full grid (server paging, sort, view settings) (unit-tested only)'),
|
||
'WID-031': (P, 'rowBuilder without an association: rows read-only as a table (unit-tested only)'),
|
||
'WID-032': (P, 'scriptViewer: Enter opens the developer view of the script (revisions, files, logs) (unit-tested only)'),
|
||
'WID-033': (X, 'a real AWS QuickSight dashboard is an embedded browser page'),
|
||
'WID-041': (P, 'label, sublabel, footer text or footer HTML as text; card vs plain is drawing only (unit-tested only)'),
|
||
'WID-046': (P, 'Export as CSV: the payload csvData (else the table rows) through the save dialog (unit-tested only)'),
|
||
'WID-047': (P, 'payload dropdowns in the widget menu: the choice is sent as a parameter and the widget reloads (unit-tested only)'),
|
||
'WID-048': (P, 'DATE_PICKER dropdown: a date typed as YYYY-MM-DD is sent as the web sends it (M/D/YYYY) (unit-tested only)'),
|
||
'WID-049': (P, 'required dropdowns: the server message shows until a choice is made, then the data loads (unit-tested only)'),
|
||
'WID-050': (P, 'storeDropdownSelections: choices kept per widget in the shared config; a stale stored option is dropped (unit-tested only)'),
|
||
'WID-052': (P, 'empty states: no rows, no data to chart, no value, no values, no child widgets, no dashboard, no schedule (unit-tested only)'),
|
||
'WID-054': (P, 'a denied widget is drawn with the permission state and never requested (unit-tested only)'),
|
||
'WID-056': (P, 'a parent widget skips denied and unknown children without requests (unit-tested only)'),
|
||
'WID-057': (P, 'composite blocks: TEXT, BIG_NUMBER, NUMBER_ICON_BADGE, UP_OR_DOWN_NUMBER, PROGRESS_BAR, DIVIDER, TABLE_SUB_ROW_DETAIL_ROW, INPUT_FIELD, BUTTON, IMAGE, AUDIO, ICON (unit-tested only)'),
|
||
'WID-059': (P, 'an unsupported block type shows a contained warning (unit-tested only)'),
|
||
'WID-061': (P, 'a process widget whose process the user may not run offers Run disabled with the reason (unit-tested only)'),
|
||
'WID-063': (P, 'an alert with hideWidget or without content draws nothing (unit-tested only)'),
|
||
'WID-066': (P, 'widget metadata from /metaData and data from POST /qqq/v1/widget/{name} (unit-tested only)'),
|
||
'WID-067': (P, 'a payload that is one block draws that block (unit-tested only)'),
|
||
'WID-068': (P, 'table cells typed: numbers with separators, html and htmlAndTooltip as text, images, composites (unit-tested only)'),
|
||
'WID-074': (P, 'standalone composite INPUT_FIELD blocks show their seeded values read-only by type (no standalone input target in a terminal) (unit-tested only)'),
|
||
'WID-075': (P, 'standalone DATE_TIME blocks show seeded instants in the local zone with seconds (unit-tested only)'),
|
||
'RPT-001': (P, 'a report with a process runs its screens and saves the file it produces (CSV chosen) (unit-tested only)'),
|
||
'RPT-002': (P, 'XLSX chosen from the report formats; the server\'s file saved as is (unit-tested only)'),
|
||
'RPT-003': (P, 'JSON chosen from the report formats; the server\'s file saved as is (unit-tested only)'),
|
||
'RPT-004': (P, 'SUMMARY reports: the grouped file with totals is the server\'s; the TUI runs and saves it (unit-tested only)'),
|
||
'RPT-005': (P, 'PIVOT reports: the XLSX pivot is the server\'s; the TUI runs and saves it (unit-tested only)'),
|
||
'RPT-006': (P, 'report input fields are the report process\'s form: required checks, values sent with the run (unit-tested only)'),
|
||
'RPT-007': (P, 'a restricted report is marked (no access) and refused with the reason; the server refuses too (unit-tested only)'),
|
||
'RPT-009': (P, 'saved report record: filter, sort, columns and pivot setup drawn (unit-tested only)'),
|
||
'RPT-010': (P, 'the record actions list the table processes (renderSavedReport) and run them on the record; the DOWNLOAD_FORM file is saved (process downloads unit-tested only)'),
|
||
'RPT-012': (P, 'scheduled report: created on its form; the record shows the cron schedule (cronUI) (unit-tested only)'),
|
||
'RPT-013': (P, 'share a saved report with a user Read-Only (Z, :share) (unit-tested only)'),
|
||
'RPT-014': (P, 'change a share to Read and Edit (unit-tested only)'),
|
||
'RPT-015': (P, 'remove a share after a confirm (unit-tested only)'),
|
||
'RPT-016': (P, 'unshared reports are the server\'s to hide; the TUI lists what it returns and shows a refused link\'s message (unit-tested only)'),
|
||
'RPT-017': (P, 'only the owner may share: others see Share disabled with the reason (unit-tested only)'),
|
||
'RPT-018': (P, 'saved views share through the same dialog (Saved views menu) (unit-tested only)'),
|
||
'RPT-019': (P, 'a report input step shows only its fields; a step without fields continues with no message (unit-tested only)'),
|
||
'HLP-001': (P, 'F1 on an app home: app help and each widget\'s help (slot maps) (unit-tested only)'),
|
||
'HLP-003': (P, 'the grid footer shows the help of the column under the cursor; table help slots in F1 (unit-tested only)'),
|
||
'HLP-004': (P, 'F1 on a process: process, step and step field help; on a record: table, section and field help for the view role (unit-tested only)'),
|
||
'HLP-006': (P, ':helpkeys (F9 Help) turns on help-authoring mode: F1 shows every slot key, with or without content (unit-tested only)'),
|
||
})
|
||
|
||
TUI.update({
|
||
'SEC-030': (B, 'needs a real Auth0 tenant: [qqq-cli#10](https://github.com/QRun-IO/qqq-cli/issues/10); the sample has no AUTH_0 configuration ([qqq#1359](https://github.com/QRun-IO/qqq/issues/1359), finding 28); the Auth0 protocol path is SEC-029'),
|
||
})
|
||
# Verified live on a private server from the qqq fix branch 8bf6a5a66 (UI API 1.9.0, sample with
|
||
# -Dqqq.sample.sharing=true) on port 8091; :8077 runs the released sample.
|
||
FIX = 'live on fix branch 8bf6a5a66'
|
||
LIVE = {
|
||
'QRY-085': (P, f'weekday(birthDate) IN 1,7 returns its 2 people ({FIX}; on the released sample it fails, finding 24, qqq#1355)'),
|
||
'QRY-010': (None, f'text matches ignore case (CASE_INSENSITIVE), Match exact case on request ({FIX})'),
|
||
'QRY-019': (None, f'q avery finds Avery (CASE_INSENSITIVE); =Text matches exact case ({FIX})'),
|
||
'NAV-030': (None, f'{FIX}: recordSearch is no longer advertised and the palette says so; POST /search still has no provider (finding 21)'),
|
||
'PRC-020': (None, 'still blocked on the fix branch (finding 13)'),
|
||
'REC-022': (None, f'upload into uploadedFileArchive (text primary key typed on create) and download of the same 22 bytes ({FIX})'),
|
||
'REC-028': (None, f'adornmentLab carrier LINK ({FIX})'),
|
||
'REC-029': (None, f'adornmentLab CHIP [ACTIVE] in the grid and the record ({FIX})'),
|
||
'REC-030': (None, f'adornmentLab SIZE ({FIX})'),
|
||
'REC-032': (None, f'adornmentLab RENDER_HTML as text in the grid and the record ({FIX})'),
|
||
'REC-033': (None, f'adornmentLab REVEAL masked in the grid and the record ({FIX})'),
|
||
'REC-034': (None, f'adornmentLab FILE_DOWNLOAD ({FIX})'),
|
||
'REC-036': (None, f'adornmentLab TOOLTIP ({FIX})'),
|
||
'REC-037': (None, f'adornmentLab WIDGET field with a plain value shows the value ({FIX})'),
|
||
'REL-007': (None, f'adornmentLab tags through the composite association ({FIX})'),
|
||
'REL-008': (None, f'a new adornmentLab tag gets adornmentLabId and labKey ({FIX})'),
|
||
'SEC-012': (None, f'adornmentLabLocker 2 answers Permission denied, shown on the record screen ({FIX})'),
|
||
'SEC-011': (None, f'sharing demo: the owner sample:alice shares her saved report ({FIX})'),
|
||
'RPT-009': (None, f'saved report Pet Species Report: filter, sort, columns, pivot ({FIX})'),
|
||
'RPT-013': (None, f'shared Pet Species Report Read-Only ({FIX})'),
|
||
'RPT-014': (None, f'changed to Read and Edit ({FIX})'),
|
||
'RPT-015': (None, f'removed the share ({FIX})'),
|
||
'HLP-002': (None, f'F1 on adornmentLab: table and section help ({FIX})'),
|
||
}
|
||
for _rid, (_st, _extra) in LIVE.items():
|
||
_cur = TUI.get(_rid, (P, ''))
|
||
_note = _cur[1].replace(' (unit-tested only)', '').replace('(unit-tested only)', '').strip()
|
||
TUI[_rid] = (_st or _cur[0], _extra if _st else (f'{_note}; {_extra}' if _note else _extra))
|
||
|
||
rows = []
|
||
for line in open(SRC, encoding='utf-8', errors='replace'):
|
||
m = re.match(r'^\|\s*([A-Z]{3}-\d{3})\s*\|\s*(.*?)\s*\|', line)
|
||
if m:
|
||
rows.append((m.group(1), m.group(2)))
|
||
|
||
fam_counts = collections.defaultdict(collections.Counter)
|
||
tui_counts = collections.defaultdict(collections.Counter)
|
||
out = ['# Terminal clients parity matrix', '',
|
||
'Generated by `scripts/parity-matrix.py` from the Next UI acceptance matrix (`qqq-frontend-next/docs/acceptance/feature-matrix.md`,',
|
||
f'{len(rows)} rows). Same ids; two columns added. **pass** = verified against the local sample server in this repo\'s',
|
||
'development; **partial** = works with the noted gap; **todo** = planned; **n/a** = browser-only, with the reason; **blocked** = needs a server change (the note cites the finding in docs/server-findings.md). "unit-tested" in a note: the sample has no fixture, so node tests (and stub-server CLI tests) verify it.',
|
||
'The TUI column is `qqq ui` (M3); its notes start with "TUI:".', '']
|
||
lines = []
|
||
for rid, feature in rows:
|
||
fam = rid[:3]
|
||
cli, note = STATUS.get(rid, (T, ''))
|
||
# the TUI column: browser-only rows stay n/a; everything else is M3 work
|
||
tui = X if cli == X and (fam in ('INT', 'SEC') or 'phone' in note or 'browser' in note or 'chrome' in note or 'theme' in note or 'Google' in note or 'web-only' in note) else T
|
||
tnote = ''
|
||
if rid in TUI:
|
||
tui, tnote = TUI[rid]
|
||
fam_counts[fam][cli] += 1
|
||
tui_counts[fam][tui] += 1
|
||
both = note if not tnote else (f'{note}; TUI: {tnote}' if note else f'TUI: {tnote}')
|
||
lines.append(f'| {rid} | {feature[:110].replace("|", "/")} | {cli} | {tui} | {both.replace("|", "/")} |')
|
||
out.append('## Summary (CLI column)'); out.append('')
|
||
out.append('| Family | rows | pass | todo | partial | blocked | n/a |'); out.append('|---|---|---|---|---|---|---|')
|
||
total = collections.Counter()
|
||
for fam in sorted(fam_counts):
|
||
c = fam_counts[fam]; total.update(c)
|
||
out.append(f'| {fam} | {sum(c.values())} | {c[P]} | {c[T]} | {c[N]} | {c[B]} | {c[X]} |')
|
||
out.append(f'| **all** | {sum(total.values())} | {total[P]} | {total[T]} | {total[N]} | {total[B]} | {total[X]} |'); out.append('')
|
||
out.append('## Summary (TUI column)'); out.append('')
|
||
out.append('| Family | rows | pass | todo | partial | blocked | n/a |'); out.append('|---|---|---|---|---|---|---|')
|
||
ttotal = collections.Counter()
|
||
for fam in sorted(tui_counts):
|
||
c = tui_counts[fam]; ttotal.update(c)
|
||
out.append(f'| {fam} | {sum(c.values())} | {c[P]} | {c[T]} | {c[N]} | {c[B]} | {c[X]} |')
|
||
out.append(f'| **all** | {sum(ttotal.values())} | {ttotal[P]} | {ttotal[T]} | {ttotal[N]} | {ttotal[B]} | {ttotal[X]} |'); out.append('')
|
||
out.append('## Rows'); out.append(''); out.append('| ID | Feature | CLI | TUI | Note |'); out.append('|---|---|---|---|---|')
|
||
out.extend(lines); out.append('')
|
||
open(OUT, 'w', encoding='utf-8').write('\n'.join(out))
|
||
print(f'{len(rows)} rows -> {OUT}; CLI: ' + ', '.join(f'{k} {v}' for k, v in sorted(total.items())) + '; TUI: ' + ', '.join(f'{k} {v}' for k, v in sorted(ttotal.items())))
|