Skip to content

Commit

Permalink
Cypress end to end smoke test (#2241)
Browse files Browse the repository at this point in the history
Co-authored-by: Thomas <[email protected]>
  • Loading branch information
allisonking and ThomasLaPiana authored Feb 10, 2023
1 parent e50015c commit eabd190
Show file tree
Hide file tree
Showing 17 changed files with 3,695 additions and 6 deletions.
54 changes: 54 additions & 0 deletions .github/workflows/cypress_e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: Cypress E2E Tests

on:
pull_request:
paths-ignore:
- "**.md"
push:
branches:
- "main"

env:
CI: true

jobs:
Cypress-E2E:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x]
steps:
- name: Checkout
uses: actions/checkout@v3

- name: Install Nox
run: pip install nox>=2022

- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}

- name: Start test environment in the background
run: nox -s "fides_env(test)" -- keep_alive

- name: Install dependencies
run: |
cd clients/cypress-e2e
npm install
- name: Cypress E2E tests
uses: cypress-io/github-action@v5
with:
working-directory: clients/cypress-e2e
install: false
wait-on: "http://localhost:8080, http://localhost:3001"
record: true
env:
# pass the Cypress Cloud record key as an environment variable
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
# pass GitHub token to allow accurately detecting a build vs a re-run build
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Teardown
run: nox -s teardown
11 changes: 8 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ The types of changes are:

* Fixed bug where refreshing a page in the UI would result in a 404 [#2502](https://github.com/ethyca/fides/pull/2502)

### Developer Experience

* Added new Cypress E2E smoke tests [#2241](https://github.com/ethyca/fides/pull/2241)
* New command `nox -s e2e_test` which will spin up the test environment and run true E2E Cypress tests against it [#2417](https://github.com/ethyca/fides/pull/2417)
* Cypress E2E tests now run in CI and are reported to Cypress Cloud [#2417](https://github.com/ethyca/fides/pull/2417)

### Removed

* Remove feature flagged config wizard stepper from Admin UI [#2553](https://github.com/ethyca/fides/pull/2553)
Expand All @@ -66,7 +72,6 @@ The types of changes are:
### Added
* Add default storage configuration functionality and associated APIs [#2438](https://github.com/ethyca/fides/pull/2438)


## [2.6.1](https://github.com/ethyca/fides/compare/2.6.0...2.6.1)

### Fixed
Expand Down Expand Up @@ -118,8 +123,8 @@ The types of changes are:

### Developer Experience

* `nox -s test_env` has been replaced with `nox -s fides_env(dev)`
* New command `nox -s fides_env(test)` creates a complete test environment with seed data (similar to `fides_env(dev)`) but with the production fides image so the built UI can be accessed at `localhost:8080` [#2399](https://github.com/ethyca/fides/pull/2399)
* `nox -s test_env` has been replaced with `nox -s "fides_env(dev)"`
* New command `nox -s "fides_env(test)"` creates a complete test environment with seed data (similar to `fides_env(dev)`) but with the production fides image so the built UI can be accessed at `localhost:8080` [#2399](https://github.com/ethyca/fides/pull/2399)
* Change from code climate to codecov for coverage reporting [#2402](https://github.com/ethyca/fides/pull/2402)

### Fixed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ const RequestRow = ({
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
height="36px"
data-testid={`privacy-request-row-${request.id}`}
data-testid={`privacy-request-row-${request.status}`}
>
<Td px={0}>
<Checkbox
Expand Down
3 changes: 3 additions & 0 deletions clients/admin-ui/src/pages/login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ const Login: NextPage = () => {
onBlur={handleBlur}
value={values.email}
isInvalid={touched.email && Boolean(errors.email)}
data-testid="input-username"
/>
<FormErrorMessage>{errors.email}</FormErrorMessage>
</FormControl>
Expand All @@ -228,6 +229,7 @@ const Login: NextPage = () => {
isInvalid={
touched.password && Boolean(errors.password)
}
data-testid="input-password"
/>
<FormErrorMessage>{errors.password}</FormErrorMessage>
</FormControl>
Expand All @@ -240,6 +242,7 @@ const Login: NextPage = () => {
disabled={!values.email || !values.password}
isLoading={isLoading}
colorScheme="primary"
data-testid="sign-in-btn"
>
Sign in
</Button>
Expand Down
33 changes: 33 additions & 0 deletions clients/cypress-e2e/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Cypress E2E Tests

This folder is meant to contain true end to end tests for Fides applications. Unlike Cypress tests nested within `admin-ui` or `privacy-center`, these tests do not stub any of their endpoints, and instead they require running against the Fides test environment set up.

## Running

First, start up the test environment. This will spin up all relevant servers and frontend services. From the root directory of this repo:

```
nox -s "fides_env(test)"
```

Admin UI will be found at `localhost:3000` and Privacy Center at `localhost:3001`.

Then, in this folder:

```
npm run cy:run
```

### Environment variables

We specify environment variables for our server URLs so that it is possible to run the test suite against different environments. These can be found in [cypress.config.ts](./cypress.config.ts) and each one can be overwritten by prefixing with `CYPRESS_`. For example:

```sh
export CYPRESS_ADMIN_UI_URL="http://localhost:8080"
```

## Development notes

Because we are testing full end to end, changes Cypress makes will propagate to the test database and be saved. This means the tests we write here are ideally resilient to data in the database. Therefore, for example, we may not be able to say "Approve the privacy request", but instead may have to say "Approve the most recent privacy request" since another test may have added a privacy request, or we ourselves may have added another privacy request while developing the test.

Also, because we are testing multiple applications that interact with one another, we may have to cross origins. Cypress 12 introduced this feature, though there are still some experimental features within it. Before using `cy.origin`, make sure to read [the documentation](https://docs.cypress.io/api/commands/origin) as there are some interesting caveats with it.
30 changes: 30 additions & 0 deletions clients/cypress-e2e/cypress.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { defineConfig } from "cypress";

export default defineConfig({
projectId: "bauzeh",
e2e: {
setupNodeEvents(on, config) {
// implement node event listeners here
},
// Enabled in order to pass custom commands to origin blocks
// https://docs.cypress.io/api/commands/origin#Dependencies--Sharing-Code
experimentalOriginDependencies: true,
},

retries: {
runMode: 3,
openMode: 0,
},

env: {
// These can be overwritten by exporting `CYPRESS_{name}`, for example
// export CYPRESS_ADMIN_UI_URL="http://staging.example.com"
API_URL: "http://localhost:8080/api/v1",
ADMIN_UI_URL: "http://localhost:8080",
PRIVACY_CENTER_URL: "http://localhost:3001",

// Credentials
USERNAME: "root_user",
PASSWORD: "Testpassword1!",
},
});
2 changes: 2 additions & 0 deletions clients/cypress-e2e/cypress/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
videos/
screenshots/
9 changes: 9 additions & 0 deletions clients/cypress-e2e/cypress/e2e/login.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { ADMIN_UI_URL } from "../support/constants";

describe("Log in", () => {
it("can log in and be redirected to the home page", () => {
cy.visit(ADMIN_UI_URL);
cy.login();
cy.getByTestId("Home");
});
});
78 changes: 78 additions & 0 deletions clients/cypress-e2e/cypress/e2e/smoke_test.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import {
ADMIN_UI_URL,
API_URL,
PRIVACY_CENTER_URL,
} from "../support/constants";

describe("Smoke test", () => {
it("can submit an access request from the privacy center", () => {
// Watch these routes without changing or stubbing its response
cy.intercept("PATCH", `${API_URL}/privacy-request/administrate/approve`).as(
"patchRequest"
);
cy.intercept("GET", `${API_URL}/privacy-request*`).as("getRequests");

// Submit the access request from the privacy center
cy.visit(PRIVACY_CENTER_URL);
cy.getByTestId("card").contains("Access your data").click();
cy.getByTestId("privacy-request-form").within(() => {
cy.get("input#name").type("Jenny");
cy.get("input#email").type("[email protected]");

cy.get("input#phone").type("555 867 5309");
cy.get("button").contains("Continue").click();
});

// Approve the request in the admin UI
cy.visit(ADMIN_UI_URL);
cy.origin(ADMIN_UI_URL, () => {
// Makes custom commands available to all subsequent cy.origin() commands
// https://docs.cypress.io/api/commands/origin#Custom-commands
require("../support/commands");
cy.login();
cy.get("div").contains("Review privacy requests").click();
let numCompletedRequests = 0;
cy.wait("@getRequests").then((interception) => {
const { items } = interception.response.body;
numCompletedRequests = items.filter(
(i) => i.status === "complete"
).length;
});

cy.getByTestId("privacy-request-row-pending")
.first()
.trigger("mouseover")
.get("button")
.contains("Approve")
.click();

// Go past the confirmation modal
cy.getByTestId("continue-btn").click();

cy.wait("@patchRequest");
cy.wait("@getRequests");

// Make sure there is one more completed request than originally
cy.getByTestId("privacy-request-row-complete").then((rows) => {
expect(rows.length).to.eql(numCompletedRequests + 1);
});
});
});

it("can access mongo and postgres connectors", () => {
cy.intercept(`${API_URL}/connection_type`).as("getConnectionType");
cy.intercept(`${API_URL}/connection*`).as("getConnections");

cy.visit(ADMIN_UI_URL);
cy.login();
cy.get("div").contains("Configure privacy requests").click();
cy.wait("@getConnections");
cy.get("a").contains("Connection manager").click();
cy.wait("@getConnectionType");
cy.getByTestId("connection-grid-item-mongodb_connector").within(() => {
// TODO: UI does not appear to indicate when test fails
cy.get("button").contains("Test").click();
});
cy.getByTestId("connection-grid-item-postgres_connector");
});
});
39 changes: 39 additions & 0 deletions clients/cypress-e2e/cypress/support/commands.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/// <reference types="cypress" />
import { CREDENTIALS } from "./constants";

Cypress.Commands.add("getByTestId", (selector, ...args) =>
cy.get(`[data-testid='${selector}']`, ...args)
);

Cypress.Commands.add("login", () => {
cy.getByTestId("input-username").type(CREDENTIALS.username);
cy.getByTestId("input-password").type(CREDENTIALS.password);
cy.getByTestId("sign-in-btn").click();
});

declare global {
namespace Cypress {
interface Chainable {
/**
* Custom command to select DOM element by data-testid attribute
* @example cy.getByTestId('clear-btn')
*/
getByTestId(
selector: string,
options?: Partial<
Cypress.Loggable &
Cypress.Timeoutable &
Cypress.Withinable &
Cypress.Shadow
>
): Chainable<JQuery<HTMLElement>>;
/**
* Log in. Must navigate to the login page before this command.
*/
login(): void;
}
}
}

// Convert this to a module instead of script (allows import/export)
export {};
8 changes: 8 additions & 0 deletions clients/cypress-e2e/cypress/support/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const CREDENTIALS = {
username: Cypress.env("USERNAME"),
password: Cypress.env("PASSWORD"),
};

export const API_URL = Cypress.env("API_URL");
export const ADMIN_UI_URL = Cypress.env("ADMIN_UI_URL");
export const PRIVACY_CENTER_URL = Cypress.env("PRIVACY_CENTER_URL");
20 changes: 20 additions & 0 deletions clients/cypress-e2e/cypress/support/e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// ***********************************************************
// This example support/e2e.ts is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************

// Import commands.js using ES2015 syntax:
import "./commands";

// Alternatively you can use CommonJS syntax:
// require('./commands')
8 changes: 8 additions & 0 deletions clients/cypress-e2e/cypress/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["es5", "dom"],
"types": ["cypress", "node"]
},
"include": ["**/*.ts", "../cypress.config.ts"]
}
Loading

0 comments on commit eabd190

Please sign in to comment.