Skip to content

Commit

Permalink
Added tests for bucket class creation flow
Browse files Browse the repository at this point in the history
  • Loading branch information
bipuladh committed Feb 25, 2020
1 parent ba3408b commit 45625d4
Show file tree
Hide file tree
Showing 6 changed files with 265 additions and 9 deletions.
@@ -0,0 +1,91 @@
import { execSync } from 'child_process';
import { $, browser, ExpectedConditions as until } from 'protractor';
import { goToInstalledOperators } from '@console/ceph-storage-plugin/integration-tests/views/add-capacity.view';
import * as crudView from '@console/internal-integration-tests/views/crud.view';
import { BucketClassHandler, createBucketClassLink, Tier } from '../views/createBC.view';
import { click } from '@console/shared/src/test-utils/utils';
import { appHost, testName } from '@console/internal-integration-tests/protractor.conf';
import { NS } from '@console/ceph-storage-plugin/integration-tests/utils/consts';
import { ocsOperator } from '../views/createBS.view';

const bcName = `${testName}-bucketclass`;
const description = `${testName}-bucketClass is a bucket class being used for testing purposes. Please do not use it for real storage purposes in case the test fails and the class is not deleted`;

const getBucketClassObject = () =>
JSON.parse(execSync(`kubectl get bucketclass ${bcName} -n openshift-storage -o json`).toString());

describe('Tests creation of Bucket Class', () => {
const bcHandler = new BucketClassHandler(bcName, description);
beforeAll(async () => {
await browser.get(`${appHost}/ns/${NS}`);
});

beforeEach(async () => {
await goToInstalledOperators();
await browser.wait(until.and(crudView.untilNoLoadersPresent));
await browser.wait(until.visibilityOf(ocsOperator));
await ocsOperator.click();
await browser.wait(until.visibilityOf(createBucketClassLink));
await click(createBucketClassLink);
});

afterEach(async () => {
await crudView.clickDetailsPageAction('Delete Bucket Class');
await browser.wait(until.presenceOf($('#confirm-action')));
await $('#confirm-action').click();
await browser.sleep(100);
});

it('Create a 1 Tier(Spread) Bucket Class', async () => {
bcHandler.setTiers([Tier.SPREAD]);
const bc = await bcHandler.createBC();
expect(bc.name).toEqual(bcName);
expect(bc.namespace).toContain(NS);
expect(bc.tier1Policy.toLocaleUpperCase()).toContain(Tier.SPREAD);
const cliData = getBucketClassObject();
expect(cliData?.metadata?.name).toEqual(bc.name);
});

it('Create a 1 Tier(Mirror) Bucket Class', async () => {
bcHandler.setTiers([Tier.MIRROR]);
const bc = await bcHandler.createBC();
expect(bc.name).toEqual(bcName);
expect(bc.namespace).toContain(NS);
expect(bc.tier1Policy.toLocaleUpperCase()).toContain(Tier.MIRROR);
const cliData = getBucketClassObject();
expect(cliData?.metadata?.name).toEqual(bc.name);
});

it('Create a 2 Tier(Spread, Spread) Bucket Class', async () => {
bcHandler.setTiers([Tier.SPREAD, Tier.SPREAD]);
const bc = await bcHandler.createBC();
expect(bc.name).toEqual(bcName);
expect(bc.namespace).toContain(NS);
expect(bc.tier1Policy.toLocaleUpperCase()).toContain(Tier.SPREAD);
expect(bc.tier2Policy.toLocaleUpperCase()).toContain(Tier.SPREAD);
const cliData = getBucketClassObject();
expect(cliData?.metadata?.name).toEqual(bcName);
});

it('Create a 2 Tier(Spread, Mirror) Bucket Class', async () => {
bcHandler.setTiers([Tier.SPREAD, Tier.MIRROR]);
const bc = await bcHandler.createBC();
expect(bc.name).toEqual(bcName);
expect(bc.namespace).toContain(NS);
expect(bc.tier1Policy.toLocaleUpperCase()).toContain(Tier.SPREAD);
expect(bc.tier2Policy.toLocaleUpperCase()).toContain(Tier.MIRROR);
const cliData = getBucketClassObject();
expect(cliData?.metadata?.name).toEqual(bcName);
});

it('Create a 2 Tier(Mirror, Mirror) Bucket Class', async () => {
bcHandler.setTiers([Tier.MIRROR, Tier.MIRROR]);
const bc = await bcHandler.createBC();
expect(bc.name).toEqual(bcName);
expect(bc.namespace).toContain(NS);
expect(bc.tier1Policy.toLocaleUpperCase()).toContain(Tier.MIRROR);
expect(bc.tier2Policy.toLocaleUpperCase()).toContain(Tier.MIRROR);
const cliData = getBucketClassObject();
expect(cliData?.metadata?.name).toEqual(bcName);
});
});
@@ -0,0 +1,160 @@
import { execSync } from 'child_process';
import * as _ from 'lodash';
import { $, $$, browser, by, element, ExpectedConditions as until } from 'protractor';
import * as crudView from '@console/internal-integration-tests/views/crud.view';
import { click } from '@console/shared/src/test-utils/utils';
import { testName } from '@console/internal-integration-tests/protractor.conf';

// Backing Store is the third one in API details page
export const createBucketClassLink = $('article:nth-child(3) a');
const tier1TableCheckBoxes = $$('table[aria-label="Select Backing Store for Tier 1"] input');
const tier2TableCheckBoxes = $$('table[aria-label="Select Backing Store for Tier 2"] input');

// Wizard Controls
const nextButton = element(by.buttonText('Next'));
const createBucketClassButton = element(by.buttonText('Create Bucket Class'));
const bucketClassNameInput = $('input[aria-label="Bucket Class Name"]');
const bucketClassDescriptionInput = $('textarea[aria-label="Description of bucket class"]');

// Tier Buttons
const tier1Spread = $('input#radio-1');
const tier1Mirror = $('input#radio-2');
const tier2Spread = $('input#radio-3');
const tier2Mirror = $('input#radio-4');
const addTierButton = $('button[data-testid="add-tier-btn"]');

// Review Page Data
const bcName = $('p[data-testid="bc-name"]');
const bcNamespace = $('p[data-testid="namespace-value"]');
const bcDescription = $('p[data-testid="bc-desc"]');
const tier1Policy = $('h5[data-testid="tier1-policy"]');
const tier2Policy = $('h5[data-testid="tier2-policy"]');

export enum Tier {
SPREAD = 'SPREAD',
MIRROR = 'MIRROR',
}

const TierCountMap = Object.freeze({
[Tier.SPREAD]: 1,
[Tier.MIRROR]: 2,
});

const getBackingStoreRequiredCount = (tiers: Tier[]) => {
let count: number = 0;
count += TierCountMap[tiers[0]];
if (tiers.length > 1) count += TierCountMap[tiers[1]];
return count;
};

const createDummyBackingStore = (name: string) => {
const defaultBS = JSON.parse(
execSync(
'kubectl get backingstores.noobaa.io noobaa-default-backing-store -n openshift-storage -o json',
).toString(),
);
const customObj = _.pick(defaultBS, ['apiVersion', 'kind', 'metadata', 'spec']);
customObj.metadata = Object.assign(_.pick(customObj.metadata, ['name', 'namespace']), {
name,
});
execSync(`echo '${JSON.stringify(customObj)}' | kubectl create -f -`);
};

const tierLevelToButton = (level: number, tier: Tier) =>
level === 1
? tier === Tier.SPREAD
? tier1Spread
: tier1Mirror
: tier === Tier.SPREAD
? tier2Spread
: tier2Mirror;

export class BucketClassHandler {
name: string;

namespace: string;

description: string;

tiers: Tier[];

constructor(name: string, description: string, namespace: string = 'openshift-storage') {
this.name = name;
this.namespace = namespace;
this.description = description;
// Create 3 backingStores similar to default backing store
for (let count = 0; count < 3; count++) {
createDummyBackingStore(`${testName}-${count}`);
}
}

setTiers(tiers: Tier[]) {
this.tiers = tiers;
}

async createBC() {
// Select namespace
await browser.wait(until.and(crudView.untilNoLoadersPresent));
// General Data Page
await this.setGeneralData();
await click(nextButton);
// Placement Policy Page
await browser.wait(until.and(crudView.untilNoLoadersPresent));
await this.setPlacementPolicy();
await click(nextButton);
// Backing Store Selection Page
await browser.wait(until.and(crudView.untilNoLoadersPresent));
await this.setBackingStores();
await click(nextButton);
// Review Page
await browser.wait(until.and(crudView.untilNoLoadersPresent));
const data = await this.getReviewPageData();
await click(createBucketClassButton);
return data;
}

async setGeneralData() {
await bucketClassNameInput.sendKeys(this.name);
await bucketClassDescriptionInput.sendKeys(this.name);
}

async setPlacementPolicy() {
await click(tierLevelToButton(1, this.tiers[0]));
if (this.tiers.length > 1) {
await click(addTierButton);
await click(tierLevelToButton(2, this.tiers[1]));
}
}

async setBackingStores() {
// Wait until the table gets populated
await browser.wait(
until.and(
async () =>
getBackingStoreRequiredCount(this.tiers) <= (await tier1TableCheckBoxes.count()),
),
);
// Select tier 1 Backing Stores
await click(tier1TableCheckBoxes.get(0));
if (TierCountMap[this.tiers[0]] > 1) await click(tier1TableCheckBoxes.get(1));
// Select tier 2 Backing Stores
if (this.tiers.length > 1) {
await click(tier2TableCheckBoxes.get(0));
if (TierCountMap[this.tiers[1]] > 1) await click(tier2TableCheckBoxes.get(1));
}
}

async getReviewPageData() {
const dump = {
name: await bcName.getText(),
namespace: await bcNamespace.getText(),
description: await bcDescription.getText(),
tier1Policy: await tier1Policy.getText(),
tier2Policy: null,
};
if (this.tiers.length > 1) {
dump.tier2Policy = await tier2Policy.getText();
}
return dump;
}
}
4 changes: 3 additions & 1 deletion frontend/packages/noobaa-storage-plugin/package.json
Expand Up @@ -11,7 +11,9 @@
"consolePlugin": {
"entry": "src/plugin.ts",
"integrationTestSuites": {
"noobaa": ["integration-tests/**/*.scenario.ts"]
"noobaa": [
"integration-tests/**/*.scenario.ts"
]
}
}
}
Expand Up @@ -45,7 +45,7 @@ const GeneralPage: React.FC<GeneralPageProps> = ({ dispatch, state }) => {
isRequired
fieldId="namespace-dropdown"
>
<NsDropdown onChange={onChange} selectedKey={state.namespace} />
<NsDropdown onChange={onChange} selectedKey={state.namespace} id="ns-dropdown" />
</FormGroup>
<FormGroup
isRequired
Expand Down
Expand Up @@ -79,6 +79,7 @@ const PlacementPolicyPage: React.FC<PlacementPolicyPageProps> = ({ dispatch, sta
icon={<PlusCircleIcon />}
onClick={() => dispatch({ type: 'setPlacementPolicyTier2', value: 'Spread' })}
isInline
data-testid="add-tier-btn"
>
Add Tier
</Button>
Expand Down
Expand Up @@ -24,38 +24,40 @@ const ReviewPage: React.FC<ReviewPageProps> = ({ state }) => {
<Title size="lg" headingLevel="h4" className="nb-create-bc-step-page-review__item-header">
Namespace
</Title>
<p>{<ResourceLink kind={ProjectModel.kind} name={namespace} linkTo={false} />}</p>
<p data-testid="namespace-value">
{<ResourceLink kind={ProjectModel.kind} name={namespace} linkTo={false} />}
</p>
</div>
<div className="nb-create-bc-step-page-review__item">
<Title size="lg" headingLevel="h4" className="nb-create-bc-step-page-review__item-header">
Bucket Class name
</Title>
<p>{bucketClassName}</p>
<p data-testid="bc-name">{bucketClassName}</p>
</div>
{description && (
<div className="nb-create-bc-step-page-review__item">
<Title size="lg" headingLevel="h4" className="nb-create-bc-step-page-review__item-header">
Description
</Title>
<p>{description}</p>
<p data-testid="bc-desc">{description}</p>
</div>
)}
<div className="nb-create-bc-step-page-review__item">
<Title size="lg" headingLevel="h4" className="nb-create-bc-step-page-review__item-header">
Placement Policy Details
</Title>
<div className="co-nobaa-create-bc-step-page-review__item-tier1">
<Title size="md" headingLevel="h5">
<Title size="md" headingLevel="h5" data-testid="tier1-policy">
Tier 1: {tier1Policy}
</Title>
<p>Selected Backing Store: {tier1BackingStore.join(', ')}</p>
<p data-testid="tier1-stores">Selected Backing Store: {tier1BackingStore.join(', ')}</p>
</div>
{tier2Policy && (
<>
<Title size="md" headingLevel="h5">
<Title size="md" headingLevel="h5" data-testid="tier2-policy">
Tier 2: {tier2Policy}
</Title>
<p>Selected Backing Store: {tier2BackingStore.join(', ')}</p>
<p data-testid="tier2-stores">Selected Backing Store: {tier2BackingStore.join(', ')}</p>
</>
)}
</div>
Expand Down

0 comments on commit 45625d4

Please sign in to comment.