test(embed): track the held-out spec, and keep its comments self-contained
Build / Build-and-ng-test (pull_request) Waiting to run
Lighthouse Checks / lighthouse (pull_request) Waiting to run

Review follow-up on the browser harness.

The exclusion of va-ddc-host now names the issue that tracks re-enabling it
(dc/dc#449), so the coverage gate cannot report a rotted exclusion as accounted
for. The comment also states the ordering constraint the spec carries: it clears
the DC_JSLIB filter tables per case, and every spec in that job shares one mock
estate, so it has to run last or be given its own.

The spec's comments and test name describe the behaviour and the harness, with
no ticket numbers, per AGENTS.md. The va-filter docstring says what the inference
is: a guess from the value's shape alone, which can disagree with the column in
either direction, and which costs the deferred call rather than the page because
the attempt suppresses the abort modal and falls back to the unfiltered load.
This commit is contained in:
dc committed 2026-11-03 14:19:20 +00:00
1 parent 45995dbfab
commit 5aba6bfc2e
3 files changed
+39 -27

No files matched your search

+7 -2
View File
@@ -31,8 +31,13 @@ SPECS="csv-limited hook-programs liveness editor excel-multi-load excel csv filt
# the coverage check below can tell a deliberate exclusion from a forgotten file.
# va-ddc-host drives the app through sasjs/server's /ddc-host page with
# `timing=early`, which needs a server release that carries it - the version this
# job pins does not. It runs green locally against sasjs/server feat/ddc-host
# (sasjs/server#513); drop it from this list once that release is pinned here.
# job pins does not, so the spec cannot run until SASJS_SERVER_VERSION moves.
# https://git.datacontroller.io/dc/dc/issues/449 tracks re-enabling it.
#
# When it is re-enabled it has to run LAST, or be given its own estate: it clears
# the DC_JSLIB filter tables (mpe_filtersource, mpe_filteranytable,
# mpe_maxkeyvalues) before each case, and every spec in this job shares one mock
# estate. See resetFilterStore in client/cypress/e2e/va-ddc-host.cy.ts.
EXCLUDED="va-ddc-host"
case "${1:-}" in
+19 -18
View File
@@ -6,9 +6,10 @@
* captures it, and the editor applies a pre-set filter on the very first load.
* Posting a message into a running page exercises a different path: it arrives
* after getdata, with column metadata available, so a date is formatted
* correctly there whatever the client does. dc/dc#444 was exactly that
* difference - a report refresh reached the metadata-less path and a
* month-name date became a character literal.
* correctly there whatever the client does. Those two paths take the same
* clause builder with different inputs, so a value can be formatted correctly on
* one and not the other - which is why this spec pins the timing as well as the
* clause.
*
* sasjs/server's /ddc-host supplies the missing half: an iframe carrying VA's
* sandbox attribute set, and `timing=early` to deliver the message repeatedly
@@ -44,10 +45,10 @@ interface StoredClause {
}
/**
* A VA message carrying one parameter. `dataType: 'string'` is the shape that
* reproduced #444: VA reports a date parameter it displays with a month name as
* a string, and the metadata-less path fell back to that rather than read the
* value. The `dataType: 'number'` variant is covered by the unit spec, where the
* A VA message carrying one parameter. `dataType: 'string'` is what a date
* parameter carries when VA displays it with a month name, and on the
* metadata-less path this type and the value's shape are all the builder has to
* go on. The `dataType: 'number'` variant is covered by the unit spec, where the
* dropped-clause branch can be driven directly.
*/
const vaMessage = (label: string, value: string, dataType = 'string') => ({
@@ -131,9 +132,9 @@ const storedClauseFor = (
* Mount the host page, then assert the two facts that make a case valid: the app
* captured the message, and it did so before the frame finished loading (the
* app's earliest listener runs at document parse, so this is the window a report
* refresh exercises). A capture that only lands after load takes the
* metadata path, which formats a date correctly on either side of #444 and so
* would prove nothing.
* refresh exercises). A capture that only lands after load takes the metadata
* path, which formats a date correctly whatever the client does, and so would
* prove nothing about the metadata-less path.
*/
const mountWithEarlyMessage = (message: object) => {
cy.visit(ddcHostUrl(message))
@@ -164,12 +165,12 @@ const applyVaFilter = (
}
context('VA data-driven content through /ddc-host:', () => {
it('1 | stores a month-name date as a date literal (#444)', () => {
// `August 02, 1971` is how VA displays SOME_DATE=4231 under WORDDATE. The
// defect formatted it as a character value, which a numeric date column
// cannot be compared with - on a live estate that is the PROC SQL type clash
// inside mp_filtervalidate, and on this mock estate it is the quoted literal
// this assertion rejects.
it('1 | stores a month-name date as a date literal', () => {
// `August 02, 1971` is how VA displays SOME_DATE=4231 under WORDDATE. A
// month-name value formatted as character cannot be compared with a numeric
// date column: on a live estate that is the PROC SQL type clash inside
// mp_filtervalidate, and on this mock estate it is the quoted literal this
// assertion rejects.
applyVaFilter(vaMessage('SOME_DATE', 'August 02, 1971'), 'SOME_DATE').then(
(clause: StoredClause) => {
expect(clause.OPERATOR_NM).to.equal('=')
@@ -179,8 +180,8 @@ context('VA data-driven content through /ddc-host:', () => {
})
it('2 | stores a DATE9 date as a date literal (control)', () => {
// The shape that always worked: proving it still does is what separates a
// date-parsing fix from a harness that stopped delivering dates at all.
// DATE9, the control: it separates a parsing problem in the month-name case
// from a harness that stopped delivering dates at all.
applyVaFilter(vaMessage('SOME_DATE', '01DEC2021'), 'SOME_DATE').then(
(clause: StoredClause) => {
expect(clause.RAW_VALUE).to.equal("'01DEC2021'd")
+13 -7
View File
@@ -590,13 +590,19 @@ export class VaFilterService {
* Infers a column's SAS kind from a VA value string (deferred load, no
* metadata).
*
* The value is offered to the same parsers the clause builder formats with,
* so any date shape that can be formatted is also recognised here. A shape
* this function could not name was formatted as character instead - which is
* how a month-name date (`December 01, 2021`, what a WORDDATE parameter sends)
* became a quoted literal compared against a numeric date column. Datetime is
* tried first because it needs a time part, then date, then time; anything
* left falls back to the VA `dataType`.
* The value is offered to the same parsers the clause builder formats with, so
* any date shape that can be formatted is also recognised here. Datetime is
* tried first because it needs a time part, then date, then time; anything left
* falls back to the VA `dataType`, and finally to character.
*
* The result is a guess by construction: the deferred path has no column
* metadata, so a shape this function cannot name is formatted as character,
* and a character parameter whose value happens to look like a date ("1 May
* 2024") is formatted as a date. Either way the clause can disagree with the
* column, which costs the deferred `validatefilter` call: it is attempted with
* `suppressErrorAbortModal`, and when it fails the editor falls back to the
* unfiltered load and the post-load replay rebuilds the filter against the
* real metadata.
*/
private inferKindFromValue(value: string, dataType?: string): VaColumnKind {
if (this.parseDatetime(value)) return 'datetime'