issue 444 was a month-name date from VA becoming a character literal, and it only reproduced on a report refresh: the deferred initial load reads VA's buffered message, which has no column metadata, so the SAS kind comes from the value's shape. Posting a message into a running page exercises the other path - it arrives after getdata, metadata in hand, so a date is formatted correctly whatever the client does - which is why the unit spec could pin the fix but not show the failure a report author sees. The new spec frames the built app in sasjs/server's /ddc-host page, which mounts it with VA's sandbox attribute set and, with timing=early, replays the message from the moment the frame is created. That is the request and apply path a refresh takes, with mock services instead of a VA estate. Where the assertion sits: the mock public/validatefilter stores what it is sent, so the clause is read back from the drive (mpe_filtersource.json) rather than inferred from a request payload. The store is emptied per case because mpFilterStore reuses an existing FILTER_RK for a clause it has already seen, and a leftover row would answer for this run. The host page's own log is the second witness: the message is captured before the frame finishes loading. Three cases in one run - the WORDDATE shape from the issue, a DATE9 date that always worked, and a character parameter. Against the unfixed service the first stores 'August 02, 1971' and fails, while both controls pass; with the fix all three store the expected literal. So the guard goes red for the defect and only the defect. The spec runs last, needs no SAS, and takes about a minute against the mock estate the other specs already use.
129 lines
4.9 KiB
TypeScript
129 lines
4.9 KiB
TypeScript
import fs from 'fs'
|
|
import { defineConfig } from 'cypress'
|
|
|
|
export default defineConfig({
|
|
reporter: 'mochawesome',
|
|
|
|
reporterOptions: {
|
|
reportDir: 'cypress/results',
|
|
overwrite: false,
|
|
html: true,
|
|
json: false
|
|
},
|
|
|
|
viewportHeight: 900,
|
|
viewportWidth: 1600,
|
|
|
|
chromeWebSecurity: false,
|
|
defaultCommandTimeout: 30000,
|
|
|
|
env: {
|
|
hosturl: 'http://localhost:4200',
|
|
appLocation: '',
|
|
// The SASjs server the mock services run against, and the location the CI
|
|
// job streams the built app to (see sas/mocks/sasjs/sasjsconfig.json and
|
|
// CONTRIBUTING.md). `va-ddc-host.cy.ts` frames that build from the server's
|
|
// own /ddc-host page, so it needs both rather than `hosturl`.
|
|
sasjsServerUrl: 'http://localhost:5000',
|
|
streamedAppLocation: '/Public/app/dc',
|
|
site_id_SAS9: '70221618',
|
|
site_id_SASVIYA: '70253615',
|
|
site_id_SASJS: '123',
|
|
serverType: 'SASJS',
|
|
libraryToOpenIncludes_SASVIYA: 'viya',
|
|
libraryToOpenIncludes_SAS9: 'dc',
|
|
libraryToOpenIncludes_SASJS: 'dc_jslib',
|
|
debug: false,
|
|
screenshotOnRunFailure: false,
|
|
longerCommandTimeout: 50000,
|
|
testLicenceUserLimits: false
|
|
},
|
|
|
|
e2e: {
|
|
video: true,
|
|
// One retry in a CI run, none interactively. `editor.cy.ts` test 28
|
|
// intermittently fails on "app-loading ... continuously found" - a slow load,
|
|
// not an assertion - and a single retry costs a few seconds where a red
|
|
// pipeline costs forty minutes. Failures that are real still fail twice, and
|
|
// the retried attempt's output is in the run log.
|
|
retries: { runMode: 1, openMode: 0 },
|
|
setupNodeEvents(on, config) {
|
|
// `download.cy.ts` clears the downloads folder before each test through
|
|
// `cy.task('deleteFolder')`. That task was registered in
|
|
// cypress/plugins/index.js, which Cypress 10+ never loads unless it is
|
|
// required from here - so the task had no handler and the spec failed on
|
|
// its first hook.
|
|
on('task', {
|
|
deleteFolder(folderName: string) {
|
|
return new Promise((resolve, reject) => {
|
|
fs.rm(folderName, { recursive: true, force: true }, (err) =>
|
|
err ? reject(err) : resolve(null)
|
|
)
|
|
})
|
|
},
|
|
|
|
/**
|
|
* Writes a copy of a fixture whose primary keys are renumbered from a
|
|
* random base, and returns the path to write it to.
|
|
*
|
|
* An upload that matches what the table already holds produces no
|
|
* changes, and a changeset with no changes has no approval waiting - so
|
|
* re-running a spec that uploads the same file finds nothing on the
|
|
* approve screen. That is not a UI fault, and a retry after a partial
|
|
* first attempt hits it every time. New keys make the upload a real
|
|
* insert whichever attempt gets there.
|
|
*/
|
|
makeUniqueExcel({ source, dest }: { source: string; dest: string }) {
|
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
const ExcelJS = require('exceljs')
|
|
const workbook = new ExcelJS.Workbook()
|
|
const base = 1000000 + Math.floor(Math.random() * 8000000)
|
|
|
|
return workbook.xlsx.readFile(source).then(() => {
|
|
const sheet = workbook.worksheets[0]
|
|
let rowNumber = 0
|
|
|
|
sheet.eachRow((row: any, index: number) => {
|
|
if (index === 1) return // header
|
|
row.getCell(1).value = base + rowNumber++
|
|
})
|
|
|
|
return workbook.xlsx.writeFile(dest).then(() => dest)
|
|
})
|
|
},
|
|
|
|
/** removes one file - deleteFolder would take the source fixture too */
|
|
deleteFile(filePath: string) {
|
|
return new Promise((resolve) => {
|
|
;(fs as any).rm(filePath, { force: true }, () => resolve(null))
|
|
})
|
|
}
|
|
})
|
|
|
|
// Pin the browser locale so locale-formatted cells (intl-date/time/datetime)
|
|
// render deterministically regardless of the runner's system locale.
|
|
on('before:browser:launch', (browser, launchOptions) => {
|
|
// Recording only: the video capture is the browser window, which
|
|
// contains the Cypress runner - the command log takes the left ~450px
|
|
// and the app sits in the remaining pane. Sizing the window is the
|
|
// only lever that moves the capture, and only for a real Chromium
|
|
// browser (Electron ignores it). For a 16:9 pane, solve
|
|
// (W - 450) / (H - 96) = 16/9: W = 1920 -> H = 923 of window content,
|
|
// so pass RECORD_WINDOW_SIZE=1920,1010 and crop the pane.
|
|
// Inert unless the env var is set, so CI is unaffected.
|
|
if (process.env.RECORD_WINDOW_SIZE && browser.family === 'chromium') {
|
|
launchOptions.args.push(
|
|
`--window-size=${process.env.RECORD_WINDOW_SIZE}`
|
|
)
|
|
}
|
|
|
|
if (browser.family === 'chromium' && browser.name !== 'electron') {
|
|
launchOptions.args.push('--lang=en-GB')
|
|
}
|
|
|
|
return launchOptions
|
|
})
|
|
}
|
|
}
|
|
})
|