Skip to content

Commit

Permalink
[PC-1411] Initialize Cypress
Browse files Browse the repository at this point in the history
Summary:
Laying a framework for UI integration testing.
Actions taken:
- `yarn add --dev cypress`
- Added a script shortcut in `package.json`
- Ran `yarn cypress:open`
- After that worked, cleared out the example files
- Added a single test suite for auth
- With env file config and sample
- Tweaked eslint config for Cypress globals

Here's what the GUI looks like with the sample test showing successful access to the Live UI:
{F176859}

Test Plan:
- `yarn install`
- Prepare environment variables following the instructions in `cypress/README.md`:
  - Find your base URL - it's where you go when you run `yarn dev` to test your local UI.
  - Get your Google session cookie for that environment (it's the value of the cookie `default-session5`).
  - If you don't want to put the cookie on the command line later, you can copy `cypress.template.env.json` to `cypress.env.json` and put the cookie's value there. If you specify the env var on the command line AND in this file, the command line value takes priority.
- `yarn dev` to start the UI. Where you access this is the `CYPRESS_BASE_URL`, port included!
- `CYPRESS_BASE_URL='...' CYPRESS_GOOGLE_COOKIE='...' yarn cypress:open` to play around with the GUI. It does quite a lot of stuff!
- `CYPRESS_... yarn cypress:run` to run the tests headlessly, entirely in your CLI.

Reviewers: michelle, vihang, #third_party_approvers, zasgar

Reviewed By: michelle, #third_party_approvers

JIRA Issues: PC-1411

Signed-off-by: Nick Lanam <[email protected]>

Differential Revision: https://phab.corp.pixielabs.ai/D10556

GitOrigin-RevId: 125affe
  • Loading branch information
NickLanam authored and copybaranaut committed Jan 28, 2022
1 parent 643632f commit e51dbdb
Show file tree
Hide file tree
Showing 14 changed files with 1,942 additions and 33 deletions.
9 changes: 9 additions & 0 deletions src/ui/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,15 @@
"jest/globals": true
}
},
{
"files": [
"cypress/**/*"
],
"plugins": ["cypress"],
"env": {
"cypress/globals": true
}
},
{
"files": ["./tools/licenses/*.js"],
"env": {
Expand Down
6 changes: 6 additions & 0 deletions src/ui/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ dist/*
# Coverage results.
coverage/

# Cypress integration/e2e tests
cypress/downloads/
cypress/screenshots/
cypress/videos/
cypress.env.json

# For Jenkins test results.
junit.xml

Expand Down
923 changes: 909 additions & 14 deletions src/ui/.pnp.cjs

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions src/ui/cypress.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"baseUrl": "https://dev.withpixie.dev"
}
4 changes: 4 additions & 0 deletions src/ui/cypress.template.env.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"GOOGLE_SESSION_COOKIE_KEY": "default-session5",
"GOOGLE_SESSION_COOKIE": ""
}
22 changes: 22 additions & 0 deletions src/ui/cypress/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Running Pixie UI Integration Tests

These tests use [Cypress][https://docs.cypress.io] to run integration tests in a browser.

To run them, you need two things:
1. The base URL of a running instance of Pixie's UI. By default, this assumes `dev.withpixie.dev` which comes from the [self-host instructions](https://docs.px.dev/installing-pixie/install-guides/self-hosted-pixie).
2. The Pixie session cookie for a Google-specific login to the Pixie UI you pointed to.
To get this, load the UI, open the dev tools, and look for the `default-sessionSOME_NUMBER` cookie. That is what you'll use to set `CYPRESS_GOOGLE_SESSION_COOKIE`.
The name of that cookie will set `CYPRESS_GOOGLE_SESSION_COOKIE_KEY`.

Then, run this command:
`CYPRESS_BASE_URL='https://dev.withpixie.dev' CYPRESS_GOOGLE_SESSION_COOKIE_KEY='default-sessionSOME_NUMBER' CYPRESS_GOOGLE_SESSION_COOKIE='paste-your-session-cookie-value-here' yarn cypress:open`

You can use `... yarn cypress:run` instead if you want to run the tests immediately and headlessly.

If you don't want to set these environment variables every time, you can override everything except the base URL in `cypress.env.json` (copy from `cypress.template.env.json`):
```json
{
"GOOGLE_SESSION_COOKIE_KEY": "default-sessionSOME_NUMBER",
"GOOGLE_SESSION_COOKIE": "paste it here"
}
```
Empty file.
59 changes: 59 additions & 0 deletions src/ui/cypress/integration/auth-flow/guards.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright 2018- The Pixie Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/

// Tests that authentication is enforced for routes that should do so.
// For example, most pages should redirect to `/login` if the user isn't authorized.
// However, some routes are accessible regardless, such as the auth routes themselves.
describe('Authentication guards', () => {
beforeEach(() => {
// TODO(nick): For later tests, this can be expensive (like refreshing on an expensive script).
// Can `cy.go` (with cy.visit in the before rather than beforeEach) work?
cy.visit('/');
});

it('Loads at all', () => {
cy.get('div#root').should('exist');
});

it('Redirects to login', () => {
cy.location('pathname').should('eq', '/auth/login');
});

it('Can access email/password auth', () => {
const loginButtons = cy.get('.MuiBox-root > button');
loginButtons.should('have.length', 2);
// TODO: We can't click the buttons (redirect leaves the domain under test, which Cypress doesn't work well with).
// We don't want to do that anyway; what we really want to do is verify our own callback flows with cy.request().
// See src/ui/cypress/commands/login.command.js for more info.
});

describe('Logged in with a Google account', () => {
// Intercepts have to be set up before a test runs.
// Otherwise, the intercept isn't wired until all commands have queued.
before(() => {
cy.loginGoogle();
});

it('Sees us as authenticated', () => {
cy.request({
url: '/api/authorized',
failOnStatusCode: false,
}).its('status').should('eq', 200);
});
});
});
Empty file added src/ui/cypress/plugins/.gitkeep
Empty file.
19 changes: 19 additions & 0 deletions src/ui/cypress/support/commands/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright 2018- The Pixie Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/

import './login.command';
75 changes: 75 additions & 0 deletions src/ui/cypress/support/commands/login.command.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright 2018- The Pixie Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/


// Note: this works, but relies on a real user's auth session cookies.
// For CI, we'll need test users and to be able to log in as them:
// https://docs.cypress.io/guides/testing-strategies/google-authentication#Setting-Google-app-credentials-in-Cypress
// https://docs.cypress.io/guides/testing-strategies/auth0-authentication
Cypress.Commands.add('loginGoogle', () => {
const GOOGLE_COOKIE_KEY = Cypress.env('GOOGLE_SESSION_COOKIE_KEY');

cy.setCookie('user-has-accepted-cookies', 'true');
cy.setCookie(GOOGLE_COOKIE_KEY, Cypress.env('GOOGLE_SESSION_COOKIE'));

// For Google auth, this is the only cookie needed to validate a session.
cy.getCookie(GOOGLE_COOKIE_KEY).should('have.property', 'value');

// Every request from here on out needs CSRF headers.
cy.intercept(`${Cypress.config().baseUrl}/api/**`, (req) => {
req.headers['x-csrf'] = 'undefined';
req.headers.Referer = req.headers.origin;
req.continue();
});
});

// cy.intercept doesn't touch cy.request.
// We always need the CSRF headers even when unauthenticated,
// so we override the request method to always inject them.
// We would do cy.visit as well, but that doesn't need the same headers.
Cypress.Commands.overwrite('request', (originalFn, ...args) => {
const defaults = {
headers: {
'x-csrf': 'undefined',
'Referer': Cypress.config().baseUrl,
},
};

// cy.request has several signatures; have to handle them all.
let options = {};
if (typeof args[0] === 'object' && args[0] !== null) {
options = args[0];
} else if (args.length === 1) {
[options.url] = args;
} else if (args.length === 2) {
[options.method, options.url] = args;
} else if (args.length === 3) {
[options.method, options.url, options.body] = args;
}

return originalFn({
...defaults,
...options,
...{
headers: {
...defaults.headers,
...options.headers,
},
},
});
});
19 changes: 19 additions & 0 deletions src/ui/cypress/support/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright 2018- The Pixie Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/

import './commands';
6 changes: 5 additions & 1 deletion src/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,15 @@
"compression-webpack-plugin": "^4.0.0",
"concurrently": "^6.2.0",
"css-loader": "^5.2.6",
"cypress": "^9.3.0",
"esbuild": "^0.13.13",
"esbuild-jest": "^0.5.0",
"esbuild-loader": "^2.16.0",
"eslint": "^8.6.0",
"eslint-config-airbnb-typescript": "^16.1.0",
"eslint-import-resolver-node": "^0.3.6",
"eslint-import-resolver-typescript": "^2.5.0",
"eslint-plugin-cypress": "^2.12.1",
"eslint-plugin-import": "^2.25.4",
"eslint-plugin-jest": "^25.3.4",
"eslint-plugin-react": "^7.28.0",
Expand Down Expand Up @@ -182,7 +184,9 @@
"coverage_ci": "jest --coverage --maxWorkers=4",
"license_check": "yarn pnpify license-checker",
"lint": "tsc --noEmit && eslint -c .eslintrc.json .",
"vgspec": "NODE_PATH=./src ts-node --compiler-options '{\"module\":\"commonjs\"}' src/utils/print-vgspec-cli.ts"
"vgspec": "NODE_PATH=./src ts-node --compiler-options '{\"module\":\"commonjs\"}' src/utils/print-vgspec-cli.ts",
"cypress:open": "cypress open",
"cypress:run": "cypress run"
},
"resolutions": {
"@types/react": "^17.0.2",
Expand Down
Loading

0 comments on commit e51dbdb

Please sign in to comment.