Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

kwa(front): Add UI tests with Cypress #2088

Merged
merged 3 commits into from
Jan 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .github/workflows/test-node.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,35 @@ jobs:
run: |
cd pkg/new-ui/v1beta1/frontend
npm run test:prod

frontend-ui-tests:
name: UI tests with Cypress
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup node version to 12
uses: actions/setup-node@v3
with:
node-version: 12

- name: Fetch Kubeflow and install common code dependencies
run: |
COMMIT=$(cat pkg/new-ui/v1beta1/frontend/COMMIT)
cd /tmp && git clone https://github.com/kubeflow/kubeflow.git
cd kubeflow
git checkout $COMMIT
cd components/crud-web-apps/common/frontend/kubeflow-common-lib
npm i
npm run build
npm link ./dist/kubeflow
- name: Install KWA dependencies
run: |
cd pkg/new-ui/v1beta1/frontend
npm i
npm link kubeflow
- name: Serve UI & run Cypress tests in Chrome and Firefox
run: |
cd pkg/new-ui/v1beta1/frontend
npm run start & npx wait-on http://localhost:4200
npm run ui-test-ci-all
4 changes: 4 additions & 0 deletions pkg/new-ui/v1beta1/frontend/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,7 @@ Thumbs.db

# Font files (automatically generating)
/src/assets/fonts

# Cypress
cypress/screenshots
cypress/downloads
40 changes: 40 additions & 0 deletions pkg/new-ui/v1beta1/frontend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Frontend

This project was generated with Angular CLI.

## Development server

Run `npm run start` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.

## Code scaffolding

Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.

## Build

Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.

## Running unit tests

Run `npm run test` to execute the unit tests via [Karma](https://karma-runner.github.io).

## Running UI tests

To run UI tests locally, make sure that node modules are installed and the frontend is serving the UI under `localhost:4200`. Then use `npm run ui-test` to execute the UI tests via [Cypress](https://www.cypress.io/). This will open Cypress and from there you may select the browser in which the tests will run.

Ideally, tests should be run both in Chrome and Firefox and for that there is the script `npm run ui-test-ci-all` that `runs` (instead of `opening`) Cypress. Note that in order for tests to run in a browser, the browser needs to be already installed on the system.

Make sure to check out these guides for system-specific information on installing and running Cypress

- https://docs.cypress.io/guides/getting-started/installing-cypress
- https://docs.cypress.io/guides/references/advanced-installation

### WSL2

In order to be run in a WSL2 installation, Cypress requires these [dependencies](https://docs.cypress.io/guides/getting-started/installing-cypress#Linux-Prerequisites).

In the case of WSL2 on _Windows 10_, [this extra setup](https://docs.cypress.io/guides/references/advanced-installation#Windows-Subsystem-for-Linux) is required in order to have an X Server running in Windows host and creating the browser window.

## Further help

To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI documents](https://angular.io/cli).
23 changes: 21 additions & 2 deletions pkg/new-ui/v1beta1/frontend/angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,21 @@
}
},
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"builder": "@cypress/schematic:cypress",
"options": {
"devServerTarget": "frontend:serve",
"watch": true,
"headless": false
},
"configurations": {
"production": {
"devServerTarget": "frontend:serve:production"
}
}
},
"cypress-run": {
"builder": "@cypress/schematic:cypress",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "frontend:serve"
},
"configurations": {
Expand All @@ -140,6 +152,13 @@
"src/**/*.html"
]
}
},
"cypress-open": {
"builder": "@cypress/schematic:cypress",
"options": {
"watch": true,
"headless": false
}
}
}
}
Expand Down
9 changes: 9 additions & 0 deletions pkg/new-ui/v1beta1/frontend/cypress.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { defineConfig } from 'cypress';

export default defineConfig({
video: false,
e2e: {
setupNodeEvents(on, config) {},
baseUrl: 'http://localhost:4200',
},
});
14 changes: 14 additions & 0 deletions pkg/new-ui/v1beta1/frontend/cypress/e2e/form-page.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
describe('New Experiment form page', () => {
beforeEach(() => {
cy.mockDashboardRequest();
cy.mockTrialTemplate();
});

it('New Experiment form page loads template without errors', () => {
cy.visit('/new');
cy.wait(['@mockDashboardRequest', '@mockTrialTemplate']);

// after fetching the data the page should not have an error snackbar
cy.get('[data-cy-snack-status=ERROR]').should('not.exist');
});
});
78 changes: 78 additions & 0 deletions pkg/new-ui/v1beta1/frontend/cypress/e2e/index-page.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { STATUS_TYPE } from 'kubeflow';
import { parseStatus } from 'src/app/pages/experiments/utils';

describe('Index page', () => {
beforeEach(() => {
cy.mockDashboardRequest();
cy.mockNamespacesRequest();
cy.fixture('settings').then(settings => {
cy.mockExperimentsRequest(settings.namespace);
});
cy.fixture('experiments').as('experimentsRequest');
});

it('should have an "Experiments" title', () => {
cy.visit('/');
cy.get('[data-cy-toolbar-title]').contains('Experiments').should('exist');
});

it('should list Experiments without errors', () => {
cy.visit('/');
// wait for the requests to complete
cy.wait(['@mockNamespacesRequest', '@mockExperimentsRequest']);

// after fetching the data the page should not have an error snackbar
cy.get('[data-cy-snack-status=ERROR]').should('not.exist');
});

// We use function () in order to be able to access aliases via this
// tslint:disable-next-line: space-before-function-paren
it('renders every Experiment name into the table', function () {
cy.visit('/');
cy.wait(['@mockNamespacesRequest', '@mockExperimentsRequest']);

let i = 0;
const experiments = this.experimentsRequest;
// Table is sorted by Name in ascending order by default
// and experiment objects are also sorted alphabetically by name
cy.get(`[data-cy-resource-table-row="Name"]`).each(element => {
expect(element).to.contain(experiments[i].name);
i++;
});
});

// tslint:disable-next-line: space-before-function-paren
it('renders properly Status icon for all experiments', function () {
cy.visit('/');
cy.wait(['@mockNamespacesRequest', '@mockExperimentsRequest']);

let i = 0;
const experiments = this.experimentsRequest;
cy.get('[data-cy-resource-table-row="Status"]').each(element => {
const status = parseStatus(experiments[i]);
if (status.phase === STATUS_TYPE.READY) {
cy.wrap(element)
.find('lib-status>mat-icon')
.should('contain', 'check_circle');
} else if (status.phase === STATUS_TYPE.STOPPED) {
cy.wrap(element)
.find('lib-status>lib-icon')
.should('have.attr', 'icon', 'custom:stoppedResource');
} else if (status.phase === STATUS_TYPE.UNAVAILABLE) {
cy.wrap(element)
.find('lib-status>mat-icon')
.should('contain', 'timelapse');
} else if (status.phase === STATUS_TYPE.WARNING) {
cy.wrap(element)
.find('lib-status>mat-icon')
.should('contain', 'warning');
} else if (
status.phase === STATUS_TYPE.WAITING ||
status.phase === STATUS_TYPE.TERMINATING
) {
cy.wrap(element).find('mat-spinner').should('exist');
}
i++;
});
});
});
Loading