diff --git a/README.md b/README.md index a475317c4..400c39899 100644 --- a/README.md +++ b/README.md @@ -44,12 +44,10 @@ CYPRESS_DHIS2_PASSWORD=district The following commands can be used to run the tests: -| Command | Backend | Environment | Tests | -| ------------------- | :--------: | ----------: | -----------: | -| `yarn cy:open-live` | API server | Cypress UI | All | -| `yarn cy:run-live` | API server | Headless | All | -| `yarn cy:open-stub` | Fixtures | Cypress UI | Non-mutating | -| `yarn cy:run-stub` | Fixtures | Headless | Non-mutating | +| Comman | Backend | Environment | Tests | +| -------------- | :--------: | ----------: | ----: | +| `yarn cy:open` | API server | Cypress UI | All | +| `yarn cy:run` | API server | Headless | All | ### `yarn test` diff --git a/cypress/integration/edit/edit_dashboard/show_description.js b/cypress/integration/edit/edit_dashboard/show_description.js index 5d56f918b..3f059664a 100644 --- a/cypress/integration/edit/edit_dashboard/show_description.js +++ b/cypress/integration/edit/edit_dashboard/show_description.js @@ -1,6 +1,6 @@ import { When, Then } from 'cypress-cucumber-preprocessor/steps' import { clickViewActionButton } from '../../../elements/viewDashboard.js' -import { getApiBaseUrl } from '../../../support/server/utils.js' +import { getApiBaseUrl } from '../../../support/utils.js' const SHOW_DESC_RESP_CODE_SUCCESS = 201 const SHOW_DESC_RESP_CODE_FAIL = 409 diff --git a/cypress/integration/edit/edit_dashboard/translate_dashboard.js b/cypress/integration/edit/edit_dashboard/translate_dashboard.js index 235897687..bcf0b4543 100644 --- a/cypress/integration/edit/edit_dashboard/translate_dashboard.js +++ b/cypress/integration/edit/edit_dashboard/translate_dashboard.js @@ -5,8 +5,7 @@ import { dashboardDescriptionSel, clickViewActionButton, } from '../../../elements/viewDashboard.js' -import { getApiBaseUrl } from '../../../support/server/utils.js' -import { EXTENDED_TIMEOUT } from '../../../support/utils.js' +import { getApiBaseUrl, EXTENDED_TIMEOUT } from '../../../support/utils.js' let norwegianTitle = '' let norwegianDesc = '' diff --git a/cypress/integration/view/dashboard_filter/create_dashboard.js b/cypress/integration/view/dashboard_filter/create_dashboard.js index 55a200d99..20b51b250 100644 --- a/cypress/integration/view/dashboard_filter/create_dashboard.js +++ b/cypress/integration/view/dashboard_filter/create_dashboard.js @@ -9,10 +9,10 @@ import { dashboardChipSel, dashboardTitleSel, } from '../../../elements/viewDashboard.js' -import { getApiBaseUrl } from '../../../support/server/utils.js' import { EXTENDED_TIMEOUT, createDashboardTitle, + getApiBaseUrl, } from '../../../support/utils.js' const TEST_DASHBOARD_TITLE = createDashboardTitle('af') diff --git a/cypress/integration/view/view_dashboard/toggle_show_more_dashboards.js b/cypress/integration/view/view_dashboard/toggle_show_more_dashboards.js index b1dad68a1..6f3aed6f7 100644 --- a/cypress/integration/view/view_dashboard/toggle_show_more_dashboards.js +++ b/cypress/integration/view/view_dashboard/toggle_show_more_dashboards.js @@ -4,8 +4,7 @@ import { dashboardsBarContainerSel, showMoreLessSel, } from '../../../elements/viewDashboard.js' -import { getApiBaseUrl } from '../../../support/server/utils.js' -import { EXTENDED_TIMEOUT } from '../../../support/utils.js' +import { getApiBaseUrl, EXTENDED_TIMEOUT } from '../../../support/utils.js' const MIN_DASHBOARDS_BAR_HEIGHT = 71 const MAX_DASHBOARDS_BAR_HEIGHT = 431 diff --git a/cypress/integration/view/view_errors/error_while_show_description.js b/cypress/integration/view/view_errors/error_while_show_description.js index e63549f04..f3b94adb6 100644 --- a/cypress/integration/view/view_errors/error_while_show_description.js +++ b/cypress/integration/view/view_errors/error_while_show_description.js @@ -1,5 +1,5 @@ import { When, Then } from 'cypress-cucumber-preprocessor/steps' -import { getApiBaseUrl } from '../../../support/server/utils.js' +import { getApiBaseUrl } from '../../../support/utils.js' // Error scenario diff --git a/cypress/support/index.js b/cypress/support/index.js index 356da91ea..116b3858b 100644 --- a/cypress/support/index.js +++ b/cypress/support/index.js @@ -1,14 +1,7 @@ import { enableAutoLogin } from '@dhis2/cypress-commands' -import { enableNetworkShim } from './server/index.js' -import { getDefaultMode, isStubMode } from './server/utils.js' import './commands.js' -enableNetworkShim() - -if (!isStubMode(getDefaultMode())) { - // log in if using a live backend - enableAutoLogin() -} +enableAutoLogin() const resizeObserverLoopErrRe = /^[^(ResizeObserver loop limit exceeded)]/ Cypress.on('uncaught:exception', (err) => { diff --git a/cypress/support/server/Networkshim.js b/cypress/support/server/Networkshim.js deleted file mode 100644 index 6ae470270..000000000 --- a/cypress/support/server/Networkshim.js +++ /dev/null @@ -1,157 +0,0 @@ -import { - getFileName, - getApiBaseUrl, - removeAPIServerFromUrlPaths, -} from './utils.js' - -export default class NetworkShim { - constructor(hosts) { - this.state = null - this.hosts = hosts - } - - initCaptureMode() { - this.state = { - totalSize: 0, - duplicates: 0, - nonDeterministicResponses: 0, - requests: {}, - } - } - - initStubMode() { - cy.readFile(getFileName()).then((file) => { - this.state = { - requests: this.parseFileRequests(file.requests), - } - }) - } - - parseFileRequests(requests) { - return requests.reduce((acc, request) => { - const { method, path, requestBody } = request - const key = this.createKey(method, path, requestBody) - request.response = JSON.parse(request.response) - acc[key] = request - return acc - }, {}) - } - - captureRequestsAndResponses() { - cy.server({ - onAnyRequest: this.captureRequest, - onAnyResponse: this.captureResponse, - }) - } - - createStubRoutes() { - cy.server() - Object.values(this.state.requests).forEach((stub) => { - cy.route({ - url: getApiBaseUrl() + stub.path, - method: stub.method, - /* - TODO: for POST / PUT requests we will quite likely have to - be able to differentiate between different request bodies - specifying a body in the route options could theoretically be - a way to do so, but quite likely it doesn't work. I have posted - a question on Stackoverlflow about this topic: - https://stackoverflow.com/questions/62530197/how-to-access-request-body-in-cy-route-response-callback - */ - body: stub.requestBody || undefined, - response: stub.response, - }) - }) - } - - processRequest(xhr) { - const host = this.hosts.find((host) => xhr.url.indexOf(host) === 0) - const path = xhr.url.substr(host.length) - const key = this.createKey(xhr.method, path, xhr.request.body) - - return { host, path, key } - } - - createKey(method, path, requestBody) { - const sections = [method, path] - - if (requestBody) { - sections.push(JSON.stringify(requestBody)) - } - - return sections.join('__') - } - - captureRequest = (_, xhr) => { - const { host, path, key } = this.processRequest(xhr) - if (!host) { - // pass through - return xhr - } - - if (this.state.requests[key]) { - // Repeated request - this.state.requests[key].count += 1 - this.state.duplicates += 1 - } else { - // New request - this.state.requests[key] = { - path, - method: xhr.method, - requestBody: xhr.request.body, - count: 1, - response: null, - } - } - return xhr - } - - captureResponse = async (_, xhr) => { - const { host, key } = this.processRequest(xhr) - if (!host) { - // pass through - return xhr - } - - const stateRequest = this.state.requests[key] - const { size, text } = await this.createResponseBlob(xhr) - - const scrubbedText = removeAPIServerFromUrlPaths(text) - - if (stateRequest.response) { - if (scrubbedText !== stateRequest.response) { - this.state.nonDeterministicResponses += 1 - stateRequest.nonDeterministic = true - } - } else { - // TODO: Capture response headers - stateRequest.response = scrubbedText - stateRequest.size = size - - this.state.totalSize += size - } - - return xhr - } - - async createResponseBlob(xhr) { - const responseBodyStr = JSON.stringify(xhr.response.body) - const blob = new Blob([responseBodyStr], { type: 'application/json' }) - const size = blob.size - const text = await blob.text() - - return { size, text } - } - - writeFile() { - const requestArray = Object.values(this.state.requests) - cy.log( - `Networkshim successfully captured ${requestArray.length} requests`, - this.state - ) - cy.writeFile(getFileName(), { - ...this.state, - requests: requestArray, - }) - } -} diff --git a/cypress/support/server/constants.js b/cypress/support/server/constants.js deleted file mode 100644 index de2af8e91..000000000 --- a/cypress/support/server/constants.js +++ /dev/null @@ -1,9 +0,0 @@ -export const API_STUB_MODES = { - STUB: 'STUB', - DISABLED: 'DISABLED', - CAPTURE: 'CAPTURE', -} -export const DEFAULT_API_STUB_MODE = API_STUB_MODES.STUB - -export const NETWORK_FIXTURES_FILE_PATH = - 'cypress/fixtures/network/requests.json' diff --git a/cypress/support/server/enableNetworkShim.js b/cypress/support/server/enableNetworkShim.js deleted file mode 100644 index 37eba42bb..000000000 --- a/cypress/support/server/enableNetworkShim.js +++ /dev/null @@ -1,36 +0,0 @@ -import NetworkShim from './Networkshim.js' -import { isDisabledMode, isCaptureMode, getApiBaseUrl } from './utils.js' -import visitWithUnfetchOverwriteFn from './visitWithUnfetchOverwriteFn.js' - -export default function enableNetworkShim(hosts = [getApiBaseUrl()]) { - // No need to stub anything when disabled - if (isDisabledMode()) { - return - } - // Replace window.fetch with unfetch, which uses XHR and cypress can work with this - Cypress.Commands.overwrite('visit', visitWithUnfetchOverwriteFn) - - const networkShim = new NetworkShim(hosts) - - before(() => { - if (isCaptureMode()) { - networkShim.initCaptureMode() - } else { - networkShim.initStubMode() - } - }) - - beforeEach(() => { - if (isCaptureMode()) { - networkShim.captureRequestsAndResponses() - } else { - networkShim.createStubRoutes() - } - }) - - after(() => { - if (isCaptureMode()) { - networkShim.writeFile() - } - }) -} diff --git a/cypress/support/server/index.js b/cypress/support/server/index.js deleted file mode 100644 index afd69b8ac..000000000 --- a/cypress/support/server/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default as enableNetworkShim } from './enableNetworkShim.js' diff --git a/cypress/support/server/utils.js b/cypress/support/server/utils.js deleted file mode 100644 index 4b69c2c6b..000000000 --- a/cypress/support/server/utils.js +++ /dev/null @@ -1,37 +0,0 @@ -import { - API_STUB_MODES, - DEFAULT_API_STUB_MODE, - NETWORK_FIXTURES_FILE_PATH, -} from './constants.js' - -export const getApiBaseUrl = () => { - const baseUrl = Cypress.env('dhis2_base_url') || '' - - if (!baseUrl) { - throw new Error( - 'No `dhis2_base_url` found. Please make sure to add it to `cypress.env.json`' - ) - } - - return baseUrl -} - -export const getDefaultMode = () => - Cypress.env('dhis2_api_stub_mode') || DEFAULT_API_STUB_MODE - -export const isDisabledMode = () => - Cypress.env('dhis2_api_stub_mode') === API_STUB_MODES.DISABLED - -export const isCaptureMode = () => - Cypress.env('dhis2_api_stub_mode') === API_STUB_MODES.CAPTURE - -export const isStubMode = () => - Cypress.env('dhis2_api_stub_mode') === API_STUB_MODES.STUB - -export const getFileName = () => NETWORK_FIXTURES_FILE_PATH - -export const removeAPIServerFromUrlPaths = (text) => { - const apiEndpointUrl = new RegExp(`${getApiBaseUrl()}/api`, 'gi') - - return text.replace(apiEndpointUrl, '') -} diff --git a/cypress/support/server/visitWithUnfetchOverwriteFn.js b/cypress/support/server/visitWithUnfetchOverwriteFn.js deleted file mode 100644 index 282a6abf7..000000000 --- a/cypress/support/server/visitWithUnfetchOverwriteFn.js +++ /dev/null @@ -1,16 +0,0 @@ -const visitWithUnfetchOverwriteFn = (originalFn, url, options = {}) => - cy - .readFile('cypress/assets/unfetch.umd.js', { log: false }) - .then((content) => - originalFn(url, { - ...options, - onBeforeLoad: (win) => { - delete win.fetch - win.eval(content) - win.fetch = win.unfetch - options.onBeforeLoad && options.onBeforeLoad(win) - }, - }) - ) - -export default visitWithUnfetchOverwriteFn diff --git a/cypress/support/utils.js b/cypress/support/utils.js index 909d50129..a4a40c59d 100644 --- a/cypress/support/utils.js +++ b/cypress/support/utils.js @@ -1,5 +1,17 @@ export const EXTENDED_TIMEOUT = { timeout: 25000 } +export const getApiBaseUrl = () => { + const baseUrl = Cypress.env('dhis2_base_url') || '' + + if (!baseUrl) { + throw new Error( + 'No `dhis2_base_url` found. Please make sure to add it to `cypress.env.json`' + ) + } + + return baseUrl +} + export const goOffline = () => { cy.log('**go offline**') .then(() => { diff --git a/package.json b/package.json index 5dd187c60..f4ab14107 100644 --- a/package.json +++ b/package.json @@ -43,11 +43,8 @@ "validate-push": "CI=true yarn test", "postinstall": "patch-package", "cypress:start": "BROWSER=none yarn start", - "cy:open-live": "cypress_dhis2_api_stub_mode=DISABLED d2-utils-cypress open --appStart 'yarn cypress:start'", - "cy:run-live": "cypress_dhis2_api_stub_mode=DISABLED d2-utils-cypress run --appStart 'yarn cypress:start'", - "cy:open-stub": "cypress_dhis2_api_stub_mode=STUB d2-utils-cypress open --appStart 'yarn cypress:start'", - "cy:run-stub": "cypress_dhis2_api_stub_mode=STUB d2-utils-cypress run --tags '@nonmutating' --appStart 'yarn cypress:start' --record", - "cy:capture": "cypress_dhis2_api_stub_mode=CAPTURE yarn d2-utils-cypress run --appStart 'yarn cypress:start'" + "cy:open": "d2-utils-cypress open --appStart 'yarn cypress:start'", + "cy:run": "d2-utils-cypress run --appStart 'yarn cypress:start'" }, "devDependencies": { "@dhis2/cli-app-scripts": "^10.3.10",