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

jwa(front): Add UI tests with Cypress #6891

Merged
merged 3 commits into from
Jan 16, 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/workflows/jwa_frontend_tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,31 @@ jobs:
run: |
cd components/crud-web-apps/jupyter/frontend
npm run test:prod

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

- name: Install Kubeflow common library dependecies
run: |
cd components/crud-web-apps/common/frontend/kubeflow-common-lib
npm i
npm run build
npm link ./dist/kubeflow
- name: Install JWA dependencies
run: |
cd components/crud-web-apps/jupyter/frontend
npm i
npm link kubeflow
- name: Serve UI & run Cypress tests in Chrome and Firefox
run: |
cd components/crud-web-apps/jupyter/frontend
npm run serve & npx wait-on http://localhost:4200
npm run ui-test-ci-all
17 changes: 15 additions & 2 deletions components/crud-web-apps/jupyter/frontend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,22 @@ Run `ng build` to build the project. The build artifacts will be stored in the `

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

## Running end-to-end tests
## Running integration tests

Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
To run integration tests locally, make sure that node modules are installed and the frontend is serving the UI under `localhost:4200`. Then use `npm run e2e` to execute the integration tests via [Cypress](https://www.cypress.io/). This will open Cypress and there 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 e2e-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

Expand Down
12 changes: 0 additions & 12 deletions components/crud-web-apps/jupyter/frontend/angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -182,18 +182,6 @@
"options": {
"lintFilePatterns": ["src/**/*.ts", "src/**/*.html"]
}
},
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "frontend:serve"
},
"configurations": {
"production": {
"devServerTarget": "frontend:serve:production"
}
}
}
}
}
Expand Down
9 changes: 9 additions & 0 deletions components/crud-web-apps/jupyter/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',
},
});
5 changes: 0 additions & 5 deletions components/crud-web-apps/jupyter/frontend/cypress.json

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
describe('New notebook form', () => {
beforeEach(() => {});
beforeEach(() => {
cy.mockDashboardRequest();
cy.mockStorageClassesRequests();
cy.mockDefaultStorageClassRequest('rok');
cy.mockGpusRequest();
cy.mockConfigRequest();
cy.fixture('settings').then(settings => {
cy.mockNotebooksRequest(settings.namespace);
cy.mockPoddefaultsRequest(settings.namespace);
});
});

it('should have a "New notebook" title', () => {
cy.visit('/new');
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { STATUS_TYPE } from 'kubeflow';

describe('Main table', () => {
beforeEach(() => {
cy.mockNamespacesRequest();
cy.fixture('settings').then(settings => {
cy.mockNotebooksRequest(settings.namespace);
});
cy.fixture('notebooks').as('notebooksRequest');
});

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

it('should list Notebooks without errors', () => {
cy.visit('/');
// wait for the request to fetch notebooks and namespaces
cy.wait(['@mockNamespacesRequest', '@mockNotebooksRequest']);

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

it('should have a `Namespace` column, when showing all-namespaces', () => {
cy.visit('/');
cy.wait(['@mockNamespacesRequest', '@mockNotebooksRequest']);

cy.fixture('settings').then(settings => {
cy.mockNotebooksAllNamespacesRequest(settings.namespace);
});
cy.selectAllNamespaces();

cy.get('[data-cy-table-header-row="Namespace"]').should('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 Notebook name into the table', function () {
cy.visit('/');
cy.wait(['@mockNamespacesRequest', '@mockNotebooksRequest']);

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

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

let i = 0;
const notebooks = this.notebooksRequest.notebooks;
cy.get('[data-cy-resource-table-row="Status"]').each(element => {
if (notebooks[i].status.phase === STATUS_TYPE.READY) {
cy.wrap(element)
.find('lib-status>mat-icon')
.should('contain', 'check_circle');
} else if (notebooks[i].status.phase === STATUS_TYPE.STOPPED) {
cy.wrap(element)
.find('lib-status>lib-icon')
.should('have.attr', 'icon', 'custom:stoppedResource');
} else if (notebooks[i].status.phase === STATUS_TYPE.UNAVAILABLE) {
cy.wrap(element)
.find('lib-status>mat-icon')
.should('contain', 'timelapse');
} else if (notebooks[i].status.phase === STATUS_TYPE.WARNING) {
cy.wrap(element)
.find('lib-status>mat-icon')
.should('contain', 'warning');
} else if (
notebooks[i].status.phase === STATUS_TYPE.WAITING ||
notebooks[i].status.phase === STATUS_TYPE.TERMINATING
) {
cy.wrap(element).find('mat-spinner').should('exist');
}
i++;
});
});
});
108 changes: 108 additions & 0 deletions components/crud-web-apps/jupyter/frontend/cypress/fixtures/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
{
"config": {
"affinityConfig": {
"options": [],
"value": ""
},
"allowCustomImage": true,
"configurations": {
"readOnly": false,
"value": []
},
"cpu": {
"limitFactor": "none",
"readOnly": false,
"value": "0.5"
},
"dataVolumes": {
"readOnly": false,
"value": []
},
"environment": {
"readOnly": false,
"value": {}
},
"gpus": {
"readOnly": false,
"value": {
"num": "none",
"vendor": "",
"vendors": [
{
"limitsKey": "nvidia.com/gpu",
"uiName": "NVIDIA"
},
{
"limitsKey": "amd.com/gpu",
"uiName": "AMD"
}
]
}
},
"hideRegistry": true,
"hideTag": false,
"image": {
"options": [
"public.ecr.aws/j1r0q0g6/notebooks/notebook-servers/jupyter-scipy:master-6e4ad3b4",
"public.ecr.aws/j1r0q0g6/notebooks/notebook-servers/jupyter-pytorch-full:master-6e4ad3b4",
"public.ecr.aws/j1r0q0g6/notebooks/notebook-servers/jupyter-pytorch-cuda-full:master-6e4ad3b4",
"public.ecr.aws/j1r0q0g6/notebooks/notebook-servers/jupyter-tensorflow-full:master-6e4ad3b4",
"public.ecr.aws/j1r0q0g6/notebooks/notebook-servers/jupyter-tensorflow-cuda-full:master-6e4ad3b4"
],
"value": "public.ecr.aws/j1r0q0g6/notebooks/notebook-servers/jupyter-scipy:master-6e4ad3b4"
},
"imageGroupOne": {
"options": [
"public.ecr.aws/j1r0q0g6/notebooks/notebook-servers/codeserver-python:master-e9324d39"
],
"value": "public.ecr.aws/j1r0q0g6/notebooks/notebook-servers/codeserver-python:master-e9324d39"
},
"imageGroupTwo": {
"options": [
"public.ecr.aws/j1r0q0g6/notebooks/notebook-servers/rstudio-tidyverse:master-e9324d39"
],
"value": "public.ecr.aws/j1r0q0g6/notebooks/notebook-servers/rstudio-tidyverse:master-e9324d39"
},
"imagePullPolicy": {
"readOnly": false,
"value": "IfNotPresent"
},
"memory": {
"limitFactor": "none",
"readOnly": false,
"value": "1.0Gi"
},
"shm": {
"readOnly": false,
"value": true
},
"storageClass": "{none}",
"tolerationGroup": {
"options": [],
"readOnly": false,
"value": ""
},
"workspaceVolume": {
"readOnly": false,
"value": {
"mount": "/home/jovyan",
"newPvc": {
"metadata": {
"name": "{notebook-name}-workspace"
},
"spec": {
"accessModes": ["ReadWriteOnce"],
"resources": {
"requests": {
"storage": "5Gi"
}
}
}
}
}
}
},
"status": 200,
"success": true,
"user": null
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"namespaces": [
"auth",
"cert-manager",
"default",
"istio-system",
"knative-serving",
"kserve",
"kube-public",
"kube-system",
"kubeflow",
"kubeflow-user"
],
"status": 200,
"success": true,
"user": null
}

This file was deleted.