e2e: Add built UI to docker container rather than run a node process (#9063)

* e2e: Add built UI to docker container rather than run a node process

* Fix to ensure docker:local:stop tried to both clean and remove cypress container

* Check dist folder is correct

* Build U first

* Remove debug, wait for ui

* Change cluster test

* Tweak test

* Improve resilience of cluster manager delete tests

* Fix lint

* Try and fix delete tests

* Remove unused var

* Remove unused baseUrl

* Fix cluster delete tests

* Address lint issues

* Fix api keys test
This commit is contained in:
Neil MacDougall 2023-06-19 09:08:19 +01:00 committed by GitHub
parent a5b55ddbf4
commit 226ed30e39
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 121 additions and 28 deletions

View File

@ -36,4 +36,8 @@ export default class ComponentPo {
checkVisible(): Cypress.Chainable<boolean> {
return this.self().scrollIntoView().should('be.visible');
}
checkNotVisible(): Cypress.Chainable<boolean> {
return this.self().scrollIntoView().should('not.be.visible');
}
}

View File

@ -9,4 +9,8 @@ export default class CreateEditViewPo extends ComponentPo {
save() {
return new AsyncButtonPo(this.self().find('.cru-resource-footer .role-primary')).click();
}
saveAndWait() {
return new AsyncButtonPo(this.self().find('.cru-resource-footer .role-primary')).action('Save', 'Saved');
}
}

View File

@ -60,7 +60,7 @@ export default class SortableTablePo extends ComponentPo {
//
rowElements() {
return this.self().find('tbody tr');
return this.self().find('tbody tr:not(.sub-row)');
}
rowElementWithName(name: string) {
@ -75,6 +75,14 @@ export default class SortableTablePo extends ComponentPo {
return new ListRowPo(this.rowElementWithName(name));
}
rowNames() {
return this.rowElements().find('.cluster-link').then(($els: any) => {
return (
Cypress.$.makeArray($els).map((el: any) => el.innerText)
);
});
}
rowActionMenu() {
return new ActionMenuPo();
}

View File

@ -22,4 +22,8 @@ export default abstract class ClusterManagerCreateImportPagePo extends PagePo {
save() {
return this.resourceDetail().createEditView().save();
}
saveAndWait() {
return this.resourceDetail().createEditView().saveAndWait();
}
}

View File

@ -15,8 +15,6 @@ const runTimestamp = +new Date();
const runPrefix = `e2e-test-${ runTimestamp }`;
// File specific consts
const { baseUrl } = Cypress.config();
const clusterRequestBase = `${ baseUrl }/v1/provisioning.cattle.io.clusters/fleet-default`;
const clusterNamePartial = `${ runPrefix }-create`;
const rke2CustomName = `${ clusterNamePartial }-rke2-custom`;
const importGenericName = `${ clusterNamePartial }-import-generic`;
@ -55,8 +53,6 @@ describe('Cluster Manager', () => {
});
it('can edit cluster and see changes afterwards', () => {
cy.intercept('PUT', `${ clusterRequestBase }/${ rke2CustomName }`).as('saveRequest');
clusterList.goTo();
clusterList.list().actionMenu(rke2CustomName).getMenuItem('Edit Config').click();
@ -64,13 +60,13 @@ describe('Cluster Manager', () => {
editCreatedClusterPage.nameNsDescription().description().set(rke2CustomName);
editCreatedClusterPage.save();
cy.wait('@saveRequest').then(() => {
clusterList.goTo();
clusterList.list().actionMenu(rke2CustomName).getMenuItem('Edit Config').click();
// We should be taken back to the list page if the save was successful
clusterList.waitForPage();
editCreatedClusterPage.waitForPage('mode=edit', 'basic');
editCreatedClusterPage.nameNsDescription().description().self().should('have.value', rke2CustomName);
});
clusterList.list().actionMenu(rke2CustomName).getMenuItem('Edit Config').click();
editCreatedClusterPage.waitForPage('mode=edit', 'basic');
editCreatedClusterPage.nameNsDescription().description().self().should('have.value', rke2CustomName);
});
it('can view cluster YAML editor', () => {
@ -100,18 +96,19 @@ describe('Cluster Manager', () => {
});
it('can delete cluster', () => {
cy.intercept('DELETE', `${ clusterRequestBase }/${ rke2CustomName }`).as('deleteRequest');
clusterList.goTo();
clusterList.sortableTable().rowElementWithName(rke2CustomName).should('exist', { timeout: 15000 });
clusterList.list().actionMenu(rke2CustomName).getMenuItem('Delete').click();
const promptRemove = new PromptRemove();
clusterList.sortableTable().rowNames().then((rows: any) => {
const promptRemove = new PromptRemove();
promptRemove.confirm(rke2CustomName);
promptRemove.remove();
promptRemove.confirm(rke2CustomName);
promptRemove.remove();
cy.wait('@deleteRequest').then(() => {
return clusterList.sortableTable().rowElementWithName(rke2CustomName).should('not.exist', { timeout: 15000 });
clusterList.waitForPage();
clusterList.sortableTable().checkRowCount(false, rows.length - 1);
clusterList.sortableTable().rowNames().should('not.contain', rke2CustomName);
});
});
});
@ -146,20 +143,21 @@ describe('Cluster Manager', () => {
});
it('can delete cluster by bulk actions', () => {
cy.intercept('DELETE', `${ clusterRequestBase }/${ importGenericName }`).as('deleteRequest');
clusterList.goTo();
clusterList.sortableTable().rowElementWithName(importGenericName).should('exist', { timeout: 15000 });
clusterList.sortableTable().rowSelectCtlWithName(importGenericName).set();
clusterList.sortableTable().bulkActionDropDownOpen();
clusterList.sortableTable().bulkActionDropDownButton('Delete').click();
const promptRemove = new PromptRemove();
clusterList.sortableTable().rowNames().then((rows: any) => {
const promptRemove = new PromptRemove();
promptRemove.confirm(importGenericName);
promptRemove.remove();
promptRemove.confirm(importGenericName);
promptRemove.remove();
cy.wait('@deleteRequest').then(() => {
return clusterList.sortableTable().rowElementWithName(importGenericName).should('not.exist', { timeout: 15000 });
clusterList.waitForPage();
clusterList.sortableTable().checkRowCount(false, rows.length - 1);
clusterList.sortableTable().rowNames().should('not.contain', importGenericName);
});
});
});

View File

@ -28,7 +28,7 @@
"mem-dev": "bash -c 'source ./scripts/version && NODE_ENV=dev node --max-old-space-size=8192 ./node_modules/.bin/vue-cli-service serve'",
"docker-dev": "docker run --rm --name dashboard-dev -p 8005:8005 -e API=$API -v $(pwd):/src -v dashboard_node:/src/node_modules rancher/dashboard:dev",
"docker:local:start": "docker run -d --restart=unless-stopped -p 80:80 -p 443:443 -e CATTLE_BOOTSTRAP_PASSWORD=password -e CATTLE_PASSWORD_MIN_LENGTH=3 --name cypress --privileged rancher/rancher:v2.7-head",
"docker:local:stop": "docker kill cypress || docker rm cypress || true",
"docker:local:stop": "docker kill cypress || true && docker rm cypress || true",
"build": "NODE_OPTIONS=--max_old_space_size=4096 ./node_modules/.bin/vue-cli-service build",
"build:lib": "cd pkg/rancher-components && yarn build:lib",
"analyze": "./node_modules/.bin/vue-cli-service build --report",
@ -43,8 +43,8 @@
"cy:run:sorry": "./scripts/e2e",
"e2e:pre-dev": "yarn docker:local:stop && yarn docker:local:start && NODE_ENV=dev TEST_INSTRUMENT=true yarn build",
"e2e:dev": "START_SERVER_AND_TEST_INSECURE=1 server-test start:dev https-get://localhost:8005 cy:run:sorry",
"e2e:pre-prod": "yarn docker:local:stop && yarn docker:local:start && DEV_PORTS=true TEST_INSTRUMENT=true yarn build",
"e2e:prod": "START_SERVER_AND_TEST_INSECURE=1 server-test start:prod https-get://localhost:8005 cy:run:sorry",
"e2e:pre-prod": "yarn docker:local:stop && mkdir dist && TEST_INSTRUMENT=true ./scripts/build-e2e && ./scripts/e2e-docker-start ",
"e2e:prod": "TEST_BASE_URL=https://127.0.0.1/dashboard yarn cy:run:sorry && yarn docker:local:stop",
"coverage": "npx nyc merge coverage coverage/coverage.json",
"storybook": "cd storybook && yarn install && yarn storybook",
"build-storybook": "cd storybook && yarn install && NODE_OPTIONS=--max_old_space_size=4096 yarn build-storybook --quiet",

30
scripts/build-e2e Executable file
View File

@ -0,0 +1,30 @@
#!/bin/bash
set -e
set -x
# Builds a production version of dashboard directly in the dist folder with the
# base configured to be /dashboard so that this can be mapped or copied into a docker container
# deploymet of Rancher
# This is currently used by the e2e tests to build a version of dashboard and map this into
# the docker container to avoid having to run a separate node process to serve up the dashboard static assets
BUILD_DEBUG="${BUILD_DEBUG:-}"
if [[ -n "${BUILD_DEBUG}" ]]; then
set -x
env
fi
cd $(dirname $0)/..
echo "Building production build for e2e ..."
yarn --pure-lockfile install
source scripts/version
echo "BRANCH: ${COMMIT_BRANCH:-<none>}"
echo "TAG: ${GIT_TAG:-<none>}"
OUTPUT_DIR=dist
echo "Building..."
COMMIT=${COMMIT} VERSION=${VERSION} OUTPUT_DIR=$OUTPUT_DIR ROUTER_BASE='/dashboard/' RESOURCE_BASE='/dashboard/' yarn run build

45
scripts/e2e-docker-start Executable file
View File

@ -0,0 +1,45 @@
#!/usr/bin/env bash
DIR=$(cd $(dirname $0)/..; pwd)
DIST=${DIR}/dist
docker run -d --restart=unless-stopped -p 80:80 -p 443:443 \
-v ${DIST}:/usr/share/rancher/ui-dashboard/dashboard \
-e CATTLE_UI_OFFLINE_PREFERRED=true \
-e CATTLE_BOOTSTRAP_PASSWORD=password \
-e CATTLE_PASSWORD_MIN_LENGTH=3 \
--name cypress \
--privileged \
rancher/rancher:v2.7-head
docker ps
echo "Waiting for dashboard UI to be reachable (initial 20s wait) ..."
sleep 20
echo "Waiting for dashboard UI to be reachable ..."
okay=0
while [ $okay -lt 20 ]; do
STATUS=$(curl --silent --head -k https://127.0.0.1/dashboard/ | awk '/^HTTP/{print $2}')
echo "Status: $STATUS (Try: $okay)"
okay=$((okay+1))
if [ "$STATUS" == "200" ]; then
okay=100
else
sleep 5
fi
done
if [ "$STATUS" != "200" ]; then
echo "Dashboard did not become available in a reasonable time"
exit 1
fi
echo "Dashboard UI is ready"