Skip to content

Commit

Permalink
Merge pull request #6521 from dtaylor113/move-login-tests-to-cypress
Browse files Browse the repository at this point in the history
Bug 1872469: Port login tests from protractor to Cypress
  • Loading branch information
openshift-merge-robot committed Sep 12, 2020
2 parents aa4d040 + 52986b6 commit 8e96822
Show file tree
Hide file tree
Showing 15 changed files with 160 additions and 92 deletions.
80 changes: 1 addition & 79 deletions frontend/integration-tests/tests/login.scenario.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,12 @@ import { $, browser, ExpectedConditions as until } from 'protractor';

import { appHost } from '../protractor.conf';
import * as loginView from '../views/login.view';
import * as sidenavView from '../views/sidenav.view';
import * as clusterSettingsView from '../views/cluster-settings.view';
import * as overview from '../views/overview.view';

const JASMINE_DEFAULT_TIMEOUT_INTERVAL = jasmine.DEFAULT_TIMEOUT_INTERVAL;
const JASMINE_EXTENDED_TIMEOUT_INTERVAL = 1000 * 60 * 3;
const KUBEADMIN_IDP = 'kube:admin';
const KUBEADMIN_USERNAME = 'kubeadmin';
const {
BRIDGE_HTPASSWD_IDP = 'test',
BRIDGE_HTPASSWD_USERNAME = 'test',
BRIDGE_HTPASSWD_PASSWORD = 'test',
BRIDGE_KUBEADMIN_PASSWORD,
} = process.env;
const { BRIDGE_KUBEADMIN_PASSWORD } = process.env;

describe('Auth test', () => {
beforeAll(async () => {
Expand All @@ -34,56 +26,6 @@ describe('Auth test', () => {
jasmine.DEFAULT_TIMEOUT_INTERVAL = JASMINE_DEFAULT_TIMEOUT_INTERVAL;
});

it('logs in via htpasswd identity provider', async () => {
await loginView.login(
BRIDGE_HTPASSWD_IDP,
BRIDGE_HTPASSWD_USERNAME,
BRIDGE_HTPASSWD_PASSWORD,
);
expect(browser.getCurrentUrl()).toContain(appHost);
expect(loginView.userDropdown.getText()).toContain(BRIDGE_HTPASSWD_USERNAME);
});

it('switches from dev to admin perspective', async () => {
await overview.closeGuidedTour();
expect(sidenavView.switcher.getText()).toContain('Developer');
await sidenavView.switchPerspective(sidenavView.Perspective.Administrator);
expect(sidenavView.switcher.getText()).toContain('Administrator');
});

it('does not show admin nav items in Administration to htpasswd user', async () => {
// Let flags resolve before checking for the presence of nav items.
await browser.sleep(5000);
await browser.wait(until.visibilityOf(sidenavView.navSectionFor('Administration')));
expect(sidenavView.navSectionFor('Administration')).not.toContain('Cluster Status');
expect(sidenavView.navSectionFor('Administration')).not.toContain('Cluster Settings');
expect(sidenavView.navSectionFor('Administration')).not.toContain('Namespaces');
expect(sidenavView.navSectionFor('Administration')).not.toContain(
'Custom Resource Definitions',
);
});

it('does not show admin nav items in Operators to htpasswd user', async () => {
await browser.wait(until.visibilityOf(sidenavView.navSectionFor('Operators')));
expect(sidenavView.navSectionFor('Operators')).not.toContain('OperatorHub');
});

it('does not show admin nav items in Storage to htpasswd user', async () => {
await browser.wait(until.visibilityOf(sidenavView.navSectionFor('Storage')));
expect(sidenavView.navSectionFor('Storage')).not.toContain('Persistent Volumes');
});

it('does not show Compute or Monitoring admin nav items to htpasswd user', async () => {
expect(sidenavView.navSectionFor('Compute').isPresent()).toBe(false);
expect(sidenavView.navSectionFor('Monitoring').isPresent()).toBe(false);
});

it('logs out htpasswd user', async () => {
await loginView.logout();
expect(browser.getCurrentUrl()).toContain('oauth-openshift');
expect(until.or(loginView.pf3Login, loginView.pf4Login)).toBeTruthy();
});

it('logs in as kubeadmin user', async () => {
await loginView.login(KUBEADMIN_IDP, KUBEADMIN_USERNAME, BRIDGE_KUBEADMIN_PASSWORD);
expect(browser.getCurrentUrl()).toContain(appHost);
Expand All @@ -93,25 +35,5 @@ describe('Auth test', () => {
'You are logged in as a temporary administrative user. Update the cluster OAuth configuration to allow others to log in.',
);
});

it('logs out kubeadmin user', async () => {
await loginView.logout();
expect(browser.getCurrentUrl()).toContain('oauth-openshift');
expect(until.or(loginView.pf3Login, loginView.pf4Login)).toBeTruthy();

// Log back in so that remaining tests can be run
await loginView.login(KUBEADMIN_IDP, KUBEADMIN_USERNAME, BRIDGE_KUBEADMIN_PASSWORD);
expect(loginView.userDropdown.getText()).toContain('kube:admin');
});
});

it('is authenticated as cluster admin user', async () => {
expect(await browser.getCurrentUrl()).toContain(appHost);
await browser.wait(until.visibilityOf(sidenavView.navSectionFor('Compute')));
await browser.wait(until.visibilityOf(sidenavView.navSectionFor('Operators')));
await browser.wait(until.visibilityOf(sidenavView.navSectionFor('Administration')));
await sidenavView.clickNavLink(['Administration', 'Cluster Settings']);
await clusterSettingsView.isLoaded();
expect(clusterSettingsView.heading.isDisplayed()).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ const TourStepComponent: React.FC<TourStepComponentProps> = ({
footer={footer}
onClose={handleClose}
id="guided-tour-modal"
data-test="guided-tour-modal"
aria-label={`guided tour ${step}`}
isFullScreen
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,22 @@ const StepFooter: React.FC<StepFooterProps> = ({
<Flex>
{children && <FlexItem>{children}</FlexItem>}
<FlexItem align={{ default: 'alignRight' }}>
<Button variant="secondary" id="tour-step-footer-secondary" onClick={secondaryButtonCallback}>
<Button
variant="secondary"
id="tour-step-footer-secondary"
data-test="tour-step-footer-secondary"
onClick={secondaryButtonCallback}
>
{secondaryButton}
</Button>
</FlexItem>
<FlexItem>
<Button variant="primary" id="tour-step-footer-primary" onClick={primaryButtonCallback}>
<Button
variant="primary"
id="tour-step-footer-primary"
data-test="tour-step-footer-primary"
onClick={primaryButtonCallback}
>
{primaryButton}
</Button>
</FlexItem>
Expand Down
3 changes: 3 additions & 0 deletions frontend/packages/integration-tests-cypress/plugins/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ module.exports = (on, config) => {
config.baseUrl = `${process.env.BRIDGE_BASE_ADDRESS || 'http://localhost:9000'}${(
process.env.BRIDGE_BASE_PATH || '/'
).replace(/\/$/, '')}`;
config.env.BRIDGE_HTPASSWD_IDP = process.env.BRIDGE_HTPASSWD_IDP;
config.env.BRIDGE_HTPASSWD_USERNAME = process.env.BRIDGE_HTPASSWD_USERNAME;
config.env.BRIDGE_HTPASSWD_PASSWORD = process.env.BRIDGE_HTPASSWD_PASSWORD;
config.env.BRIDGE_KUBEADMIN_PASSWORD = process.env.BRIDGE_KUBEADMIN_PASSWORD;
return config;
};
3 changes: 2 additions & 1 deletion frontend/packages/integration-tests-cypress/support/login.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { submitButton } from '../views/form';
import { masthead } from '../views/masthead';

declare global {
namespace Cypress {
Expand Down Expand Up @@ -32,7 +33,7 @@ Cypress.Commands.add('login', (provider: string, username: string, password: str
cy.get('#inputUsername').type(username || KUBEADMIN_USERNAME);
cy.get('#inputPassword').type(password || Cypress.env('BRIDGE_KUBEADMIN_PASSWORD'));
cy.get(submitButton).click();
cy.byTestID('user-dropdown').should('be.visible');
masthead.username.shouldBeVisible();
});

Cypress.Commands.add('logout', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { checkErrors } from '../../support';
import { guidedTour } from '../../views/guided-tour';
import { nav } from '../../views/nav';
import { masthead } from '../../views/masthead';

describe('Auth test', () => {
const KUBEADMIN_IDP = 'kube:admin';
const KUBEADMIN_USERNAME = 'kubeadmin';

afterEach(() => {
checkErrors();
cy.logout();
});

if (Cypress.env('BRIDGE_KUBEADMIN_PASSWORD')) {
it(`logs in as 'test' user via htpasswd identity provider`, () => {
const idp = Cypress.env('BRIDGE_HTPASSWD_IDP') || 'test';
const username = Cypress.env('BRIDGE_HTPASSWD_USERNAME') || 'test';
const passwd = Cypress.env('BRIDGE_HTPASSWD_PASSWORD') || 'test';
cy.login(idp, username, passwd);
cy.url().should('include', Cypress.config('baseUrl'));
guidedTour.close();
masthead.username.shouldHaveText(username);

cy.log('switches from dev to admin perspective');
nav.sidenav.switcher.shouldHaveText('Developer');
nav.sidenav.switcher.changePerspectiveTo('Administrator');
nav.sidenav.switcher.shouldHaveText('Administrator');

cy.log('does not show admin nav items in Administration to test user');
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(10000); // wait for feature FLAGS to load
nav.sidenav.shouldNotHaveNavSection(['Administration', 'Cluster Status']);
nav.sidenav.shouldNotHaveNavSection(['Administration', 'Cluster Settings']);
nav.sidenav.shouldNotHaveNavSection(['Administration', 'Namespaces']);
nav.sidenav.shouldNotHaveNavSection(['Administration', 'Custom Resource Definitions']);

cy.log('does not show admin nav items in Operators to test user');
nav.sidenav.shouldNotHaveNavSection(['Operators', 'OperatorHub']);

cy.log('does not show admin nav items in Storage to test user');
nav.sidenav.shouldNotHaveNavSection(['Storage', 'Persistent Volumes']);

cy.log('does not show Compute or Monitoring to test user');
nav.sidenav.shouldNotHaveNavSection(['Compute']);
nav.sidenav.shouldNotHaveNavSection(['Monitoring']);
});
}

it(`log in as 'kubeadmin' user`, () => {
cy.login(KUBEADMIN_IDP, KUBEADMIN_USERNAME, Cypress.env('BRIDGE_KUBEADMIN_PASSWORD'));
cy.visit('');
cy.url().should('include', Cypress.config('baseUrl'));
masthead.username.shouldHaveText(KUBEADMIN_IDP);
cy.byTestID('global-notifications').contains(
'You are logged in as a temporary administrative user. Update the cluster OAuth configuration to allow others to log in.',
);

cy.log('verify sidenav menus and Administration menu access for cluster admin user');
nav.sidenav.shouldHaveNavSection(['Compute']);
nav.sidenav.shouldHaveNavSection(['Operators']);
nav.sidenav.clickNavLink(['Administration', 'Cluster Settings']);
cy.byLegacyTestID('cluster-settings-page-heading').should('be.visible');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { submitButton, errorMessage } from '../../views/form';
import { listPage } from '../../views/list-page';
import { detailsPage } from '../../views/details-page';
import { modal } from '../../views/modal';
import { nav } from '../../views/nav';

const shouldBeWatchdogAlertDetailsPage = () => {
cy.byTestID('resource-title').contains('Watchdog');
Expand Down Expand Up @@ -39,7 +40,7 @@ describe('Monitoring: Alerts', () => {

it('displays and filters the Alerts list page, links to detail pages', () => {
cy.log('use vert. nav. menu to goto Monitoring -> Alerting');
cy.clickNavLink(['Monitoring', 'Alerting']);
nav.sidenav.clickNavLink(['Monitoring', 'Alerting']);
// TODO, switch to 'listPage.titleShouldHaveText('Alerting');', when we switch to new test id
cy.byLegacyTestID('resource-title').should('have.text', 'Alerting');
listPage.projectDropdownShouldNotExist();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { listPage } from '../../views/list-page';
import { PVC, testerDeployment, SnapshotClass, patchForVolume } from '../../mocks/snapshot';
import { detailsPage } from '../../views/details-page';
import { modal } from '../../views/modal';
import { nav } from '../../views/nav';
import { resourceStatusShouldContain } from '../../views/common';

const snapshotName = `${PVC.metadata.name}-snapshot`;
Expand All @@ -17,7 +18,7 @@ if (Cypress.env('BRIDGE_AWS')) {
cy.exec(`echo '${JSON.stringify(PVC)}' | oc apply -n ${testName} -f -`);
cy.exec(`echo '${JSON.stringify(testerDeployment)}' | oc apply -n ${testName} -f -`);
cy.exec(`echo '${JSON.stringify(SnapshotClass)}' | oc apply -f -`);
cy.clickNavLink(['Storage', 'Persistent Volume Claims']);
nav.sidenav.clickNavLink(['Storage', 'Persistent Volume Claims']);
listPage.filter.byName(PVC.metadata.name);
resourceStatusShouldContain('Bound');
});
Expand All @@ -36,7 +37,7 @@ if (Cypress.env('BRIDGE_AWS')) {
});

it('Creates Snapshot', () => {
cy.clickNavLink(['Volume Snapshots']);
nav.sidenav.clickNavLink(['Volume Snapshots']);
listPage.clickCreateYAMLbutton();
cy.byTestID('pvc-dropdown').click();
cy.get(dropdownFirstItem)
Expand Down Expand Up @@ -70,7 +71,7 @@ if (Cypress.env('BRIDGE_AWS')) {
});

it('Lists Snapshot', () => {
cy.clickNavLink(['Volume Snapshots']);
nav.sidenav.clickNavLink(['Volume Snapshots']);
listPage.rows.shouldBeLoaded();
listPage.rows.shouldExist(snapshotName);
listPage.rows.shouldNotExist(`${snapshotName}dup`);
Expand Down
11 changes: 11 additions & 0 deletions frontend/packages/integration-tests-cypress/views/guided-tour.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export const guidedTour = {
close: () => {
cy.get('body').then(($body) => {
if ($body.find(`[data-test="guided-tour-modal"]`).length) {
cy.byTestID('tour-step-footer-secondary')
.contains('Skip tour')
.click();
}
});
},
};
12 changes: 12 additions & 0 deletions frontend/packages/integration-tests-cypress/views/masthead.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export const masthead = {
username: {
shouldBeVisible: () =>
cy
.byTestID(Cypress.env('BRIDGE_KUBEADMIN_PASSWORD') ? 'user-dropdown' : 'username')
.should('be.visible'),
shouldHaveText: (text: string) =>
cy
.byTestID(Cypress.env('BRIDGE_KUBEADMIN_PASSWORD') ? 'user-dropdown' : 'username')
.should('have.text', text),
},
};
28 changes: 28 additions & 0 deletions frontend/packages/integration-tests-cypress/views/nav.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
export const nav = {
sidenav: {
switcher: {
shouldHaveText: (text: string) =>
cy.byLegacyTestID('perspective-switcher-toggle').contains(text),
changePerspectiveTo: (newPerspective: string) =>
cy
.byLegacyTestID('perspective-switcher-toggle')
.click()
.byLegacyTestID('perspective-switcher-menu-option')
.contains(newPerspective)
.click(),
},
shouldHaveNavSection: (path: string[]) => {
cy.get('#page-sidebar').contains(path[0]);
if (path.length === 2) {
cy.get('#page-sidebar').contains(path[1]);
}
},
shouldNotHaveNavSection: (path: string[]) => {
cy.get('#page-sidebar').should('not.have.text', path[0]);
if (path.length === 2) {
cy.get('#page-sidebar').should('not.have.text', path[1]);
}
},
clickNavLink: (path: string[]) => cy.clickNavLink(path),
},
};
2 changes: 1 addition & 1 deletion frontend/public/components/global-notifications.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ImpersonateNotifier } from './impersonate-notifier';
import { KubeAdminNotifier } from './kube-admin-notifier';

export const GlobalNotifications = () => (
<div className="co-global-notifications">
<div className="co-global-notifications" data-test="global-notifications">
<KubeAdminNotifier />
<ImpersonateNotifier />
</div>
Expand Down
6 changes: 5 additions & 1 deletion frontend/public/components/masthead-toolbar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,11 @@ class MastheadToolbarContents_ extends React.Component {
}

if (_.isEmpty(actions)) {
return <div className="co-username">{username}</div>;
return (
<div data-test="username" className="co-username">
{username}
</div>
);
}

const userToggle = (
Expand Down
6 changes: 5 additions & 1 deletion test-cypress.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,8 @@ function generateReport {
}
trap generateReport EXIT

yarn run test-cypress-headless
if [ $# -gt 0 ] && [ -n "$1" ]; then
yarn run test-cypress-headless --spec "$1"
else
yarn run test-cypress-headless
fi
11 changes: 8 additions & 3 deletions test-prow-e2e.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,14 @@ oc patch oauths cluster --patch "$(cat ./frontend/integration-tests/data/patch-h
DBUS_SESSION_BUS_ADDRESS=/dev/null
export DBUS_SESSION_BUS_ADDRESS

CHROME_VERSION=$(google-chrome --version) ./test-gui.sh "${1:-e2e}"
SCENARIO="${1:-e2e}"

# Only run Cypress tests if no agruments passed, or if the 'release' argument was passed
if [ $# -eq 0 ] || [ "$1" == "release" ]; then
if [ "$SCENARIO" != "login" ]; then
CHROME_VERSION=$(google-chrome --version) ./test-gui.sh "$SCENARIO"
fi

if [ "$SCENARIO" == "e2e" ] || [ "$SCENARIO" == "release" ]; then
./test-cypress.sh
elif [ "$SCENARIO" == "login" ]; then
./test-cypress.sh 'tests/app/auth-multiuser-login.spec.ts'
fi

0 comments on commit 8e96822

Please sign in to comment.